Components · Layout
SectionHeader
Unboxed section title with description, meta, actions, a trailing link and busy state.
Meta and trailing link
import { SectionHeader, SectionHeaderLink } from '@rookcom/monarch-react';
<SectionHeader
title="Recent invoices"
meta="12 issued this month"
link={
<SectionHeaderLink href="#/components/section-header">
View all invoices
</SectionHeaderLink>
}
/>Usage examples
Description and actions
import { Button, SectionHeader } from '@rookcom/monarch-react';
// PlusIcon: demo icon from the docs site; use your own.
<SectionHeader
size="md"
title="Team members"
description="People who can sign in to this workspace."
actions={
<Button size="sm" variant="primary" leadingIcon={<PlusIcon />}>
Invite
</Button>
}
/>Section with busy state
import { useEffect, useState } from 'react';
import { Button, SectionHeader } from '@rookcom/monarch-react';
function BusyDemo() {
const [busy, setBusy] = useState(false);
useEffect(() => {
if (!busy) return;
const t = setTimeout(() => setBusy(false), 1500);
return () => clearTimeout(t);
}, [busy]);
return (
<SectionHeader
title="Traffic sources"
meta="Last 7 days"
busy={busy}
actions={
<Button size="xs" onClick={() => setBusy(true)}>
Refresh
</Button>
}
>
<ul style={{ margin: 0, paddingInlineStart: '1.25rem' }}>
<li>Search</li>
<li>Direct</li>
<li>Referral</li>
</ul>
</SectionHeader>
);
}
<BusyDemo />Purpose
Titles a section of a page or dashboard without drawing a box around it: a heading, an optional description and meta line, and trailing actions or a "View all" link. With content it becomes the labelled section itself.
Use when
- A dashboard or detail page is split into sections, each with a title and maybe a count or "View all" link.
- The header row inside a Card needs actions or a link next to the title.
- A section reloads in place and should say so (
busy).
Avoid when
- The page title: use PageHeader.
- A titled group of settings with a footer: use SettingsSection.
Design principles
- Unboxed: it adds no border or background, so it works on the page, inside a Card or in a split pane.
- The heading level is chosen by the page outline (
headingLevel), never by size; size issize. - Secondary text is quiet: description in muted text, meta smaller still.
- Trailing content stays on the title row while it fits and wraps under it on narrow screens.
API reference
import { SectionHeader, SectionHeaderLink } from '@rookcom/monarch-react';| Export | Kind | Rendering | Root class |
|---|---|---|---|
SectionHeader | component | server-safe | .mn-section-header- |
SectionHeaderLink | component | server-safe | .mn-section-header- |
Without children it renders <div class="mn-section-header"> with the header row; with children it renders <section aria-labelledby> with the row and a .mn-section-header__body. The ref goes to the root either way.
SectionHeaderLink renders an <a> (or your router link with asChild) with a decorative arrow.
Generated from the TypeScript declarations of packages/react/src/components/layout/section-header.
Properties
SectionHeader
An unboxed section header: title, optional description and meta line, and trailing actions or a "View all" link. With `children` it becomes the whole labelled `<section>`; without, it is the header row alone (for example inside a Card).
| Prop | Type | Description |
|---|---|---|
title required | ReactNode | The section title, rendered as a heading. |
actions | ReactNode | Buttons or a menu at the end of the header. |
busy | boolean | The section's content is loading or refreshing: sets `aria-busy`, shows a spinner after the title and, with `children`, dims the content. |
busyLabel | string | Accessible name of the busy spinner. Default "Loading". |
children | ReactNode | The section's content. When given, SectionHeader renders a `<section>` labelled by the heading, with the content under the header. |
className | string | |
description | ReactNode | A sentence under the title explaining the section. |
headingId | string | Id for the heading, to label something else with it. Generated when omitted. |
headingLevel | "h2" | "h3" | "h4" | "h5" | "h6" | Heading element. Pick the level that fits the page outline. Default `h2`. |
id | string | |
link | ReactNode | A trailing navigation link, usually SectionHeaderLink ("View all"). Placed after `actions`. |
meta | ReactNode | A short secondary line: a count, a date range, "Updated 5 min ago". |
size | "sm" | "md" | `sm` (default) for dashboard sections, `md` for page sections. |
SectionHeaderLink
The trailing "View all" link of a SectionHeader, with a decorative arrow. Give it text that makes sense out of context ("View all invoices") or an `aria-label`, since a page often has several.
| Prop | Type | Description |
|---|---|---|
asChild | boolean | Render the child element (a router link) with this styling. |
children | ReactNode | |
className | string | |
id | string |
Variants
| Prop | Values | Description |
|---|---|---|
size | 'sm''md' | sm (default, 14px semibold) for dashboard sections; md (16px) for page sections. Exposed as data-size. |
headingLevel | 'h2''h3''h4''h5''h6' | The heading element. Default h2. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Busy | data-busy, aria-busy="true" | A small spinner named by busyLabel ("Loading") follows the title, and the section content dims to 60% opacity. |
Interaction behaviour
- No behaviour of its own. Actions and links are ordinary controls in reading order after the title.
- Busy is driven by the product; content stays mounted while it dims.
Keyboard support
| Keys | Action |
|---|---|
| Tab | Moves through the actions and then the trailing link, in source order. |
SectionHeader adds no key handling; the slotted controls keep their own.
Accessibility
- Role
- region (with children, via
<section aria-labelledby>), otherwise none - Accessible name
- The heading. Pass
headingIdto label another element with it.
- Choose
headingLevelso the outline does not skip levels. - With
busy,aria-busyis set on the root and the spinner is a named status. - The link arrow is
aria-hidden; write link text that makes sense alone ("View all invoices").
Responsive behaviour
- The row wraps: when the title and trailing controls do not fit, the controls move under the text (
flex-wrap). - Long titles wrap instead of overflowing (
overflow-wrap: anywhere), so there is no horizontal scrolling at 320px.
Theme support
- Text uses
--mn-color-text-defaultand--mn-color-text-muted; no surfaces of its own, so it inherits the page or card behind it in every theme. - The link focus ring uses
--mn-color-focus-ring; the dim transition uses duration tokens, which are zero under reduced motion.
Do and don't
Do
- Pick
headingLevelfrom the page outline: h2 under the page h1, h3 inside an h2 section. - Pass
childrenwhen the header titles a whole section, so the section is a labelled region. - Keep meta short: a count, a period, a freshness note.
Don't
- Do not use it as the page title.
- Do not use
size="md"to fake a higher heading level, or a lower level to make text smaller. - Do not put more than two or three trailing controls; move the rest into a menu.
Related components
- PageHeader: The page title, with breadcrumbs and page actions.
- SettingsSection: A titled card of settings rows with a footer.
- Card: Boxes a section; put a SectionHeader inside it for actions.
- SectionCard: Planned card with a standard header and actions.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | @radix-ui/react-slotreact |
| Monarch components | Spinner |
Testing coverage
- Renders title (h2), description, meta, actions and the trailing link; Tab order is actions then link; no region without children.
- With children it is a region labelled by its heading, honouring
headingLevelandheadingId. - Busy sets aria-busy and data-busy and shows a named spinner, and clears when done.
- Forwards the ref, passes attributes through and exposes
size. - SectionHeaderLink renders a router link with
asChild, with a hidden arrow. - Renders on the server.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery; Cross-browser axe: /components/section-header |
Tests: packages/react/src/components/layout/section-header