PageHeader

Top-of-content region header — breadcrumb, heading, description, and primary actions. Universal across all TimelyCare products.

Spec · from metadata

When to use

  • Top of a route or page section that needs a title + primary action
  • Any page with a breadcrumb trail that precedes the main title
  • List/index pages with a 'New X' CTA aligned to the right
  • Detail pages with a heading + contextual actions (Edit, Share, etc.)

When not to use

  • App-level top bar with global nav/search/avatar — use the `Header` primitive instead
  • Card-level titles inside a section — use `CardTitle`
  • Form section labels — use `Label` or a plain heading element
  • Marketing hero sections — build a custom layout; PageHeader is utility, not hero

Anti-patterns

Avoid<Header>
  <h1>Appointments</h1>
  <Button>New appointment</Button>
</Header>
Prefer<Header>…app-level nav…</Header>
<main>
  <PageHeader>
    <PageHeaderContent>
      <PageHeaderHeadingGroup>
        <PageHeaderHeading>Appointments</PageHeaderHeading>
      </PageHeaderHeadingGroup>
      <PageHeaderActions>
        <Button>New appointment</Button>
      </PageHeaderActions>
    </PageHeaderContent>
  </PageHeader>
</main>

Header is the app shell (banner). Route titles live in PageHeader inside <main>, not in the app-level Header.

Avoid<PageHeader>
  <h1>Appointments</h1>
  <Button>New</Button>
</PageHeader>
Prefer<PageHeader>
  <PageHeaderContent>
    <PageHeaderHeadingGroup>
      <PageHeaderHeading>Appointments</PageHeaderHeading>
    </PageHeaderHeadingGroup>
    <PageHeaderActions>
      <Button>New</Button>
    </PageHeaderActions>
  </PageHeaderContent>
</PageHeader>

Use the slot sub-components. They handle responsive stacking, spacing, and alignment consistently.

Avoid<PageHeader>
  <PageHeaderContent>
    <PageHeaderHeadingGroup>
      <PageHeaderHeading>Details</PageHeaderHeading>
    </PageHeaderHeadingGroup>
    <PageHeaderActions>
      <Button>Save</Button>
      <Button>Publish</Button>
    </PageHeaderActions>
  </PageHeaderContent>
</PageHeader>
Prefer<PageHeaderActions>
  <Button variant="outline">Save</Button>
  <Button>Publish</Button>
</PageHeaderActions>

Only one primary (variant='default') Button per header. Supporting actions use variant='outline' or 'ghost' to preserve visual hierarchy.

Accessibility

  • Min touch target48px
  • Screen readerPageHeaderHeading defaults to <h1> — ensure only one PageHeader per route unless you lower subsequent headings via the `level` prop. Breadcrumb announces nav landmark.
  • ContrastHeading uses text-foreground, description uses text-muted-foreground; both meet WCAG AA on the design-system background.

Token bindings

TokenCategoryUsage
foregroundcolorPage heading text
muted-foregroundcolorDescription + breadcrumb text
3 (12px) / 4 (16px)spacingGaps between breadcrumb/heading/actions
text-2xl / font-semiboldtypographyPage heading scale
text-smtypographyDescription scale
Recent changes
  • new

    AppSidebar section-aware active routes + NavBreadcrumb

    2026-07-24

  • new

    Compound components join the catalog

    2026-07-09

[!NOTE] Compound component — an opinionated composition of Helix primitives. Composes: Button, Breadcrumb.

Import

import { PageHeader, PageHeaderBreadcrumb, PageHeaderContent, PageHeaderHeadingGroup, PageHeaderHeading, PageHeaderDescription, PageHeaderActions } from "@timelycare/helix-ui"

Usage

<PageHeader>
  <PageHeaderBreadcrumb>
    <Breadcrumb>
      <BreadcrumbList>
        <BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem><BreadcrumbPage>Appointments</BreadcrumbPage></BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  </PageHeaderBreadcrumb>
  <PageHeaderContent>
    <PageHeaderHeadingGroup>
      <PageHeaderHeading>Appointments</PageHeaderHeading>
      <PageHeaderDescription>Manage your upcoming visits</PageHeaderDescription>
    </PageHeaderHeadingGroup>
    <PageHeaderActions>
      <Button>New appointment</Button>
    </PageHeaderActions>
  </PageHeaderContent>
</PageHeader>

For route-driven breadcrumbs, NavBreadcrumb fills the trail from the same nav items model you pass to AppSidebar:

<PageHeaderBreadcrumb>
  <NavBreadcrumb items={navItems} pathname={pathname} trailing={[{ label: client.name }]} />
</PageHeaderBreadcrumb>

NavBreadcrumb derives only the static section → item chain from the route; the nav model can't know a dynamic entity name. On a detail route (e.g. /clients/1074, which resolves to the /clients/list item), pass the entity as trailing — otherwise the resolved list item becomes the terminal (current-page) crumb, so the breadcrumb reads as the list and offers no link back to it. With trailing, the item links back and the dynamic leaf reads as the current page.