Page Header
Top-of-page heading pattern with eyebrow, title, subtitle, and action area. Pure Tailwind composition — no library component needed. Four variants for list pages, detail pages with breadcrumb, tabbed pages, and form pages.
Live Preview
List page
Property management
Apartments
Master data of all managed apartments — addresses, occupancy, last inspection.
Detail page with breadcrumb
Building 12
Sunset Heights
18 apartments · 4 floors · occupied since 1987.
Tab page
Settings
Manage your account, billing, and team preferences.
Form page (h2 inside dialog)
Invite a new member
They'll receive an email with a link to join your workspace.
Features
- List-page header with eyebrow, title, subtitle, and a primary action on the right
- Detail-page header with a leading Breadcrumb above the title
- Tab-page header where the heading row sits above a Tab strip
- Form-page header with secondary actions (Cancel/Save) on the right
- Responsive layout — actions wrap below the heading on narrow viewports
- Heading-level prop pattern (h1 vs h2 vs h3) for nesting inside larger layouts
Code
List-page header
<header class="mb-6 flex flex-col gap-4 sm:mb-8">
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between sm:gap-6">
<div class="min-w-0 flex-1">
<p class="text-text-tertiary mb-1 text-xs font-medium tracking-wide uppercase">
Property management
</p>
<h1 class="text-text-primary text-3xl font-semibold tracking-tight">Apartments</h1>
<p class="text-text-secondary mt-1.5 max-w-2xl text-sm leading-relaxed sm:text-base">
Master data of all managed apartments.
</p>
</div>
<div class="flex flex-wrap items-center gap-2 sm:shrink-0">
<Button intent="primary" onclick={openCreate}>New apartment</Button>
</div>
</div>
</header>Detail-page header with Breadcrumb
<header class="mb-6 flex flex-col gap-4 sm:mb-8">
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between sm:gap-6">
<div class="min-w-0 flex-1">
<div class="mb-2">
<Breadcrumb items={[
{ label: 'Buildings', href: '/buildings' },
{ label: 'Sunset Heights' }
]} />
</div>
<p class="text-text-tertiary mb-1 text-xs font-medium tracking-wide uppercase">Building 12</p>
<h1 class="text-text-primary text-3xl font-semibold tracking-tight">Sunset Heights</h1>
<p class="text-text-secondary mt-1.5 max-w-2xl text-sm leading-relaxed sm:text-base">
18 apartments · 4 floors · occupied since 1987.
</p>
</div>
<div class="flex flex-wrap items-center gap-2 sm:shrink-0">
<Button variant="ghost" intent="neutral">Archive</Button>
<Button intent="primary">Edit</Button>
</div>
</div>
</header>Tab-page header — heading row + Tab strip
<header class="mb-6 flex flex-col gap-4 sm:mb-8">
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between sm:gap-6">
<div class="min-w-0 flex-1">
<h1 class="text-text-primary text-3xl font-semibold tracking-tight">Settings</h1>
<p class="text-text-secondary mt-1.5 max-w-2xl text-sm leading-relaxed sm:text-base">
Manage your account, billing, and team preferences.
</p>
</div>
<div class="flex flex-wrap items-center gap-2 sm:shrink-0">
<Button variant="outlined" intent="neutral">Export</Button>
</div>
</div>
<Tab bind:value={activeTab} variant="line">
{#snippet tabs()}
<TabItem value="overview">Overview</TabItem>
<TabItem value="billing">Billing</TabItem>
<TabItem value="team">Team</TabItem>
{/snippet}
</Tab>
</header>Form-page header — h2 for nested context
<!-- Inside a Dialog, Drawer, or any context that already has an outer <h1>: -->
<header class="mb-6 flex flex-col gap-4 sm:mb-8">
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between sm:gap-6">
<div class="min-w-0 flex-1">
<h2 class="text-text-primary text-2xl font-semibold tracking-tight">
Invite a new member
</h2>
<p class="text-text-secondary mt-1.5 max-w-2xl text-sm leading-relaxed sm:text-base">
They'll receive an email with a link to join your workspace.
</p>
</div>
<div class="flex flex-wrap items-center gap-2 sm:shrink-0">
<Button variant="ghost" intent="neutral" onclick={onCancel}>Cancel</Button>
<Button intent="primary" onclick={onSubmit}>Send invite</Button>
</div>
</div>
</header>Best Practices
Pick the right heading level
Use <h1> when the header is the single
top-of-page heading. Drop to <h2> when you're inside a Dialog, Drawer, or
a route that already has an outer h1 — a page should have exactly one h1.
Actions on the right, wrapping below on narrow viewports
flex-col gap-4 sm:flex-row sm:justify-between gives you the responsive default: heading + actions stack on mobile, sit side-by-side from sm: upward. The actions container uses flex-wrap so two-button groups don't push the heading off-screen.
Eyebrow vs. Breadcrumb — pick one
Eyebrow ("Property management") is a category label; Breadcrumb is a navigation trail. Use eyebrow for context that doesn't navigate; use Breadcrumb when each ancestor is a real route. Stacking both above the title is visually noisy — pick the one that earns its weight.
Subtitle should describe the page, not repeat the title
"Apartments — list of apartments" is filler. A good subtitle either narrows the scope ("…managed by your team this quarter") or tells the user what they can do here. If you can't write a useful one, leave it out.
Why this is a recipe, not a component
The header is pure layout composition with zero state, zero variants, and zero behaviour. Wrapping it in a component would hide the markup from the consumer without saving any logic — and would lock the structure when projects routinely need small variations (different eyebrow position, a status pill next to the title, a custom action row). Copy the snippet that matches your page; adapt it in place.