Top bar / app bar
Primary app header: drive it live (toggle search, push the inbox count, condense the bar)
Inbox shows 3 unread · search is collapsed · bar is full. Tab through every action; the cyan boundary line closes the bar. The island is live.
Variants
Condensed: the same bar after scroll (height collapses; freezes under reduced motion)
// Web: React, consuming @dizyx/nockerl-tokens. (Web is the laggard platform. Spec + live demo,// not yet exported from @dizyx/nockerl-react; promotion is tracked.)import { NockerlIconButton, NockerlAvatar } from '@dizyx/nockerl-react';
export function AppHeader() { const [searchOpen, setSearchOpen] = useState(false); return ( <TopBar brand={{ mark: 'nockerl', product: 'Nockerl' }} // mark + optional product label title="Chat" // page title (centered on Android) align="center" // 'lead' | 'center' actions={ <> <NockerlIconButton icon="search" label="Search" pressed={searchOpen} onClick={() => setSearchOpen((v) => !v)} /> <ClusterStatus online={3} total={4} onClick={openCluster} /> <InboxButton count={3} onClick={openInbox} /> {/* count → "99+" → unseen dot */} <NockerlAvatar src={user.photo} name={user.name} presence="streaming" onClick={openSettings} /> </> } /> );}
// A sub-screen header: a leading back button + title instead of the brand.<TopBar leading={<NockerlIconButton icon="back" label="Back" onClick={goBack} />} title="Session settings" />// Android: Jetpack Compose (canonical). com.nockerl.app.core.ui.GlobalTopBar +// TopChromeBoundary (the 1.5dp cyan line). GlobalTopBar is a Box so the three-peaks// mark stays centered to the SCREEN regardless of the trailing controls: avatar at// the start, the logo dead center, the cluster Hub + the notification bell (a// BadgedBox: unread count -> "99+" -> an 8dp unseen dot in the orange agent accent)// at the end. The cyan boundary closes the header chrome below it.import com.nockerl.app.core.ui.GlobalTopBarimport com.nockerl.app.core.ui.TopChromeBoundary
Column { GlobalTopBar( onAvatarClick = { showSettingsSheet = true }, // start: profile photo -> settings onInboxClick = { showInboxSheet = true }, // end: bell + BadgedBox onClusterClick = { showClusterSheet = true }, // end: Hub (cluster) icon unreadCount = unreadCount, // count badge (agent-orange) hasUnseen = hasUnseen, // falls back to an 8dp dot ) HorizontalDivider(thickness = 1.dp, color = colors.divider) // hairline under the title bar // ... project / session rows (the collapsible header chrome) ... TopChromeBoundary( // the full-width 1.5dp cyan line collapsed = headerCollapsed, // + a grab handle: drag / tap to onCollapsedChange = { headerCollapsed = it }, // collapse / expand the chrome )}// macOS: SwiftUI. Voice is a MENU-BAR app with no title bar// (.windowStyle(.hiddenTitleBar)): the brand instead anchors the sidebar header and// the floating command pill (RecordingHUD), built from a NockerlLogo, a Rectangle(divider)// separator, and morphing content on chromeSurface, in a capsule with a 1.5pt accent// border. This pill is the closest analogue to the global top bar; see the drift note.HStack(spacing: 11) { NockerlLogo() // the brand anchor, fixed on the left .frame(width: 18, height: 16) Rectangle() .fill(NockerlTheme.divider) // a full-height separator .frame(width: 1, height: 22) content // status / page content (cross-fades) .transition(.opacity)}.padding(.horizontal, 14).padding(.vertical, 9).background(NockerlTheme.chromeSurface, in: Capsule()).overlay(Capsule().strokeBorder(NockerlTheme.accent, lineWidth: 1.5)) // the cyan boundary.shadow(color: .black.opacity(0.4), radius: 14, y: 5)
// The dashboard sidebar header (brand lockup, pushed clear of the traffic lights):HStack(spacing: 9) { NockerlLogo().frame(width: 22, height: 19) Text("Nockerl Voice").font(.system(size: 14, weight: .semibold)) Spacer(minLength: 0)}Parameters
Section titled “Parameters”| Prop | Type | Default | Description |
|---|---|---|---|
brand | { mark: BrandMark; product?: string } | Leading identity: the three-peaks mark and an optional product label. Omit when leading is set (a sub-screen). | |
leading | ReactNode | Replaces the brand on a sub-screen, typically a back IconButton. Mutually exclusive with brand. | |
title | string | Page title. Bound to the title.medium type role. | |
align | 'lead' | 'center' | 'lead' | Title placement. center is the Android idiom, absolutely centered to the bar so the action cluster never shifts it. |
breadcrumb | Crumb[] | Wayfinding trail ({ label, href }[]) shown in place of the title; the last crumb is aria-current="page". | |
actions | ReactNode | Trailing account cluster, right-aligned and evenly gapped: search · ClusterStatus · InboxButton · Avatar. | |
condensed | boolean | false | Scrolled state: the bar shrinks its height. Animates (height); freezes under prefers-reduced-motion. |
selection | { count: number; actions: ReactNode } | Contextual mode. Replaces the chrome with a cyan-tinted "N selected" bar + bulk actions and a clear button. | |
InboxButton.count | number | 0 | Inbox badge count (agent-orange keycap); clamps to 99+, falls back to an unseen dot at 0. |
GlobalTopBar is the title-bar row; TopChromeBoundary is the 1.5dp cyan line that
closes the header chrome. Both are composed by MainScaffold.
| Parameter | Type | Default | Description |
|---|---|---|---|
GlobalTopBar.onAvatarClick | () -> Unit | {} | Start: the circular profile photo (32dp image in a 40dp target) → the settings drawer. |
GlobalTopBar.onInboxClick | () -> Unit | {} | End: the notification bell (a BadgedBox) → the inbox sheet. |
GlobalTopBar.onClusterClick | () -> Unit | {} | End: the cluster Hub icon → the Nockerl Laboratory sheet. |
GlobalTopBar.unreadCount | Int | 0 | Inbox badge count: family.agent orange fill, pickOnAccent label, 99+ overflow. |
GlobalTopBar.hasUnseen | Boolean | false | When the count is 0 but there is unseen activity, shows a bare 8dp dot instead of a number. |
GlobalTopBar.modifier | Modifier | Modifier | Outer modifier. The bar fills width and sits on colors.canvas (a Box, so the logo stays screen-centered). |
TopChromeBoundary.collapsed * | Boolean | Whether the project + session rows are collapsed up under the title bar. The pill looks identical in both states. | |
TopChromeBoundary.onCollapsedChange * | (Boolean) -> Unit | Drag (up = collapse, down = expand) past the threshold, or tap the grab-handle pill, to toggle. |
Voice is a menu-bar utility with no title bar, so there is no TopBar view at all.
Its analogue is the floating command pill; that surface (the brand anchor, the divider,
the capsule + cyan outline, and its phase morphing) is documented in full on the
Recording HUD page and is not duplicated here.