Gesso
Data display

Description list

Label and value rows as joined tiles, each with an optional action.

Description list shows the facts about one thing, one row each: a review step's summary, a record's details, or a settings recap. Each row is a tile on the sunken well (bg-surface-sunken), set apart from the next by the stroke width, so the surface underneath draws the dividers. It reads on a page or a card, and on AppShell's tray it sits on its own color, so give it a card there. A row holds a label and a value, and an optional action at the top right of the row, usually an IconButton that jumps to where that fact is edited. Values can run several lines.

Template
Spring newsletter
Content
Spring fleet updateSubject: New trucks, same great serviceFrom: Santana Fleet <fleet@santana.com>
Audience
Spring list · 1,204 recipients
Delivery
Sent Sep 22, 9:00 AM

Usage

import { DescriptionItem, DescriptionList } from '@/components/ui/description-list';
import { IconButton } from '@/components/ui/icon-button';

<DescriptionList>
  <DescriptionItem
    label="Audience"
    action={<IconButton icon={PencilSimple} label="Edit audience" onClick={() => goTo('audience')} />}
  >
    Spring list · 1,204 recipients
  </DescriptionItem>
  <DescriptionItem label="Delivery">Sent Sep 22, 9:00 AM</DescriptionItem>
</DescriptionList>

The list has no padding of its own and rounds its corners at rounded-lg, clipping the tiles to them. Each tile has 16px of side padding and 12px above and below, the label is text-fg-muted, and the value is text-fg. Both are 14px in font-sans.

Layout follows the list's width

Once the list is wider than 28rem, labels take their own column, up to 10rem wide, and values line up beside them. The label, value, and action center on each other, so a chip in the value or an avatar in the label doesn't knock the others off their line. Narrower, as in a side panel or on a phone, each label sits above its value, and a row's action stays at the top right, level with the label. The list measures its own width and not the viewport's, so the same list works in a page and in a sheet.

Audience
Spring list · 1,204 recipients
Delivery
Sent Sep 22, 9:00 AM

Actions belong to a row

action takes a control, placed at the right of the row and pulled in so it does not make the row taller. It lines up with the label's line at the top right when labels stack, and centers on the row beside them, along with the label, even when the value runs several lines. It sits inside the row's value, so a row with an action reads as that fact plus its control. Name each action for its row ("Edit audience", not "Edit"), because a list of identical names tells nobody which one they are on.

Props

DescriptionList takes the props of a <dl>, and DescriptionItem takes those of a div plus these.

PropTypeDefaultWhat it does
labelReactNodeThe row's name, drawn in the label column.
actionReactNodeA control at the top right of the row, outside the value's own width.
childrenReactNodeThe value.

Accessibility

  • The list is a <dl>, and each row is a div holding one <dt> and one <dd>, which is the grouping HTML allows. A screen reader announces the label with its value and counts the terms in the list.
  • The action sits inside the <dd>, so it is read as part of the value, and Tab reaches it in row order. Its name comes from the control, so give it a label that includes the row.
  • The <dd> lays out as display: contents so its action can reach the label's line, and it stays in the accessibility tree as the value.
  • The tiles carry no role or state of their own, so a row is not selectable or focusable unless the action in it is.
  • Label and value use text-fg-muted and text-fg on bg-surface-sunken, so contrast follows the look you load.
  • Nothing animates, and target size is the action's own, since the row is not interactive.

When to use something else

  • Table for many things that share columns, which a reader compares and sorts. A description list is one thing in labels and values.
  • Cell fields to edit values in place inside a table, where a description list hands editing to an action beside each row.
  • Record list for many records, one line each, grouped into buckets with a keyboard model for moving through them.
  • Field for labels over controls in a form, where the value is typed in and not read.
  • A plain list of Item rows when entries are not label and value pairs.
  • A data grid library does not apply here. A description list is not tabular, so reach for one only if the facts have grown into rows and columns.

Install

shadcn add @gesso/description-list

On this page