ClassicLogo

The original PureStack logo, with its compact bordered container, filled icon tile, uppercase brand and evenly distributed subtitle. Every original color and responsive sizing option is available in this separate component.

ClassicLogo

View source · API reference

Interactive playground

This starts with the original Studio identity and its original colors. Edit either line, change the color maps, or switch to SiteLogo to compare the two components. Restore original resets every control. Changing the component uses Regor's :is, just like TopBar.

<RegorApp id="classic-logo-playground" src="./playground.ts"/>
import {
  type ClassicLogoConfig,
  defineButtonComponents,
  defineClassicLogoComponents,
  defineFlexComponents,
  defineFormInputField,
  defineFormSelectField,
  defineGridComponents,
  defineIconComponents,
  defineLogoComponents,
  definePanelComponents,
  type FormSelectOption,
} from '@purestack/ts-components'
import {
  lucide_chevron_down,
  tabler_device_desktop_analytics,
} from '@purestack/ts-svg-icons'
import {
  type ComputedRef,
  computed,
  createApp,
  defineComponent,
  html,
  type Ref,
  ref,
} from 'regor'

export interface ClassicLogoPlayground {
  brand: Ref<string>
  subtitle: Ref<string>
  letterColors: Ref<string>
  subtitleLetterColors: Ref<string>
  accent: Ref<string>
  component: Ref<string>
  componentOptions: FormSelectOption[]
  config: ComputedRef<ClassicLogoConfig>
  source: ComputedRef<string>
  reset: () => void
}

const classicLogoPlaygroundTemplate = html`<Flex direction="column">
  <Panel variant="surfaceAlt" bodyClass="p-4 min-w-0 overflow-auto">
    <p class="text-eyebrow mt-0">Original Studio identity</p>
    <div id="classic-preview" class="py-4"><div :is="component" :config="config"></div></div>
    <p class="text-muted mb-0">The original container, letter spacing, icon and responsive sizes. Switch the site theme to compare both skins.</p>
  </Panel>
  <Grid columns="1" columnsMd="2">
    <FormSelectField id="classic-component" label="Logo component" :model="component" :options="componentOptions"/>
    <FormInputField id="classic-brand" label="Brand" :model="brand"/>
    <FormInputField id="classic-subtitle" label="Subtitle" :model="subtitle"/>
    <FormInputField id="classic-colors" label="Brand color map" :model="letterColors" :disabled="component !== 'ClassicLogo'"/>
    <FormInputField id="classic-subtitle-colors" label="Subtitle color map" :model="subtitleLetterColors" :disabled="component !== 'ClassicLogo'"/>
    <FormInputField id="classic-accent" label="Palette color 2" :model="accent" :disabled="component !== 'ClassicLogo'"/>
  </Grid>
  <p class="text-muted m-0">Palette: 0 = subtle text, 1 = normal text, 2 = accent, 3 = white. Each digit colors one non-space character; the final digit repeats.</p>
  <Flex><Btn variant="outline" @click="reset">Restore original</Btn></Flex>
  <Panel variant="outline" bodyClass="p-3 min-w-0">
    <p class="text-eyebrow mt-0">siteConfig.json · logo</p>
    <pre class="overflow-auto max-h-inspector m-0"><code id="classic-config">{{ source }}</code></pre>
  </Panel>
</Flex>`

const classicLogoPlayground = defineComponent<ClassicLogoPlayground>(
  classicLogoPlaygroundTemplate,
  {
    context: () => {
      const brand = ref('PureStack')
      const subtitle = ref('AI-Native Frontend')
      const letterColors = ref('111122')
      const subtitleLetterColors = ref('0')
      const accent = ref(
        'var(--ps-semantic-tone-accent-button-rest-background)',
      )
      const component = ref('ClassicLogo')
      const config = computed<ClassicLogoConfig>(() => ({
        brand: brand(),
        subtitle: subtitle(),
        letterColors: letterColors(),
        subtitleLetterColors: subtitleLetterColors(),
        colors: [
          'var(--ps-current-text-subtle)',
          'var(--ps-current-text-default)',
          accent(),
          '#fdfdfd',
        ],
        logoBackground: 2,
        logoForeground: 3,
        icon: 'tabler:device-desktop-analytics',
        href: '/',
      }))
      return {
        brand,
        subtitle,
        letterColors,
        subtitleLetterColors,
        accent,
        component,
        config,
        componentOptions: ['ClassicLogo', 'SiteLogo'].map((value) => ({
          label: value,
          value,
        })),
        source: computed(() =>
          JSON.stringify({ component: component(), ...config() }, null, 2),
        ),
        reset: () => {
          brand('PureStack')
          subtitle('AI-Native Frontend')
          letterColors('111122')
          subtitleLetterColors('0')
          accent('var(--ps-semantic-tone-accent-button-rest-background)')
          component('ClassicLogo')
        },
      }
    },
  },
)

const icons: Record<string, string> = {
  'tabler:device-desktop-analytics': tabler_device_desktop_analytics,
  'lucide:chevron-down': lucide_chevron_down,
}

createApp(
  {
    components: {
      ClassicLogoPlayground: classicLogoPlayground,
      ...defineClassicLogoComponents(),
      ...defineLogoComponents(),
      ...defineButtonComponents(),
      ...defineFlexComponents(),
      ...defineGridComponents(),
      ...definePanelComponents(),
      ...defineFormInputField(),
      ...defineFormSelectField(),
      ...defineIconComponents((name) => icons[name] ?? ''),
    },
  },
  {
    selector: 'app#classic-logo-playground',
    template: html`<ClassicLogoPlayground/>`,
  },
)

Use the original logo in TopBar

Set logo.component to ClassicLogo in siteConfig.json. This complete configuration restores the original Studio identity:

{
  "logo": {
    "component": "ClassicLogo",
    "brand": "PureStack",
    "subtitle": "AI-Native Frontend",
    "letterColors": "111122",
    "subtitleLetterColors": "0",
    "colors": [
      "var(--ps-current-text-subtle)",
      "var(--ps-current-text-default)",
      "var(--ps-semantic-tone-accent-button-rest-background)",
      "#fdfdfd"
    ],
    "logoBackground": 2,
    "logoForeground": 3,
    "icon": "tabler:device-desktop-analytics",
    "href": "/"
  }
}

Or choose the component directly on a custom header:

<TopBar logoComponent="ClassicLogo"/>

The direct prop takes precedence over site.logo.component. If neither is set, TopBar renders SiteLogo. TopBar passes site.logo as the selected component's config prop. Registered custom components can use the same contract.

Outside TopBar, use <ClassicLogo :config="identity"/> with a ClassicLogoConfig object, or pass individual props. A direct prop overrides the corresponding configuration field. Props and configuration remain reactive.

Color each letter

colors is a palette of CSS colors, gradients or theme variables. letterColors and subtitleLetterColors contain single-digit palette indexes, one digit per non-space character. The last digit repeats when the map is shorter than the text. Spaces keep their position and do not consume a digit.

For the original brand, 111122 means:

Text Palette index Result
Pure 1 Normal theme text
Stack 2 Accent fill; the final 2 repeats
Entire subtitle 0 Subtle theme text

logoBackground and logoForeground select colors from the same palette for the icon tile. They accept nonnegative integer indexes, including indexes beyond 9. Letter maps use individual digits, so they address palette entries 0–9.

Without a map, brand letters use the theme's accent button background and subtitle letters use subtle text. Missing palette entries fall back to those fills. A nonnumeric map character leaves that letter unfilled; use valid digits for visible lettering. Icon foregrounds need a CSS color, while tile backgrounds and letter fills can also use gradients.

Responsive dimensions

ClassicLogo preserves the original responsive sizes. Each dimension has a base prop and Sm, Md, Lg and Xl overrides, matching the theme breakpoints. Once supplied, a value carries forward to larger breakpoints until another override replaces it.

Dimension Base / sm md lg / xl
Brand 1.5rem 1.75rem 2rem
Subtitle 0.7rem 0.74rem 0.77rem
Icon tile 2rem 2.5rem 2.85rem
Subtitle side inset 0.03125rem 0.0625rem 0.0625rem
<ClassicLogo
  brand="PureStack"
  subtitle="AI-Native Frontend"
  icon="tabler:device-desktop-analytics"
  brandSize="1.25rem"
  brandSizeMd="1.75rem"
  iconSize="1.75rem"
  iconSizeMd="2.5rem"
  subtitleSize="0.6rem"
  subtitleSizeMd="0.74rem"
/>

The original layout keeps both text lines on one line, distributes their letters across a shared width and uses a fixed 0.25rem mark-to-text gap. Long brand names need smaller dimensions in narrow navigation. SiteLogo offers wrapping, layout presets and a configurable gap when those are needed.

API reference

Original appearance · independent component

Import ClassicLogoConfig for identity data and ClassicLogo for the component interface. Every input accepts a literal or a ref. Register defineClassicLogoComponents() and defineIconComponents(); the standard SSG registration includes both.

config

RefOrValue<Partial<ClassicLogoConfig>>
Default
not set

Shared settings. Direct props override defined fields; undefined props inherit from config. Standalone instances do not read site configuration automatically.

brand · subtitle

RefOrValue<string>
Default
not set

Brand and optional secondary line. Both render as individual uppercase letters. An empty subtitle removes the second line. Supply a brand for an accessible name.

colors

RefOrValue<string[]>
Default
not set

Indexed color palette shared by the brand, subtitle and icon tile. Accepts CSS colors, gradients and theme-variable references.

letterColors · subtitleLetterColors

RefOrValue<string>
Default
theme fills

Digit sequences selecting palette entries for each non-space letter. The final digit repeats; spaces do not advance the color index.

logoBackground · logoForeground

RefOrValue<number>
Default
theme button colors

Nonnegative integer palette indexes for the tile background and icon foreground. Invalid or missing indexes use the theme defaults.

icon

RefOrValue<string>
Default
built-in square

Registered SVG icon name. The icon fills 80% of its tile; without one, a small rounded square is drawn in the center. ClassicLogo has no image or mark slot.

brandSize · subtitleSize · iconSize · subtitleInset

RefOrValue<string>
Default
responsive defaults

CSS lengths for the brand font, subtitle font, square tile, and subtitle's left/right inset. Each supports Sm, Md, Lg and Xl suffixes: brandSizeMd, subtitleSizeLg, iconSizeSm, subtitleInsetXl, and the other corresponding combinations.

Overrides carry forward to larger breakpoints. The table above lists the untouched original defaults.

href · ariaLabel

RefOrValue<string | null> · RefOrValue<string>
Default
/ · brand

Home destination and accessible name. SSG links respect the public base path. A null href removes navigation. Keyboard focus keeps the original visible outline.

  • SiteLogo: flexible layouts, image marks and modern wordmark treatments.
  • TopBar: choose which logo component renders the shared site identity.