Skip to content

List

In review
Live · web
Density

Grouped card: sections carry structure, no inset hairlines

Zebra (task 2763, experimental): opt-in alternating row tone

Full-bleed (plain): no card, edge-to-edge rules

Selectable · single · ↑/↓ to move, one cyan indicator

nockerl-design · docs siteStreaming · 2 tools running
api-server · gateway refactorIdle · last active 12m ago
credential-store · allowlist auditApproval required
dueydo · failed deployBuild exited 1

Selectable · multi · leading checkboxes

ListDemo.tsx
ListItemDemo.tsx
DividerDemo.tsx
ButtonDemo.tsx
tokens.css

Sticky section header: scroll the region

Loading (skeleton) & empty affordance

No sessions yetNew sessions will appear here.

Selected session r2 · 2 files checked · comfortable density. The container is live: change --color-divider and every hairline moves.

// Web: React, consuming @dizyx/nockerl-tokens. (Web is the laggard platform. Spec + live demo,
// not yet exported from @dizyx/nockerl-react; promotion is tracked.)
// `List` is the CONTAINER; it arranges `NockerlListItem` rows (see the List item page).
import { NockerlListItem } from '@dizyx/nockerl-react';
export function SessionList({ groups, activeId, onSelect, density }: SessionListProps) {
return (
// variant: 'card' = lifted, inset grouped card · 'plain' = full-bleed (the dense tree)
// selection: 'single' = listbox + arrow-key roving · 'multi' = leading checkboxes
<List variant="card" density={density} selection="single" aria-label="Sessions">
{groups.map((g) => (
<ListSection key={g.key} label={g.label} count={g.items.length} sticky>
{g.items.map((s, i) => (
<NockerlListItem
key={s.id}
primary={s.name}
secondary={s.statusLine}
status={s.status} // 'success' | 'warning' | 'error' | 'info'
value={s.timeAgo}
selected={s.id === activeId}
onSelect={() => onSelect(s.id)}
divider={i < g.items.length - 1} // inset hairline BETWEEN rows
/>
))}
</ListSection>
))}
</List>
);
}

List is the container; ListSection groups rows under a header; the row itself is ListItem. Selection, density, and the card-vs-plain surface are list-level concerns set on List.

PropTypeDefaultDescription
variant'card' | 'plain''card'Surface treatment. card = a lifted, inset grouped card (depth on the container; inset hairlines). plain = full-bleed, no card or shadow, edge-to-edge rules (the dense tree).
density'comfortable' | 'dense''comfortable'Row height + padding only, never the fill. dense tightens spacing and drops the supporting line.
selection'none' | 'single' | 'multi''none'List-level selection. single renders a listbox with arrow-key roving focus + the selected-row soft cyan wash + trailing check (never a stripe, Law 6); multi adds leading checkboxes.
children *ReactNodeListSection groups or bare ListItem rows.
label (ListSection) *stringSection header text, a muted uppercase label on the surface.
count (ListSection)numberOptional trailing count shown beside the section label.
sticky (ListSection)booleanfalsePin the header to the top of a scroll region; keeps the surface token under it so rows never bleed through.
divider (ListItem)booleantrueRender the hairline after the row, inset to the content column (clears the leading slot). Pass false on the last row of a group.