HabSectionHeader
The label above a block of content.
Hosts
12The block this label is labelling.
Notification settings
3 unsavedThe block this title is titling.
<HabSectionHeader note="12">Hosts</HabSectionHeader>
<HabSectionHeader level="panel">Notification settings</HabSectionHeader>eyebrow is the small uppercase form used over a list or a grid. panel is the sentence-case title used inside a card header.
It owns no outer spacing
The gap between the header and whatever it labels is yours, at the call site:
<HabSectionHeader class="mb-3">Hosts</HabSectionHeader>
<HostList />A heading cannot know what follows it, so it cannot know whether a gap is wanted, and it certainly cannot know what spacing its parent has already applied. This is the rule the rest of the kit follows — no Habitus component spaces itself from a sibling — and HabSectionHeader was the one exception until it stopped being one.
It mattered most where it was least visible. HabPanel's header slot is documented as "usually a section heading" and its band owns px-5 py-4, so the two were designed to compose — and the header's own margin double-counted against the band's padding, putting the heading 9px off the optical centre of a band that looks symmetrical. Drop it in a panel header now and it simply centres:
<HabPanel>
<template #header>
<HabSectionHeader level="panel">Notification settings</HabSectionHeader>
</template>
…
</HabPanel>An eyebrow over a list is the case that costs you a class, since that one nearly always does want the gap. mb-3 is the value it used to apply.
Register and element are separate
level sets how it looks. as sets what it is:
<HabSectionHeader as="h3" level="panel">
Hosts
</HabSectionHeader>The design contract always emits an h2. A component that pins its own heading level cannot sit correctly in someone else's outline, so the element is yours — conflating the two is how a page ends up with an h2 inside an h4.
The action slot
Sessions
4 activeForm fields
8 fieldsIt sits at the far edge and is centred on the row, so a control of any height lines up with the heading rather than hanging below it.
The row has two alignment contexts, and that is the reason it can take either. The heading and the note are text at two different sizes and share a baseline — centring them against each other makes the note float. The action is usually a control, and a control is a box — putting it in that same baseline group aligns its label rather than its box, which leaves a 34px button sitting 4.5px high against the heading. An inner baseline group inside a centred row gets both.
So the slot takes a link or a button interchangeably, and you do not have to tell it which.
Accessibility
- Renders a real heading, so it appears in the document outline and in a screen reader's heading list. Pick
asto match where the block actually sits. - The note is beside the heading rather than inside it, so it is not part of the heading's accessible name.
- Nothing is rendered for
noteor the action slot when they are absent — an empty span in the baseline group shifts the heading's alignment.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
as | string | "h2" | The heading element. The design contract always emits h2; a component that pins its own heading level cannot sit correctly in someone else's outline, so this is yours to set. |
level | "eyebrow" | "panel" | "eyebrow" | Visual register. |
note | string | undefined | Secondary count or summary beside the heading. |
Slots
| Slot | Bindings | Description |
|---|---|---|
action | any | Right-aligned slot — usually a link or a small button. |
default | any | The heading text. |