Skip to content

HabListRow ​

One standalone row in a list of things you can open — recent activity, search results, entity lists. The whole row is the target.

A feed of things you open — the whole row is the target
vue
<HabListRow
	:icon="mdiInboxOutline"
	title="Evening briefing"
	meta="Session · Ada Lovelace"
	when="12m"
	chevron
	interactive
>
	<template #flag>
		<HabBadge tone="success">Live</HabBadge>
	</template>
</HabListRow>

Which row ​

Four things in this system are a row, and they look similar in a screenshot. The question that separates them is what the user does with it:

ComponentThe row is…
HabListRowa thing you open — bordered, spaced apart
HabListItema thing you operate — on a shared surface
HabDataTablea record whose fields line up in columns
HabCarda thing whose left border classifies it

If the row navigates away it is a HabListRow. If it changes something in place, or is one of a set being managed, it is a HabListItem.

Keep the chevron ​

It is the only thing distinguishing a row that opens from a row that merely holds controls — so the component warns when a row is interactive without one.

It is a warning rather than a forced chevron because a row whose trailing edge already carries a flag may deliberately skip it. But that is a decision, not a default.

Anatomy ​

The leading tile is a HabIconTile, carrying the entity hue — what kind of thing this is, not decoration. Swap the leading slot for a HabAvatar when the row is about a person: a tile says what, an avatar says who.

when is rendered tabular, so a column of timestamps lines up down the page rather than jittering.

A row with href stays an anchor; a row that is only interactive becomes a real <button>; a row with neither is a plain div with no hover at all. Same rule as HabButton: a link that looks like a row must still be a link, because middle-click, copy-link and open-in-new-tab are the browser's to provide.

Saving ​

A write against one row reports itself on the row. It does not block by default: a row's writes are small and frequent.
vue
<HabListRow :saving="saving" … />

A write against one row reports itself on the row — this is where it belongs, not on the panel around the list. The wave goes on the row itself, the button or link, and marks it aria-busy.

blocking is off by default, unlike every other host. A row's writes are small and frequent, and locking it for each one feels heavy; the wave alone says the write is in flight. Turn it on when acting on the row again mid-write would race — and note an inert row drops focus if it had it. The wave is the same one HabSavingFrame draws; its page has the rules.

Accessibility ​

  • title names the row. The component warns when there is neither a title nor content in the default slot, because the row is then an unnamed target.
  • A disabled button is disabled natively; a disabled link takes aria-disabled, since disabled means nothing on an anchor.
  • The leading tile is decorative and hidden from assistive tech — it repeats what meta already says in words.

API ​

Props ​

PropTypeDefaultDescription
blockingbooleanfalseWhether a saving wave also swallows input: a veil washes the row out and it goes inert. OFF by default, unlike every other host. A row's write is small and frequent, and locking it for every one feels heavy; the wave alone says the write is in flight. Turn it on when acting on the row again mid-write would race. Note an inert row drops focus if it had it — move focus deliberately first.
chevronbooleanfalseTrailing chevron. Keep it when the row navigates — it is the only thing distinguishing a row that opens from a row that merely holds controls.
disabledbooleanfalse—
hrefstringundefinedRenders as an anchor to this href instead of a button.
iconstringundefinedSVG path data from @mdi/js, for the leading tile.
iconColorstringundefinedThe tile's hue — the entity color, not decoration. Any CSS color, including one from data.
interactivebooleanfalseMakes the whole row the hit target, rendering it as a real <button>. Omit for a static row.
metastringundefinedSecond line: kind, who touched it, when.
savingbooleanfalseA write against this row's thing is in flight: a wave sweeps the row and it is marked aria-busy. This is the "a row reports itself on the row" case — a write against one row belongs here, not on the panel around the list.
titlestringundefinedThe thing's name. The row's own accessible name when it navigates.
whenstringundefinedShort tabular timestamp, e.g. 12m.

Slots ​

SlotBindingsDescription
defaultanyBody, in place of title and meta.
flaganyRight-aligned state pill, usually a HabBadge.
leadinganyReplaces the leading tile — pass a HabAvatar when the row is about a person.

Proprietary — internal use only.