@k2b/ui
Description list
Semantic responsive key-value details with optional actions.
Semantic key-value content in a responsive grid or compact inspector rows. Both layouts retain real dl/dt/dd elements and support optional actions. A plain control as a row's value turns the whole row into that control.
- Owner
- Platform team
- Region
- Europe West
- Created
- 31 July 2026
- Repository
- cloud
- Created
- 31 July 2026, 14:32
- Updated
- 13 August 2026, 18:41
- ID
- Res7K2
- Due
- Estimate
- Priority
- Labels
- Hardware
- Created
- 31 July 2026, 14:32
TSX
Copy{/* Scan-friendly facts */}<DescriptionList columns={2} items={facts} /> {/* Compact metadata in a detail panel */}<DescriptionList layout="rows" size="sm" actionVisibility="progressive" items={metadata}/> {/* Property rows: the value is the control, the whole row opens it */}<DescriptionList layout="rows" size="sm" items={[ { term: "Due", description: <DateTimePicker aria-label="Due" appearance="plain" placeholder="No due date" clearable value={due} onValueChange={setDue} /> }, { term: "Estimate", description: <NumberInput aria-label="Estimate" appearance="plain" placeholder="No estimate" suffix="min" min={1} formatValue={formatDuration} value={estimate} onValueCommit={setEstimate} /> }, { term: "Priority", description: <Select aria-label="Priority" appearance="plain" placeholder="No priority" clearable options={priorities} value={priority} onValueChange={setPriority} /> }, { term: "Labels", description: <MultiSelectInput aria-label="Labels" appearance="plain" placeholder="Label" placeholderIcon="ti ti-plus" options={labelOptions} value={labels} onValueChange={setLabels} /> }, ]}/>DescriptionList presents exact key-value information with native description-list semantics.
Use DescriptionList
Use it for metadata, summaries, and compact detail panels. In layout="rows", a
value may contain one compact control such as a status or assignee picker when
that control has its own accessible name. Use a normal form layout for broader
editing, and do not use the component for arbitrary card layouts.
Assistant details views use DescriptionList with layout="rows" for one
record's 1..100 exact fields. These rows are read-only; number and date text
uses the viewer's locale. A Cloud reference carries no URL and opens through
the owning app's reader with the viewer's access after a confirmation; its pill
keeps the neutral page icon, because a reference names an app object rather
than a kind the chat knows. A hidden copy with stand-in text of each value's
length reserves the rows, including lines that wrap on a phone, up to twelve
one-line rows; more scrolls inside the record.
Property rows
A property row shows a value that people edit in place: label left, value as
plain text, and the whole row as the control. Put a control with
appearance="plain" directly into a row's description:
Select and MultiSelectInput,
the date pickers, or
NumberInput. Give it an
"aria-label" that repeats the term.
The row keeps its height and columns. Hovering anywhere in the row, keyboard
focus, or an open picker paints a quiet surface 0.5rem past the row on both
sides, behind the label and value; focus adds the ring to that surface. A click
on the label opens the picker or edits the number. Nothing in the list moves
between these states, and an item action stays clickable above the row.
Because the surface reaches past the row, keep 0.5rem of room on both sides,
as a DetailPanel.Summary or a section inside a DetailPanel.Group does. A
list flush against a scrolling or clipping edge cuts off the surface and its
ring.
Mix property rows with plain text rows freely. For people who cannot edit, render
the same value as text, such as the dot and label of a priority or the
Tag chips of labels, instead of a disabled control.
The row then has no hover surface.
In layout="rows", the term sits where a one-line value sits. A value made
of row-height lines, such as wrapping plain pills or a list of linked items,
keeps the term beside its first line. Plain text that wraps starts at the top
of the row, so the term sits slightly below its first line; keep row values
short and move long text to its own section.
Import
import { Button, DescriptionList } from "@k2b/ui";Layout
columns controls the wide-screen grid and collapses to one column on narrow
screens. Set layout="rows" for a compact inspector-style label/value list. An
item may provide one short action directly related to its value. Set
actionVisibility="progressive" when repeated row actions should stay quiet:
with a mouse or trackpad, including a tablet with a mouse, they stay hidden
until their row is hovered, keyboard-focused, or has its menu open. Touch-only
screens always show them.
Use layout="compact" for read-mostly metadata in previews and small panels.
Labels size to their content (up to 40% of the available width), with a smaller
horizontal gap than the space between rows. Unlike inspector rows, compact rows
do not reserve extra height for controls. Values can wrap without losing their
association with the label.
API reference
type DescriptionListItem = {
term: JSX.Element; description: JSX.Element; action?: JSX.Element;
};
type DescriptionListProps = {
items: readonly DescriptionListItem[]; columns?: 1 | 2 | 3; layout?: "grid" | "rows" | "compact"; size?: "sm" | "md";
actionVisibility?: "always" | "progressive"; class?: string;
};columns defaults to 1, layout to "grid", and actionVisibility to "always"; size defaults to "md". Item content accepts Solid JSX. Only a plain control that is the direct value of a rows item takes over the row; nested deeper, such as an add action below a list of entries, it covers only itself.
Accessibility
The component renders real dl, dt, and dd elements. A property row's
control keeps its own role and name; the row-wide hit area only extends where a
pointer reaches it and adds no second tab stop. Terms must be concise,
descriptions must remain meaningful without visual position, and icon-only
actions need labels. Progressive actions remain keyboard-focusable and become
visible through focus-within. An open menu keeps its trigger visible, and
touch-only screens always show progressive actions. Visibility must never be
the only indication that an action exists.
Runtime
Description lists are server-renderable and need no client JavaScript unless an item action is interactive.
Example
<DescriptionList
layout="rows"
actionVisibility="progressive"
items={[
{ term: "Owner", description: "Platform team" },
{ term: "Region", description: "Europe West" },
{ term: "Repository", description: "cloud", action: <Button size="xs">Open</Button> },
]}
/>