Top Bar
The app header block: leading control, title, search and actions. It condenses on its own when narrow (shorter, subtitle hidden, search behind an icon) and adds the status-bar inset in immersive pages.
Usage
STopbar({
title: 'Projects',
subtitle: '12 active',
search: true, // expands from an icon when condensed
actions: [new SparkTopbarAction('new', 'plus', 'New project'),
new SparkTopbarAction('alerts', 'bell', 'Notifications')],
onAction: (id: string) => ...,
onSearch: (q: string) => ...
}) {
SBadge({ text: 'Beta', variant: 'secondary' }) // optional trailing slot
}
STopbar({ title: 'Invoice #1024', leading: 'back', onBack: () => ... })
STopbar({ title: 'Gallery', translucent: true }) // backdrop blur for content scrolling underneathApproximate web preview — the HarmonyOS rendering is the reference.
API
Extracted from the @Param / @Event / @BuilderParam declarations in the library sources.
STopbar
App header (shadcn "site header" block): leading control, title / subtitle, optional search, icon actions and a trailing slot. Breakpoint-free: below ~520 vp of its own width it condenses (48 vp tall, subtitle hidden, search behind an icon that expands over the title).
It stays at the top because it lives outside the scrolling content (SAppShell / SSidebarInset). With translucent the background is translucent + backdrop-blurred, for content that scrolls underneath (SAppShell does that). safeArea adds the status-bar inset for immersive pages (skipped automatically inside a framed 'inset' sidebar layout, whose card already clears it).
Extension points: the trailing closure (after actions); leadingBuilder / titleBuilder regions as WrappedBuilders (titleBuilder: wrapBuilder(myGlobalBuilder); additive — set leading: 'none' or an empty title to replace the built-ins), condenseBelow and barHeight instead of fixed metrics, onBack for any back handling (no routing built in). Inside an SSidebarProvider, leading: 'auto' follows the sidebar controller (toggleable).
| Prop | Type | Default | Description |
|---|---|---|---|
title | ResourceStr | '' | |
subtitle | ResourceStr | undefined | undefined | |
leading | TopbarLeadingTopbarLeading = 'auto' | 'trigger' | 'back' | 'none' | 'auto' | |
actions | SparkTopbarAction[] | [] | |
search | boolean | false | show a search field (an icon that expands when condensed) |
searchPlaceholder | ResourceStr | $r('app.string.spark_search_placeholder') | |
translucent | boolean | false | translucent, backdrop-blurred background |
bordered | boolean | true | |
safeArea | boolean | false | add the top safe-area inset (immersive pages) |
condenseBelow | number | TOPBAR_CONDENSED_BELOW | condense below this width of the bar itself (vp) |
barHeight | number | 0 | bar height without the safe-area inset (vp, 0 = --topbar-height[-condensed], else 56 / 48 condensed) |
leadingBuilder | WrappedBuilder<[]> | undefined | undefined | Extra leading content after the built-in leading control (use leading: 'none' to replace it), as wrapBuilder(myLeading) of a global @Builder (no this: render a component or read shared state). |
titleBuilder | WrappedBuilder<[]> | undefined | undefined | Extra title content under / instead of title (breadcrumbs, a switcher…; leave title empty to replace), as wrapBuilder(myTitle) of a global @Builder. |
tokens | TopbarTokens | {} | per-instance look (ADR D13): instance → --topbar-* tokens → recipe |
sidebarController | SparkSidebarState | undefined | undefined | drive the leading trigger from this controller (e.g. a bar outside the SSidebarProvider) |
| Event | Type | Default | Description |
|---|---|---|---|
onBack | () => void | () => { } | |
onAction | (id: string) => void | () => { } | |
onSearchChange | (value: string) => void | () => { } | |
onSearch | (value: string) => void | () => { } |
Slots: children (trailing closure)