TopBar

The standard site header combines branding, search, theme switching and optional account access.

TopBar

View source ยท API reference

The header above this guide is TopBar. The contained preview below has its own document so its navigation toggle ID does not collide with the real page header.

A complete header

This preview reads the same logo configuration as the site. Search is disabled inside this isolated document because it has no independent index.

<TopBarPreview />
import fs from 'node:fs/promises'
import path from 'node:path'
import type { SiteConfig, TsSsgContext } from '@purestack/ts-common'
import { defineComponents } from '@purestack/ts-components'
import {
  buildMenuRuntimeScript,
  buildThemeSwitchScript,
} from '@purestack/ts-page-scripts'
import { renderApp } from '@purestack/ts-render'
import { getSvgIcon } from '@purestack/ts-svg-icons'
import { withBasePath } from '@purestack/ts-util'
import { defineComponent, html } from 'regor'

export interface TopBarPreview {
  previewHref: string
}

const topBarPreviewTemplate = html`<iframe
  title="TopBar isolated example"
  class="w-full rounded-md b-1 b-subtle"
  style="height: 260px"
  :src="previewHref"
></iframe>`
const exampleTemplate = html`<TopBar tone="neutral" variant="surface" />
<p class="mt-4">
  A complete header in its own document. The logo links home and the theme control
  changes this preview.
</p>`

function createTopBarPreviewDocument(site: SiteConfig): string {
  const context: TsSsgContext = {
    site: { ...site },
    pageInfo: {
      relPath: 'components/site/top-bar/top-bar.mdx',
      urlPath: '/components/site/top-bar/',
      frontmatter: {
        template: 'doc',
        hidden: false,
        draft: false,
        nav: { hidden: false },
        layout: {
          showNav: false,
          showToc: false,
          showFooter: false,
          fullWidth: false,
          navMode: 'sidebar',
          tocCollapsed: false,
        },
      },
    },
    theme: site.style.theme,
    basePath: site.basePath,
    locales: site.i18n.locales,
    defaultLocale: site.i18n.defaultLocale,
    resolveLocaleHref: () => undefined,
    resolvePublicHref: (href) => withBasePath(site.basePath, href),
    recordScriptEntrypoint: () => {},
    recordRuntimeEmbed: () => {},
  }
  context.site.pagefind = { ...context.site.pagefind, enabled: false }
  const body = renderApp(exampleTemplate, {
    components: defineComponents(getSvgIcon),
    context,
  })
  const scripts =
    buildThemeSwitchScript(['light', 'dark']) + buildMenuRuntimeScript()
  const documentHtml = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="robots" content="noindex"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="${withBasePath(site.basePath, '/assets/site.css')}" data-theme="light"><link rel="stylesheet" href="${withBasePath(site.basePath, '/assets/site.dark.css')}" data-theme="dark"></head><body class="template-doc tone--neutral p-3" data-pagefind-ignore="all"><main class="doc-content">${body}</main><script>${scripts}</script></body></html>`
  return documentHtml
}

export function defineTopBarPreviewComponent(site: SiteConfig) {
  return defineComponent<TopBarPreview>(topBarPreviewTemplate, {
    context: () => ({
      previewHref: withBasePath(
        site.basePath,
        '/components/site/top-bar/preview.html',
      ),
    }),
  })
}

export async function writeTopBarPreview(site: SiteConfig) {
  const output = path.join(
    site.outDir,
    'components',
    'site',
    'top-bar',
    'preview.html',
  )
  const documentHtml = createTopBarPreviewDocument(site)
  await fs.mkdir(path.dirname(output), { recursive: true })
  await fs.writeFile(output, documentHtml, 'utf8')
}

Choose the logo component

TopBar uses Regor's :is binding to render the registered component named by logoComponent. It passes the site's logo settings as config.

<TopBar logoComponent="ClassicLogo"/>

For the standard documentation header, select it in siteConfig.json:

{
  "logo": {
    "component": "ClassicLogo",
    "brand": "PureStack",
    "subtitle": "AI-Native Frontend",
    "icon": "tabler:device-desktop-analytics"
  }
}

The direct prop overrides site.logo.component; when both are omitted, TopBar renders SiteLogo. Bind a ref with :logoComponent="selectedLogo" to switch reactively. Custom registered components work too: accept a config prop and read the identity fields you need. Register the chosen component alongside TopBar when creating a standalone app.

ClassicLogo's guide includes the exact original Studio color configuration and all its responsive sizing options. SiteLogo provides the newer layouts and treatments.

Behavior and accessibility

When a different logo or custom header changes TopBar's height, set the sidebar offsets in siteConfig.json:

{
  "docLayout": {
    "sidebarTop": "5.5625rem",
    "sidebarTopMobile": "4.875rem"
  }
}

These are the defaults. Set each value to the header height plus the clearance you want below it. sidebarTop controls the desktop navigation and table of contents, including collapsed sidebars and drawers. sidebarTopMobile controls their mobile offsets at the existing responsive breakpoints. CSS lengths such as 96px, 6rem and calc(5rem + 8px) are supported.

Both the top position and available scrolling height use the same setting. Desktop scroll panels retain their existing bottom clearance. This reserves space below the header; it does not resize TopBar itself. Rebuild the site after changing these settings.

Custom documentation templates can set --ps-doc-layout-sidebar-top and --ps-doc-layout-sidebar-top-mobile on a shared ancestor of their sidebars. The default doc template sets both from site configuration automatically.

TopBar renders a fixed doc-nav-toggle ID and should appear once per document. Its mobile navigation toggle works with the standard doc layout. Logo content comes from site.logo. Search visibility currently follows site.pagefind.enabled, even when a searchEnabled prop is supplied.

API reference

TopBar contract

Props below are the public template API. RefOrValue accepts a literal or a reactive ref; bind refs with a colon-prefixed attribute. Native attributes and events can be passed through the component root.

logoComponent

RefOrValue<string>
Default
site.logo.component or SiteLogo

Registered component name rendered through :is. Supports SiteLogo, ClassicLogo or a custom component accepting config. A direct prop takes precedence over the site setting and can change reactively.

tone

RefOrValue<SemanticTone>
Default
inherited

Semantic intent: neutral, accent, secondary, info, success, warning, danger, feature, custom or ghost. The active skin supplies the colors.

variant

RefOrValue<ComponentVariant>
Default
surfaceAlt

Visual treatment. Accepts solid, surface, surfaceAlt, spotlight, glass, flat, flatAlt, flatSolid, outlineFill, outline, subtle, subtleBtn, link, sheen, underline, rail, bracket or none.

signInAvatarSrc

RefOrValue<string>
Default
not set

Avatar URL forwarded to SignIn when account access is shown.

signInAvatarAlt

RefOrValue<string>
Default
not set

Avatar alternative text forwarded to SignIn.

signInEnabled

boolean
Default
false

Shows the SignIn slot in the header; site.auth.enabled must also be true for the account component to render.

searchEnabled

boolean
Default
site.pagefind.enabled

Registered prop currently resolved from site configuration. Configure pagefind.enabled to control search visibility.

Composition

No slots. Register defineTopBarComponents and the selected logo component, SearchBox, ThemeToggle, SignIn, Flex and Icon families. Standard SSG registration provides these dependencies.