Section Header
ProThe lede every section opens with: a Badge eyebrow, a balanced heading, a supporting paragraph and a CTA row. Two axes, align and orientation, recompose it, so every block on the page shares one rhythm.
Installation#
Usage#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import {
SectionHeader,
SectionHeaderText,
SectionHeaderHeading,
SectionHeaderChip,
SectionHeaderDescription,
SectionHeaderActions,
} from "@/components/ui/section-header"
<SectionHeader>
<SectionHeaderText>
<SectionHeaderHeading>
... <SectionHeaderChip /> ...
</SectionHeaderHeading>
<SectionHeaderDescription />
</SectionHeaderText>
<SectionHeaderActions>...</SectionHeaderActions>
</SectionHeader>Alignment#
align="left" (default) anchors the block to the leading edge; align="center" centers it in a capped column, the classic centered section lede.
Orientation#
orientation="stacked" (default) drops the actions below the text. split moves them beside the text on a row from the lg breakpoint up (and stacks again below it). Split is a left-aligned layout; centered headers always stack.
Sizes#
size scales the heading and description together to roughly the scale of a Heading 1 (lg), Heading 2 (md, the default), or Heading 3 (sm). It is independent of the heading level (the semantic tag), so an h2 can read at the Heading 1 size.
| Size | Heading | Description |
|---|---|---|
sm Heading 3 | text-2xl sm:text-3xl24 → 30px | text-sm sm:text-base14 → 16px |
md Heading 2 · default | text-4xl sm:text-5xl36 → 48px | text-base sm:text-lg16 → 18px |
lg Heading 1 | text-4xl sm:text-5xl lg:text-6xl36 → 48 → 60px | text-lg18px |
Values are mobile → sm (640px) → lg (1024px); the heading and description step up together at those breakpoints. Both slots inherit Tailwind's default type scale (no token override).
Responsive#
The header is responsive on every axis at once. size steps the heading and description up at sm and lg; the actions row stacks full-width on a phone (items-stretch) and returns to a wrapping row from sm; a split header lays the lede and actions side by side from lg and falls back to the stacked column below it; a centered block shrinks to the viewport instead of overflowing; and any inline SectionHeaderChip rides the heading (it is sized in em). Drag the frame, or use the mobile / tablet / desktop presets, to watch it recompose.
With an email capture#
The actions row composes any control, not just buttons: drop an Input beside a Button for a newsletter or waitlist lede. The row imposes one height on everything inside it, so the field and the button come out level without either of them being given a size.
Inline media#
Drop a SectionHeaderChip between the words of the heading to flow a mark or emoji inline with the copy. A framed chip (default) is a rounded tile that holds a logo, icon, or image, the app-icon look set right into the headline; a bare chip drops the box for a naked glyph or emoji interleaved with the text. It is sized entirely in em, so the tile, its radius, and the glyph inside all scale with the heading at every size and breakpoint. Chips are decorative by default (aria-hidden); pass an aria-label when one stands in for a word.
Drop the frame with variant="bare" and the chip is just a glyph or emoji sized to the cap height, so icons and emoji sit inline between the words without a container.
Reveal on scroll
Add reveal and the chip waits for the reader: until the heading scrolls into view it is closed, taking no width and giving back one of the two spaces around it, so the heading reads as plain copy. When it arrives, the words part to make room and the mark pops into the gap. It fires later than the library's other scroll gates, once the heading has climbed past the lower third of the screen: this animation is the thing you came to look at, so it waits until the heading is actually in front of you instead of playing while it is still a sliver at the bottom edge. Every gap in a heading opens together, so a heading wrapped over several lines keeps its line breaks the whole way; the marks then land one after another. Plays once, and with prefers-reduced-motion the chips are simply there. Scroll this preview out of view and reload to see it again.
Stagger#
A section can opt in to a load animation: set stagger and the lede cascades in on mount, each unit rising one beat after the last rather than the whole block appearing at once. It is off by default, so you choose where motion earns its keep. Pass stagger (or stagger={true}) for the default 70 ms cadence, or a number to set the gap between units yourself.
Two knobs shape how it reads. staggerBy picks the unit: "phrase" (default) reveals the lede part by part (eyebrow, heading, description, then the actions row); "word" reveals the heading and description word by word, so the copy types itself in. And staggerBlur adds a soft 4px → 0 focus pull as each unit lands, on top of the rise and fade. Try the combinations:
Build interfaces that feel alive
Choose how the lede arrives: reveal it part by part, or let the copy type itself in word by word, with or without a soft focus pull as each unit lands.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
By default the cascade plays on mount, the right gate for an above-the-fold load reveal. For a section further down the page, set staggerTrigger="inView" and it waits until the header scrolls into view, so the reveal happens as the reader arrives instead of finishing off-screen. (The demo above uses inView, which is why it replays each time you scroll back to it.)
The whole cascade is continuous: in word mode the eyebrow leads, the heading and description words flow in reading order, and the actions land last, one slot after the final word. It plays once and honors prefers-reduced-motion (every unit simply appears). It is backed by the same motion tokens as the Stagger primitive, so a header reads identically to a staggered list or grid below it. For the content below the header (a feature grid, a list of cards), wrap it in the Stagger primitive (which takes the matching blur and inView props) to keep one cascade down the section.
API reference#
SectionHeader
The root that sets alignment, scale and the entrance for its parts. Forwards all native <div> props, and every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| align | "left" | "center" | "left" | Aligns the lede and the actions row; centered text caps at max-w-2xl (max-w-3xl at lg). |
| orientation | "stacked" | "split" | "stacked" | split sets the lede and actions side by side from lg up; left-aligned only, it stacks below lg. |
| size | "sm" | "md" | "lg" | "md" | Visual scale of the heading and description; lg is the bold display step. |
| stagger | boolean | number | false | Cascades the eyebrow, heading, description and actions in: true at 70ms per unit, a number for that many ms. Honors reduced motion. |
| staggerBy | "phrase" | "word" | "phrase" | The cascade unit: part by part, or word by word through the heading and description. |
| staggerBlur | boolean | false | Adds a 4px to 0 focus pull to each unit's entrance. |
| staggerTrigger | "mount" | "inView" | "mount" | Plays the cascade on first render, or the first time the header scrolls into view. |
SectionHeaderText
Groups the lede (eyebrow Badge, heading, description) so alignment applies as a unit and it can sit beside the actions in split. Forwards all native <div> props.
SectionHeaderHeading
The heading; its visual scale comes from the root's size. Forwards all native heading props.
| Prop | Type | Default | Description |
|---|---|---|---|
| level | 1 | 2 | 3 | 2 | Semantic tag: h1, h2 or h3. |
SectionHeaderDescription
The muted supporting paragraph. Forwards all native <p> props.
SectionHeaderActions
The CTA row for Buttons, or an Input plus a Button; it wraps and aligns with the block. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "xl" | "md" | Control height imposed on every Button and field in the row; a control's own size still wins. |
| Data attribute | Description |
|---|---|
| data-control-size | The imposed control size. |
SectionHeaderChip
An inline logo, icon or emoji chip dropped between the heading's words, sized in em so it scales with the heading. Decorative (aria-hidden) unless it gets an aria-label, which exposes it as an image. Forwards all native <span> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "framed" | "bare" | "framed" | A rounded tile that frames the mark, or a naked glyph or emoji. |
| reveal | boolean | false | Holds the chip closed until the heading scrolls into view, then parts the words and pops the mark in; chips in one heading take turns. |
| Data attribute | Description |
|---|---|
| data-state | open or closed, on the reveal wrapper when reveal is set. |
| CSS variable | Description |
|---|---|
| --chip-w | The chip's measured width in em, which the reveal opens the gap to. |
| --chip-space | The heading's space width in em, swallowed while the chip is closed. |
| --chip-index | The chip's order in its heading, which staggers the marks. |
