# Description list

The facts about one thing, as rows of a name and its value, such as an account’s details or a record’s fields, with an optional action on each.

Block: `smbk-description-list`. Status: experimental. Figma: the Figma file, https://www.figma.com/community/file/1688324308358870630. Desktop and mobile: differ by presentation only.

## Options

| Axis | Values | Default | Meaning |
|---|---|---|---|
| `layout` | `stacked`, `side-by-side` | `stacked` | Stacked puts each value under its name; side by side gives the names a column of their own. Side by side stacks in a narrow space, unless small. |
| `size` | `md`, `sm` | `md` | Small is for a narrow panel, about 320 to 360 wide: the small text sizes and closer rows, and side by side the names take about a quarter of the row, so short values stay on one line. |

Parts: `smbk-description-list__heading`, `smbk-description-list__rows`, `smbk-description-list__row`, `smbk-description-list__term`, `smbk-description-list__value`, `smbk-description-list__absent`, `smbk-description-list__icon`, `smbk-description-list__action`, `smbk-description-list__hidden`

## Markup

```html
<div class="smbk-description-list smbk-description-list--layout-stacked smbk-description-list--size-md">
  <h3 class="smbk-description-list__heading">Contact details</h3>
  <dl class="smbk-description-list__rows">
    <div class="smbk-description-list__row">
      <dt class="smbk-description-list__term">Email address</dt>
      <dd class="smbk-description-list__value">ana.silva@example.com</dd>
      <dd class="smbk-description-list__action"><a class="smbk-link smbk-link--size-md" href="#"><span class="smbk-link__label">Change<span class="smbk-description-list__hidden"> Email address</span></span></a></dd>
    </div>
    <div class="smbk-description-list__row">
      <dt class="smbk-description-list__term">Phone</dt>
      <dd class="smbk-description-list__value"><span class="smbk-description-list__absent">Not provided</span></dd>
      <dd class="smbk-description-list__action"><a class="smbk-link smbk-link--size-md" href="#"><span class="smbk-link__label">Change<span class="smbk-description-list__hidden"> Phone</span></span></a></dd>
    </div>
    <div class="smbk-description-list__row">
      <dt class="smbk-description-list__term">Date of birth</dt>
      <dd class="smbk-description-list__value"><span class="smbk-description-list__absent"><i class="ph ph-eye-slash smbk-icon smbk-icon--size-md smbk-description-list__icon" aria-hidden="true"></i>Hidden</span></dd>
      <dd class="smbk-description-list__action"><a class="smbk-link smbk-link--size-md" href="#"><span class="smbk-link__label">Change<span class="smbk-description-list__hidden"> Date of birth</span></span></a></dd>
    </div>
  </dl>
</div>
```

## React

```tsx
import { DescriptionList, DescriptionListRow } from './design-system/components/description-list/description-list.js';

<DescriptionList layout="stacked" size="md" heading="Contact details">
  <DescriptionListRow term="Email address" action={{ href: "#" }}>ana.silva@example.com</DescriptionListRow>
  <DescriptionListRow term="Phone" missing action={{ href: "#" }} />
  <DescriptionListRow term="Date of birth" redacted action={{ href: "#" }} />
</DescriptionList>
```

## Stylesheet

```css
/* smbk-description-list — Description list. Figma "Description list" 7113:1025 and "Description list row"
 * 7113:941. A heading over rows of a term and its value, and an optional action. Stacked, the term sits over
 * its value and the action under it; side by side, the term has a column of its own and the action ends the
 * row. Side by side lines the three up on their first lines, so words of two sizes share one baseline. A value
 * that is not shown, missing or hidden, says so in quieter words; one still loading is a line of skeleton as
 * tall as the value, so nothing moves when it arrives. */

.smbk-description-list {
  display: flex;
  flex-direction: column;
  gap: var(--smbk-space-stack-lg);
  min-width: 0;
}

/* The block frames the words it hides for screen readers, so they stay inside it wherever it is placed: without a
   frame they hang from the page itself and stretch it, past the end of an area that scrolls. No weight, so a parent
   that places the block keeps its say. */
:where(.smbk-description-list) {
  position: relative;
}

.smbk-description-list__heading {
  margin: 0;
  color: var(--smbk-color-text-default);
  font-family: var(--smbk-typography-heading-2xs-font-family);
  font-size: var(--smbk-typography-heading-2xs-font-size);
  font-weight: var(--smbk-typography-heading-2xs-font-weight);
  line-height: var(--smbk-typography-heading-2xs-line-height);
  letter-spacing: var(--smbk-typography-heading-2xs-letter-spacing);
  text-transform: var(--smbk-typography-heading-2xs-text-transform);
}

.smbk-description-list__rows {
  display: flex;
  flex-direction: column;
  gap: var(--smbk-space-stack-xl);
  margin: 0;
  padding: 0;
}

.smbk-description-list__row {
  display: flex;
  flex-direction: column;
  gap: var(--smbk-space-stack-sm);
  min-width: 0;
}

.smbk-description-list__term {
  margin: 0;
  color: var(--smbk-color-text-subtle);
  font-family: var(--smbk-typography-label-md-font-family);
  font-size: var(--smbk-typography-label-md-font-size);
  font-weight: var(--smbk-typography-label-md-font-weight);
  line-height: var(--smbk-typography-label-md-line-height);
  letter-spacing: var(--smbk-typography-label-md-letter-spacing);
  text-transform: var(--smbk-typography-label-md-text-transform);
  overflow-wrap: break-word;
}

.smbk-description-list__value {
  min-width: 0;
  margin: 0;
  color: var(--smbk-color-text-default);
  font-family: var(--smbk-typography-body-md-font-family);
  font-size: var(--smbk-typography-body-md-font-size);
  font-weight: var(--smbk-typography-body-md-font-weight);
  line-height: var(--smbk-typography-body-md-line-height);
  letter-spacing: var(--smbk-typography-body-md-letter-spacing);
  overflow-wrap: anywhere;
}

/* A value that is not shown: "Not provided", or "Hidden" after its icon. Never a value in color alone. */
/* Its icon sits on the words' baseline, which centers the glyph on the capitals and keeps the row's line. */
.smbk-description-list__absent {
  display: inline-flex;
  align-items: baseline;
  gap: var(--smbk-space-inline-xs);
  color: var(--smbk-color-text-subtle);
}

.smbk-description-list__icon {
  flex: none;
  color: var(--smbk-color-icon-subtle);
  font-size: var(--smbk-size-icon-sm);
}

.smbk-description-list__action {
  flex: none;
  margin: 0;
}

/* .smbk-description-list__hidden: hidden from view, kept for screen readers and the keyboard, by the shared rule in visually-hidden/visually-hidden.css. */

/* ---- side by side: the term in a column of its own, the value filling the rest, the action at the end ---- */

.smbk-description-list--layout-side-by-side .smbk-description-list__rows {
  gap: var(--smbk-space-stack-lg);
}

.smbk-description-list--layout-side-by-side .smbk-description-list__row {
  flex-direction: row;
  align-items: baseline;
  gap: var(--smbk-space-inline-xl);
}

.smbk-description-list--layout-side-by-side .smbk-description-list__term {
  flex: none;
  width: var(--smbk-size-width-240);
}

.smbk-description-list--layout-side-by-side .smbk-description-list__value {
  flex: 1 1 0;
}

/* A value still loading has no line of text to line up by: it sits level with the term's line. */
.smbk-description-list--layout-side-by-side .smbk-description-list__value[aria-busy="true"] {
  align-self: flex-start;
}

/* A narrow space has no room for a column of names: the rows stack, keeping the term before its value. */
@container style(--smbk-layout: narrow) {
  .smbk-description-list--layout-side-by-side .smbk-description-list__rows {
    gap: var(--smbk-space-stack-xl);
  }

  .smbk-description-list--layout-side-by-side .smbk-description-list__row {
    flex-direction: column;
    align-items: stretch;
    gap: var(--smbk-space-stack-sm);
  }

  .smbk-description-list--layout-side-by-side .smbk-description-list__term {
    width: auto;
  }

  .smbk-description-list--layout-side-by-side .smbk-description-list__value {
    flex: none;
  }
}

/* ---- small: for a narrow panel, such as a settings column about 320 to 360 wide. The term in Label SM and the value in
   Body SM, the rows closer together. Side by side, the term takes about a quarter of the row and never less than its
   longest word, so a short value stays on one line beside it; the narrow layout keeps the columns, since the small
   form is made for that space. ---- */

.smbk-description-list--size-sm .smbk-description-list__rows {
  gap: var(--smbk-space-stack-lg);
}

.smbk-description-list--size-sm .smbk-description-list__row {
  gap: var(--smbk-space-stack-xs);
}

.smbk-description-list--size-sm .smbk-description-list__term {
  font-family: var(--smbk-typography-label-sm-font-family);
  font-size: var(--smbk-typography-label-sm-font-size);
  font-weight: var(--smbk-typography-label-sm-font-weight);
  line-height: var(--smbk-typography-label-sm-line-height);
  letter-spacing: var(--smbk-typography-label-sm-letter-spacing);
  text-transform: var(--smbk-typography-label-sm-text-transform);
}

.smbk-description-list--size-sm .smbk-description-list__value {
  font-family: var(--smbk-typography-body-sm-font-family);
  font-size: var(--smbk-typography-body-sm-font-size);
  font-weight: var(--smbk-typography-body-sm-font-weight);
  line-height: var(--smbk-typography-body-sm-line-height);
  letter-spacing: var(--smbk-typography-body-sm-letter-spacing);
}

.smbk-description-list--size-sm .smbk-description-list__icon {
  font-size: var(--smbk-size-icon-xs);
}

.smbk-description-list--size-sm.smbk-description-list--layout-side-by-side .smbk-description-list__rows {
  gap: var(--smbk-space-stack-sm);
}

.smbk-description-list--size-sm.smbk-description-list--layout-side-by-side .smbk-description-list__row {
  flex-direction: row;
  align-items: baseline;
  gap: var(--smbk-space-inline-md);
}

.smbk-description-list--size-sm.smbk-description-list--layout-side-by-side .smbk-description-list__term {
  flex: 0 0 25%;
  width: auto;
  min-width: min-content;
}

.smbk-description-list--size-sm.smbk-description-list--layout-side-by-side .smbk-description-list__value {
  flex: 1 1 0;
}

@container style(--smbk-layout: narrow) {
  .smbk-description-list--size-sm.smbk-description-list--layout-side-by-side .smbk-description-list__rows {
    gap: var(--smbk-space-stack-sm);
  }

  .smbk-description-list--size-sm.smbk-description-list--layout-side-by-side .smbk-description-list__row {
    flex-direction: row;
    align-items: baseline;
    gap: var(--smbk-space-inline-md);
  }

  .smbk-description-list--size-sm.smbk-description-list--layout-side-by-side .smbk-description-list__term {
    width: auto;
  }

  .smbk-description-list--size-sm.smbk-description-list--layout-side-by-side .smbk-description-list__value {
    flex: 1 1 0;
  }
}
```

## Tokens it reads

```json
{
  "space": {
    "stack": {
      "lg": {
        "$type": "dimension",
        "$value": "{{space.16}}",
        "$description": "Gap between things stacked one above another, large."
      },
      "xl": {
        "$type": "dimension",
        "$value": "{{space.24}}",
        "$description": "Gap between things stacked one above another, extra large."
      },
      "sm": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Gap between things stacked one above another, small."
      },
      "xs": {
        "$type": "dimension",
        "$value": "{{space.4}}",
        "$description": "Gap between things stacked one above another, extra small."
      }
    },
    "inline": {
      "xs": {
        "$type": "dimension",
        "$value": "{{space.4}}",
        "$description": "Gap between things side by side, extra small."
      },
      "xl": {
        "$type": "dimension",
        "$value": "{{space.16}}",
        "$description": "Gap between things side by side, extra large."
      },
      "md": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Gap between things side by side, medium."
      }
    }
  },
  "color": {
    "text": {
      "default": {
        "$type": "color",
        "$value": "{{color.grey.900}}",
        "$description": "Main text."
      },
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Secondary text: descriptions, metadata."
      }
    },
    "icon": {
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Quiet icons."
      }
    }
  },
  "typography": {
    "heading-2xs": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Funnel Display, Funnel Display Fallback, system-ui, sans-serif",
        "fontSize": {
          "min": "18px",
          "max": "24px"
        },
        "fontWeight": 400,
        "lineHeight": 1.2,
        "letterSpacing": "0px"
      }
    },
    "label-md": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "14px",
        "fontWeight": 500,
        "lineHeight": 1.4,
        "letterSpacing": "0px"
      },
      "$description": "Label MD: labels on controls and short UI text (buttons, chips, tabs, menu headings). Geist Medium, sentence case. Code and keys use Code SM and Code MD."
    },
    "body-md": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "16px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    },
    "label-sm": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "12px",
        "fontWeight": 500,
        "lineHeight": 1.4,
        "letterSpacing": "0px"
      },
      "$description": "Label SM: labels on controls and short UI text (buttons, chips, tabs, menu headings). Geist Medium, sentence case. Code and keys use Code SM and Code MD."
    },
    "body-sm": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "14px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    }
  },
  "size": {
    "icon": {
      "sm": {
        "$type": "dimension",
        "$value": "{{size.16}}",
        "$description": "Icon box, small."
      },
      "xs": {
        "$type": "dimension",
        "$value": "{{size.12}}",
        "$description": "Icon box, extra small."
      }
    },
    "width": {
      "240": {
        "$type": "dimension",
        "$value": "240px",
        "$description": "A fixed width of 240 px: a narrow column, a list’s greatest height. Semantic sizes point here."
      }
    }
  }
}
```

## Purpose

The facts about one thing, as rows of a name and its value: an account's contact details, a record's fields, a site's settings at a glance. Each row can carry an action, such as a link to change the value. It differs from the list, which holds many things of one kind, and from the table, which compares many things across the same columns.

## Anatomy

- The block, a div around everything.
- The heading part, over the rows. Optional.
- The rows part, a real dl.
- The row part, a div inside the dl holding one name and its value.
- The term part, the dt: the name of the value.
- The value part, a dd: the value itself, which may be words, a link or a badge.
- The absent part, inside the value, for a value that is not shown: the missing words, or the hidden words after the icon part.
- The action part, a second dd holding a link, such as Change.
- The hidden part: words for screen readers only, such as the name that follows Change.

## When to use

- The details of an account, a person, an order or a site, read at a glance.
- A summary to check before sending a form, each row with a way to change it.
- A short record of a thing's properties, such as size and speed.

## When not to use

- Many things of the same kind. Use the [list](list.md).
- Many things compared across the same columns. Use the [table](table.md).
- Values to edit in place. Use [fields](field.md) in a form.

## Behavior

Stacked, each value sits under its name and the action under the value. Side by side, the names take a column of their own and each row ends with its action; the name, the value and the action share the line of their first words. Where the layout is narrow, side by side stacks before anything is cut off, and a long value wraps.

Two sizes. Medium, the default, is for a page, a card or a dialog. Small is for a narrow panel, such as a settings column about 320 to 360 pixels wide: the names and values take the small text sizes and the rows sit closer. Side by side and small, the names take about a quarter of the row, never less than their longest word, so a short value stays on one line beside its name; this form keeps its columns where the layout is narrow, since it is made for that space. The action links and the loading lines follow the size.

A value that is not given says "Not provided" in quieter words. A value that exists but is not shown here says "Hidden" after an eye icon, and nothing of the value is in the page. A value still loading is a line of skeleton as tall as the value, so nothing moves when it arrives. Each of these words can be changed.

## Accessibility

### What it does for you

- It is a real description list: a screen reader reads each name with its value.
- It takes no focus of its own. Each action link is a Tab stop.
- A missing value is said in words, never shown as a blank or a dash.
- A hidden value is never in the page, so it is not read; its icon is decoration and the word Hidden says it.
- A value still loading is read as "Loading".
- Side by side keeps the name before its value in the page, whatever the layout.

### What you must do

- Name each action for its row: words only a screen reader hears follow the visible ones, so a row of Change links reads "Change Email address", "Change Phone". Markup written by hand adds them in the hidden part.
- Leave a secret value out of the page rather than hiding it with style.

### How to test

- With a screen reader, each name is read with its value, a missing value as words, a hidden one as "Hidden", and a loading one as "Loading".
- Tab through the list: only the action links take focus, and each is read with its row's name.
- With text at 200% and the page 320 pixels wide, the names and values stay whole and the layout stacks.
- In a forced-color mode, the action links are still visible.

### Keyboard

| Key | What it does |
|---|---|
| Tab | Moves focus to the next action link. |
| Enter | Follows the focused action link. |

## Usage rules

### Do

- Say a missing value in words.
- Give each action a name of its own.
- Use side by side where there is room for the names' column, and stacked in a narrow card.
- Use small, side by side, in a narrow panel whose names and values are short, such as a tool's settings column.

### Don't

- Leave a value blank or put a dash in its place.
- Show a secret value and hide it with style: leave it out of the page.
- Use it for many things of one kind.
- Use small for long names or long values: they wrap a word a line in a quarter of a narrow row.
