TopBar
The standard site header combines branding, search, theme switching and optional account access.
TopBarThe 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
Sidebar spacing below the header
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.