rather than a grid of divs.
- Group: Surfaces
- Import: `import { DescriptionList } from '@misoto22/design'`
- Page: https://ui.misoto22.com/components/description-list/
- Related: table, card, timestamp
## When to reach for it
One record seen from the front — a detail page, a summary panel. Several records seen from above is a Table.
## Anatomy
- **List** (required) — The
. It is the element that carries the pairing: a grid of divs looks identical and tells a screen reader there are two columns of unrelated text.
- **Pair** (required) — A
around each dt/dd, which the HTML specification allows inside a
precisely so a pair can be laid out as a unit. It is what the hairline is drawn on, so the rule crosses the whole row rather than stopping in the column gap.
- **Term** (required) — item.term, rendered as
at --ink-3-aa. In the row layout it holds a 12rem column at sm and above and stacks below it, because a 12rem label column on a phone leaves the value about eight characters wide.
- **Description** (required) — item.description, rendered as
at --ink-2 with its browser margin reset. It takes a node, not a string, so a value can be a Badge, a link or a Timestamp.
- **Hairline** — divided, on by default: a --rule under every pair but the last. Turn it off inside a Card, which already has an edge of its own.
## Best practices
### Do
- Let it render nothing when items is empty. It returns null rather than an empty bordered box, so the page above is free to show an EmptyState instead of a hairline around no content.
- Put an element in description when the value is a state or a link — it is a
, so a Badge, an anchor or a Timestamp belongs there and a string of text does not have to be faked into one.
- Pass id on each item when rows are added, removed or reordered. The index is the key without one, which is right for the fixed field list a record page renders and wrong for a list that changes shape.
### Don’t
- Do not reach for it to show several records. Every dt would repeat down the page and a reader comparing two records has to hold both in their head — that is what a Table’s column headings exist to avoid.
- Do not use layout="row" inside a narrow sidebar. It only collapses on the sm breakpoint, which is the viewport, not the container — a 12rem label column inside a 20rem panel leaves nothing for the value. Use layout="stacked" there.
## Accessibility
- A real
,
and
, which is what tells a screen reader that a label names the value beside it.
- Each pair is grouped in a
, which the specification permits inside a
and which assistive technology reads through.
- An empty list renders null rather than an empty
, so nothing announces a list with no items in it.
## DescriptionList
A record's fields: label, value, label, value. The single most repeated shape in any detail page, and the one most often hand-built out of a `
` grid — which loses the only thing the markup was carrying. A `
` tells a screen reader that "Owner" names the thing beside it; a grid of divs tells it there are two columns of unrelated text, and the reader has to infer the pairing from reading order alone. Reach for `Table` instead when there are several records. This is one record seen from the front; a table is many records seen from above. Each pair is wrapped in a `
` — which the HTML specification allows inside a `
` precisely so a pair can be laid out as a unit — so the hairline runs the full width of the row rather than stopping in the column gap. An empty `items` renders `null` rather than an empty bordered box. A record with no fields is a state the page above should be handling with an `EmptyState`, and a hairline around nothing looks like a component that failed to load.
### Props
- `items` (required) — `DescriptionListItem[]`. The pairs, in the order they should read. An empty array renders nothing.
- `layout` — `DescriptionListLayout` default `'row'`. How the value sits relative to its label. See DescriptionListLayout.
- `divided` — `boolean` default `true`. Draws a hairline under every pair but the last. Off for a list inside a card that already has an edge, on for a list that is the page's structure.
Also accepts: `HTMLAttributes`.
## Example — record fields
```tsx
import { Badge, DescriptionList, Timestamp } from '@misoto22/design'
Deployed },
{
term: 'Last deploy',
description: ,
},
]}
/>
```
## Example — row and stacked
```tsx
import { DescriptionList } from '@misoto22/design'
```
## Example — a metadata rail
```tsx
import { Badge, DescriptionList, Heading, Text, Timestamp } from '@misoto22/design'
ui.misoto22.com
The documentation site for the design package. Statically exported, so every
page is HTML on a CDN and the only JavaScript is the parts a reader touches.