Skip to content

HabInspectorSection ​

One section of an inspector panel's body: a small caps label, the body, an optional footer, and a rule to the next section. The piece a HabInspectorPanel body is made of, so every field in every inspector is labelled, spaced and divided the same way.

Activity
a_validate_policy
Name
Inputs
policy
cover_line
claimant
3 inputs
A plain section, a folded one with its summary, and a fill section with a footer
vue
<HabInspectorPanel title="Activity" flush>
	<div class="flex h-full flex-col">
		<HabInspectorSection title="Name">
			<HabInput v-model="name" aria-label="Name" />
		</HabInspectorSection>

		<HabInspectorSection v-model:open="open" title="Condition" collapsible summary="Always">
			…
		</HabInspectorSection>

		<HabInspectorSection title="Inputs" fill>
			<template #actions>
				<HabButton :icon="mdiPlus" icon-only aria-label="Add an input" class="ml-auto" />
			</template>
			…
			<template #footer>3 inputs</template>
		</HabInspectorSection>
	</div>
</HabInspectorPanel>

Inside a panel, and on its own ​

The section carries the panel's 16px column itself — the line the panel header's title and its footer already sit on. So put sections in a flush panel: a padded body would inset them twice. Standalone it needs nothing; it is a column with its own inset and a bottom rule.

The label is the inspector's 11px caps, the register HabDataTable's column heads and HabMetaList's keys use — not HabSectionHeader's eyebrow, which is 13px and sized to head a block on a page.

Leave title out for a section that is only a body, such as a list that runs straight under the panel header.

Folding ​

collapsible turns the title into a disclosure button that folds the body and the footer away. Bind v-model:open, or leave it unbound and the section owns it, starting open. Opt-in on purpose: a section that is one field folds to nothing worth saving.

summary says what the section holds in one line — "Always", "3 inputs" — and shows beside the title only while folded. It is part of the button, because it is what the reader decides to open by, and it truncates rather than pushing the chevron out.

The chevron sits at the far end of the row, so every section's title starts on the same line whether it folds or not. The whole row is the button except where an action is, including the empty stretch between them.

A folded body stays mounted, hidden: a half-typed value survives a fold.

Actions beside the title ​

The actions slot takes controls — an add button, a count. It is laid outside the disclosure button: an action never toggles the fold, and a button is never nested in a button.

Actions pack against the title. One that belongs at the far edge claims the space itself with ml-auto. In a collapsible section the order is title, actions, chevron: the actions sit between the two, so ml-auto lands them against the chevron and the chevron keeps the far edge in every section.

Filling the height ​

fill makes the section grow to fill its parent's height, with a body that scrolls inside it. The parent has to be a flex column with a definite height — in a panel, wrap the sections in <div class="flex h-full flex-col">.

The body starts at its natural height, so a short list keeps the footer right under it, and it only shrinks and scrolls once the content would overflow — at which point the footer is pinned to the bottom. Under fill the 16px inset moves off the section onto its header and footer, so the scrollbar reaches the true edges; rows in the body carry the inset themselves, as they do in a flush panel. The footer gets a rule above it.

A folded fill section gives its height back to its siblings.

Read-only ​

The section reads nothing from a HabReadonly region, so its fold stays usable in one. Folding changes nothing, and a reader has to be able to open a section to read it.

That is also why a read-only inspector cannot simply be made inert as a whole: inert cannot be lifted for one descendant, and it takes every fold with it. Making a body inert is your policy, not the section's — wrap the body slot, and the fold stays live outside the wrapper:

vue
<HabReadonly :readonly="locked">
	<HabInspectorSection title="Inputs" collapsible>
		<template #actions>
			<span class="contents" :inert="locked"><HabButton … /></span>
		</template>
		<div :inert="locked">…</div>
	</HabInspectorSection>
</HabReadonly>

Habitus form controls in the body need no wrapper: they already go read-only from the region, and stay focusable and readable. Reach for inert for what the region does not reach — buttons, and controls of your own.

Accessibility ​

  • A titled section is a role="group" named by its title, so a screen reader announces which field a control belongs to. Without a title it is a plain box.
  • The fold is a native button with aria-expanded and aria-controls pointing at the body. It needs a title to be its name; a collapsible section without one does not fold.
  • The chevron is decorative; the state is announced from aria-expanded.

Not verified here: the header's layout is a CSS subgrid and the fill body scrolls, and happy-dom lays out neither. Both were checked by eye in Chromium, which is not a test; they belong to the browser test layer.

API ​

Props ​

PropTypeDefaultDescription
collapsiblebooleanfalseThe title becomes a disclosure that folds the body away. Opt-in: a section that is one field folds to nothing worth saving, and a column of chevrons on every section is noise. Needs a title: it is the button's name, and without one the section does not fold.
fillbooleanfalseThe section grows to fill its parent's height, and its body scrolls inside it. The parent must be a flex column with a definite height. The body starts at its natural height, so a short list keeps the footer right under it, and only shrinks and scrolls once the content would overflow. The 16px inset moves off the section onto its header and footer, so the scrollbar reaches the true edges — rows in the body carry the inset themselves, as they do in a flush panel.
openbooleanundefinedWhether a collapsible section is open. Bind with v-model:open; left unbound, the section owns it and starts open. Ignored without collapsible — that body always shows.
summarystringundefinedWhat the section holds, in one line — "3 inputs", "Always". Shown beside the title only while the section is folded, and part of the disclosure button: it is what the reader decides to open by. Truncates.
titlestringundefinedThe section's label, set as the inspector's small uppercase caps. Leave it out for a section that is only a body — a list that runs straight under the panel header.

Events ​

EventPayloadDescription
update:openvalue: boolean—

Slots ​

SlotBindingsDescription
actionsanyControls beside the title — an add button, a count. Outside the disclosure button, so they never toggle the fold and are never a button inside a button. They pack against the title; one that belongs at the far edge claims the space itself with ml-auto.
defaultanyThe section body. Under fill it scrolls, and its rows carry the 16px inset.
footeranyBelow the body — a hint, a total, one action. Under fill it is pinned and ruled off. It folds away with the body.

Proprietary — internal use only.