DocumentationComponentsTheme CreatorGitHub
Theme CreatorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleChatBubbleNewCheckboxChipCoachMarkCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPickerPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderCircularSliderMediaTrimmerSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupTokenizerToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Top Header Mobile

Mobile top app bar with leading/trailing slots, centred or start-aligned title, subtitle, six variants, five sizes, tokened bottom rounding, three layout modes, scroll elevate/collapse behavior, search and sub-row decks, Surface tone, and full RTL support.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import { TopHeaderMobile } from "@tessinaui/ui";
import { IconButton } from "@tessinaui/ui";
<TopHeaderMobile
  bordered
  title="Inbox"
  subtitle="3 unread"
  leadingAction={<IconButton variant="ghost" size="sm" icon={<ArrowLeft />} aria-label="Back" />}
  trailingActions={<IconButton variant="ghost" size="sm" icon={<Search />} aria-label="Search" />}
/>

Examples

Default

Back, dead-centred title, one trailing action — the dominant single-row shape, composed from library IconButtons. The second bar names the back destination (a ghost Button in the same slot).

Variants

Six paints — filled, elevated, transparent, blur, primary, on-color. The coloured variants publish the Surface tone, so nested library controls re-ink themselves.

Sizes

Bar min-heights 48 / 56 / 64 / 80 / 96px. xs, sm and md land exactly on Carbon's 48px shell, Atlassian's 56px global nav and M3's 64px small bar.

Layout modes

M3 lineage: small inline, medium and large drop the title below the action row. Content-hugging — a subtitle simply grows the bar, and the large row wraps up to two lines instead of truncating.

Scroll behavior

scrollBehavior="elevate" paints a hairline + shadow once content scrolls under the sticky bar; "collapse" additionally folds a medium/large bar to the inline title (M3 compress / iOS large-title fold). An IntersectionObserver sentinel drives both — no scroll listeners.

Subtitle

Presence, status, count or context under the title — in centred, start-aligned and large layouts. The chat header composes an Avatar into the title cluster.

Title alignment

center holds the title dead-centre regardless of the side clusters; start left-aligns after the leading slot. Both slots are optional.

Title menu

The title as a dropdown trigger — the feed/workspace switcher, iOS 16's titleMenuProvider pattern. Pure composition with DropdownMenu.

Search

The search slot renders its own deck under the action row inside a search landmark. For a field that replaces the title, pass it as the title.

Sub row

The structured second deck — tabs, segmented control, or a scrolling chip rail — padded to the bar's own gutter.

Transparent over media

The bar paints nothing; the actions carry their own fill (chip-style IconButtons), tone="on-color" re-inks any text, and the H1 stays in the content.

Select mode

Cancel + live count + one prominent Done — pure composition, no mode prop.

Commerce

Brand mark in the title slot, a filled CTA, wishlist and a cart count on IconButton's own badge.

Skip link

WCAG 2.4.1: TopHeaderMobileSkipLink hides until keyboard focus, then pins over the bar's leading corner. Click into the demo and press Tab.

Loading

The skeleton mirrors the bar's box — same cva, same size maps, icon circles on IconButton's size tokens, text bars sized in characters.

API Reference

TopHeaderMobile

PropTypeDefaultDescription
variant"filled" | "elevated" | "transparent" | "blur" | "primary" | "on-color""filled"Visual style. primary/on-color publish the on-color Surface tone
size"xs" | "sm" | "md" | "lg" | "xl""md"Bar min-height 48/56/64/80/96px
rounded"none" | "sm" | "md" | "lg" | "full""none"Bottom-corner rounding; full = the tokened panel scale
titleReactNode—Title text or custom element
titleAs"h1" | "h2" | "h3" | "span""h1"Heading element for a string title. Use "span" when the page H1 lives in the content
titleAlign"start" | "center""center"Inline-title alignment
subtitleReactNode—Second line under the title (status, presence, context)
leadingActionReactNode—Leading slot (back/menu control)
trailingActionsReactNode—Trailing slot (icon buttons, text actions)
layout"small" | "medium" | "large""small"Inline title, or title below the row at two scales
collapsedboolean—Fold medium/large to inline. Leave unset with scrollBehavior="collapse" for self-folding; set it and the prop wins
stickybooleanfalsesticky top-0 + safe-area top inset
borderedbooleanfalseHairline bottom border
scrollBehavior"none" | "elevate" | "collapse""none"Scroll response — elevate chrome, or elevate + collapse. Pair with sticky
searchReactNode—Search deck row under the bar, wrapped in a search landmark
subRowReactNode—Structured second deck (tabs, segmented, chips)
tone"default" | "on-color"autoSurface tone for nested controls; auto-detected from variant, override for transparent-over-media
skeletonbooleanfalseSwap the bar for its skeleton
renderuseRender.RenderProp—Replace the rendered <header> element
dir"ltr" | "rtl"—Text and layout direction

asChild was removed — it was declared but never implemented and leaked onto the DOM. Use render instead.

TopHeaderMobileSkeleton

PropTypeDefaultDescription
variant / size / rounded / layout / borderedsame as the bar—The skeleton is the same box
showLeadingbooleantrueLeading circle
trailingCountnumber (0–3)1Trailing circles
titleCharsnumber12Title bar width, in characters of the real title
showSubtitlebooleanfalseSubtitle line

TopHeaderMobileSkipLink

PropTypeDefaultDescription
hrefstring"#content"Jump target. Render it first inside leadingAction

Keyboard navigation

KeyAction
Tab / Shift+TabMove focus through the slot controls in DOM order (skip link first when present)
Enter / SpaceActivate the focused control

The bar itself is not interactive and never traps focus; arrow-key behavior belongs to composed widgets (Tabs, SegmentedControl, DropdownMenu).

Accessibility

  • Renders a plain <header> with no hardcoded role="banner" — the banner landmark applies only at body level (APG); nested bars stay generic.
  • A string title renders as titleAs (default h1). Set "span" for actions-only bars so the content owns the document outline.
  • The search slot is wrapped in a search landmark (element + explicit role for older AT).
  • TopHeaderMobileSkipLink provides the WCAG 2.4.1 bypass block.
  • Slot controls must carry aria-labels; library IconButton enforces the 44px touch floor on phones.
  • The skeleton is one role="status" region announced by sr-only text.
  • The large-title fold animates opacity only, gated by motion-safe.
  • RTL: dir="rtl" flips the row; the title cluster right-aligns.

Boundaries

  • Trailing actions: M3 caps at two icons (one if filled); the observed mobile mode is two, max five. Keep to ≤3 and push overflow into a DropdownMenu — never an in-bar auto-overflow.
  • One prominent action. At most one filled/text-emphasized action, on the trailing side (HIG .prominent). Everything else stays ghost.
  • Back vs Close are different verbs. Back returns within a flow; Close dismisses a modal context. A sheet header (top rounding + grabber) belongs to Drawer/ActionSheet, not this bar.
  • Full-screen takeover menus belong to Drawer/Command — the bar hosts the trigger only.
  • Bottom rounding only. A top bar never curves its top edge; M3 keeps app bars straight (curvature implies expandability) — our rounded axis is the documented divergence, tokened through §2.
  • Non-sticky bars at the very top of a standalone PWA still sit under the status bar — add pt-[env(safe-area-inset-top)] via className there, or use sticky.
Top Header DesktopContributing

On this page

PlaygroundInstallationUsageExamplesDefaultVariantsSizesLayout modesScroll behaviorSubtitleTitle alignmentTitle menuSearchSub rowTransparent over mediaSelect modeCommerceSkip linkLoadingAPI ReferenceTopHeaderMobileTopHeaderMobileSkeletonTopHeaderMobileSkipLinkKeyboard navigationAccessibilityBoundaries