# List

Rows of the same kind, one thing each: plain, bulleted or numbered text, or rows with an icon, a title and a quiet line that go somewhere, do something or can be chosen.

Block: `smbk-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 |
|---|---|---|---|
| `type` | `plain`, `bulleted`, `numbered`, `navigation`, `action`, `selectable` | `plain` | Plain, bulleted and numbered lists are text. Navigation rows are links, action rows are buttons or hold one, and selectable rows are options a reader chooses. |
| `size` | `sm`, `md` | `sm` | The rows of navigation, action and selectable lists: sm rows are at least 40 tall, md rows 48, with more room above and below. Text lists have one size. |
| `dividers` | on / off | off | A line between rows, for rows that are actions or choices. Text lists are grouped by their spacing and rarely need one. |
| `tickless` | on / off | off | A selectable list whose chosen row shows by its fill and a bolder title, with no tick: for a list where choosing a row opens it, such as an inbox beside its reading pane, where a tick would read as chosen for later. The keyboard and what a screen reader hears stay the same. |

Parts: `smbk-list__title`, `smbk-list__items`, `smbk-list__item`, `smbk-list__marker`, `smbk-list__text`, `smbk-list__row`, `smbk-list__start`, `smbk-list__leading`, `smbk-list__body`, `smbk-list__item-title`, `smbk-list__meta`, `smbk-list__trailing`, `smbk-list__check`, `smbk-list__end`, `smbk-list__divider`, `smbk-list__empty`, `smbk-list__glide`

## Markup

```html
<nav class="smbk-list smbk-list--type-navigation smbk-list--size-sm" aria-labelledby="list-1">
  <p id="list-1" class="smbk-list__title">Recent files</p>
  <ul class="smbk-list__items" role="list">
    <li class="smbk-list__item">
      <a
        class="smbk-list__row"
        href="#"
        aria-current="page"
        aria-labelledby="list-2-title"
        aria-describedby="list-2-meta"
      >
        <i
          class="ph ph-files smbk-icon smbk-icon--size-md smbk-list__leading"
          aria-hidden="true"
        ></i>
        <span class="smbk-list__body">
          <span
            id="list-2-title"
            class="smbk-list__item-title"
            data-label="Check the color pairs in both modes"
          >Check the color pairs in both modes</span>
          <span id="list-2-meta" class="smbk-list__meta">Edited 2 hours ago by Ana</span>
        </span>
        <i
          class="ph ph-caret-right smbk-icon smbk-icon--size-md smbk-list__trailing"
          aria-hidden="true"
        ></i>
      </a>
    </li>
    <li class="smbk-list__item">
      <a
        class="smbk-list__row"
        href="#"
        aria-labelledby="list-3-title"
        aria-describedby="list-3-meta"
      >
        <i
          class="ph ph-files smbk-icon smbk-icon--size-md smbk-list__leading"
          aria-hidden="true"
        ></i>
        <span class="smbk-list__body">
          <span
            id="list-3-title"
            class="smbk-list__item-title"
            data-label="Name every layer after its job"
          >Name every layer after its job</span>
          <span id="list-3-meta" class="smbk-list__meta">Edited yesterday by Sam</span>
        </span>
        <i
          class="ph ph-caret-right smbk-icon smbk-icon--size-md smbk-list__trailing"
          aria-hidden="true"
        ></i>
      </a>
    </li>
    <li class="smbk-list__item">
      <a
        class="smbk-list__row"
        href="#"
        aria-labelledby="list-4-title"
        aria-describedby="list-4-meta"
      >
        <i
          class="ph ph-files smbk-icon smbk-icon--size-md smbk-list__leading"
          aria-hidden="true"
        ></i>
        <span class="smbk-list__body">
          <span
            id="list-4-title"
            class="smbk-list__item-title"
            data-label="Describe every component set"
          >Describe every component set</span>
          <span id="list-4-meta" class="smbk-list__meta">Edited on Monday by Ana</span>
        </span>
        <i
          class="ph ph-caret-right smbk-icon smbk-icon--size-md smbk-list__trailing"
          aria-hidden="true"
        ></i>
      </a>
    </li>
  </ul>
</nav>
```

## React

```tsx
import { List, ListItem } from './design-system/components/list/list.js';

<List type="navigation" size="sm" defaultValue="1" title="Recent files">
  <ListItem value="0" href="#" current meta="Edited 2 hours ago by Ana" leadingIcon="Files" trailingIcon="CaretRight">Check the color pairs in both modes</ListItem>
  <ListItem value="1" href="#" meta="Edited yesterday by Sam" leadingIcon="Files" trailingIcon="CaretRight">Name every layer after its job</ListItem>
  <ListItem value="2" href="#" meta="Edited on Monday by Ana" leadingIcon="Files" trailingIcon="CaretRight">Describe every component set</ListItem>
</List>
```

## Stylesheet

```css
/* smbk-list — List. Figma "List" 7113:863 (Type × Dividers) and "List item" 7113:173 (State × Size).
 * A title over rows of one kind, and the words to show when there are none. Plain, bulleted and numbered
 * lists are text, a line of list spacing apart; a bulleted or numbered row puts its marker in front of its
 * text, and a line that wraps keeps under the text, not under the marker. Navigation, action and selectable
 * lists are rows with an icon, a title and a quiet line, touching: the row is the link, the button or the
 * option, and its states are the browser's and the markup's own (hover, pressed, focus, aria-current,
 * aria-selected, disabled), never classes. A row's edge is drawn inside its height: its padding gives the
 * edge's width back. The dividers are the divider component, ending every row but the last. */

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

.smbk-list__title {
  margin: 0;
  color: var(--smbk-color-text-default);
  font-family: var(--smbk-typography-label-lg-font-family);
  font-size: var(--smbk-typography-label-lg-font-size);
  font-weight: var(--smbk-typography-label-lg-font-weight);
  line-height: var(--smbk-typography-label-lg-line-height);
  letter-spacing: var(--smbk-typography-label-lg-letter-spacing);
  text-transform: var(--smbk-typography-label-lg-text-transform);
}

.smbk-list__items {
  display: flex;
  flex-direction: column;
  gap: var(--smbk-space-text-list);
  margin: 0;
  padding: 0;
  list-style: none;
  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);
}

.smbk-list__item {
  min-width: 0;
  overflow-wrap: break-word;
}

.smbk-list__empty {
  margin: 0;
  color: var(--smbk-color-text-subtle);
  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);
}

/* ---- text lists: bulleted and numbered rows hold a marker and the text; the marker hugs a bullet and
   gives a number a fixed column, so the texts of a numbered list line up. ---- */

.smbk-list:is(.smbk-list--type-bulleted, .smbk-list--type-numbered) .smbk-list__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--smbk-space-inline-md);
  row-gap: var(--smbk-space-text-list);
}

.smbk-list__marker {
  flex: none;
  color: var(--smbk-color-text-subtle);
}

.smbk-list--type-numbered .smbk-list__marker {
  min-width: var(--smbk-size-20);
}

/* A numbered row's marker left empty counts itself; one written by hand shows what was written. */
.smbk-list--type-numbered .smbk-list__items {
  counter-reset: smbk-list;
}

.smbk-list--type-numbered .smbk-list__item {
  counter-increment: smbk-list;
}

.smbk-list--type-numbered .smbk-list__marker:empty::before {
  content: counter(smbk-list) ".";
}

.smbk-list__text {
  flex: 1 1 0;
  min-width: 0;
}

/* ---- rows: navigation, action and selectable ---- */

.smbk-list:is(.smbk-list--type-navigation, .smbk-list--type-action, .smbk-list--type-selectable) .smbk-list__items {
  gap: var(--smbk-space-0);
}

.smbk-list:is(.smbk-list--type-navigation, .smbk-list--type-action, .smbk-list--type-selectable) .smbk-list__item {
  display: flex;
  flex-direction: column;
}

.smbk-list__row {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: var(--smbk-space-inline-lg);
  width: 100%;
  min-height: var(--smbk-size-control-md);
  margin: 0;
  padding: calc(var(--smbk-space-inset-md) - var(--smbk-border-width-hairline)) calc(var(--smbk-space-inset-lg) - var(--smbk-border-width-hairline));
  border: var(--smbk-border-width-hairline) solid transparent;
  border-radius: var(--smbk-radius-control-md);
  background: transparent;
  color: var(--smbk-color-text-default);
  font: inherit;
  letter-spacing: inherit;
  text-align: start;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

.smbk-list--size-md .smbk-list__row {
  min-height: var(--smbk-size-control-lg);
  padding-block: calc(var(--smbk-space-inset-lg) - var(--smbk-border-width-hairline));
}

:where(a[href], button).smbk-list__row,
.smbk-list__item[role="option"] {
  cursor: pointer;
}

.smbk-list__start,
.smbk-list__end {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--smbk-space-stack-sm);
}

.smbk-list__leading {
  flex: none;
  color: var(--smbk-color-icon-subtle);
  font-size: var(--smbk-size-icon-md);
}

.smbk-list__body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--smbk-space-text-label);
  min-width: 0;
}

.smbk-list__item-title {
  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);
}

/* The title is always as wide as its bolder self: an invisible copy of its words (`data-label`, which the wrapper
   writes) at the current row's weight lies under it with no height, so a title that turns bold never pushes
   anything beside it. The copy is generated content, hidden, with empty words for assistive technology, so the
   title is read once. */
.smbk-list__item-title[data-label]::after {
  content: attr(data-label);
  content: attr(data-label) / "";
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: var(--smbk-typography-body-md-medium-font-weight);
  pointer-events: none;
  user-select: none;
}

.smbk-list__meta {
  color: var(--smbk-color-text-subtle);
  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-list__trailing {
  flex: none;
  color: var(--smbk-color-icon-subtle);
  font-size: var(--smbk-size-icon-sm);
}

/* The tick of a chosen option: in every option, so a row never changes width as it is chosen, and shown
   only by the option's own aria-selected, so the look and what a screen reader hears always agree. */
.smbk-list__check {
  flex: none;
  visibility: hidden;
  color: var(--smbk-color-icon-default);
  font-size: var(--smbk-size-icon-sm);
}

/* ---- states. Hover and pressed fill a row that can be pressed; the current row and the chosen option
   keep their own look under the pointer. ---- */

/* With the wrapper the pointer's fill is the glide part instead (below), so a row's own hover is drawn only in
   markup without it. */
@media (hover: hover) {
  :where(a[href], button:enabled).smbk-list__row:hover:where(:not([data-glide] *)),
  .smbk-list__item:where([role="option"]:not([aria-disabled="true"])):hover:where(:not([data-glide] *)) > .smbk-list__row {
    background: var(--smbk-color-action-ghost-background-hover);
  }
}

:where(a[href], button:enabled).smbk-list__row:active,
.smbk-list__item:where([role="option"]:not([aria-disabled="true"])):active > .smbk-list__row {
  background: var(--smbk-color-action-ghost-background-pressed);
}

/* An option takes focus itself; its row draws the ring. */
.smbk-list__item[role="option"] {
  outline: none;
}

.smbk-list__row:focus-visible,
.smbk-list__item[role="option"]:focus-visible > .smbk-list__row {
  outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
  outline-offset: var(--smbk-focus-ring-offset);
}

/* Where the reader is: the default surface, the rim for its edge and the title one weight bolder. */
.smbk-list__row[aria-current]:not([aria-current="false"]) {
  border-color: var(--smbk-color-border-rim);
  background: var(--smbk-color-surface-default);
}

.smbk-list__row[aria-current]:not([aria-current="false"]) .smbk-list__item-title {
  font-weight: var(--smbk-typography-body-md-medium-font-weight);
}

/* A chosen option: the see-through selected fill and its tick. The fill shows on the page and on a card alike,
   and is the fill made for chosen items, as in the sidebar. */
.smbk-list__item[aria-selected="true"] > .smbk-list__row {
  background: var(--smbk-color-surface-selected);
}

.smbk-list__item[aria-selected="true"] > .smbk-list__row .smbk-list__check {
  visibility: visible;
}

/* Tickless: a row that opens as it is chosen keeps the fill and takes the title one weight bolder, as the current row
   does, so it never shows by color alone; the tick takes no room. */
.smbk-list--tickless .smbk-list__check {
  display: none;
}

.smbk-list--tickless .smbk-list__item[aria-selected="true"] > .smbk-list__row .smbk-list__item-title {
  font-weight: var(--smbk-typography-body-md-medium-font-weight);
}

.smbk-list__row:is(:disabled, [aria-disabled="true"]),
.smbk-list__item[aria-disabled="true"] > .smbk-list__row {
  cursor: default;
}

.smbk-list__row:is(:disabled, [aria-disabled="true"]) :is(.smbk-list__item-title, .smbk-list__meta),
.smbk-list__item[aria-disabled="true"] > .smbk-list__row :is(.smbk-list__item-title, .smbk-list__meta) {
  color: var(--smbk-color-text-disabled);
}

.smbk-list__row:is(:disabled, [aria-disabled="true"]) :is(.smbk-list__leading, .smbk-list__trailing, .smbk-list__check),
.smbk-list__item[aria-disabled="true"] > .smbk-list__row :is(.smbk-list__leading, .smbk-list__trailing, .smbk-list__check) {
  color: var(--smbk-color-icon-disabled);
}

/* ---- the glide: with the wrapper, when every row can be pressed, the hover fill is one part that slides to the
   row under the mouse (glide.ts) instead of each row lighting up on its own, so a pointer on a divider or past
   the last row still lights the nearest row. The wrapper adds it as the rows' first child and marks the list
   data-glide ("lit" while it shows); the rows are lifted over it, so a current or chosen row's own fill still
   shows over it. It fades in where it lands and out as the pointer leaves, and slides with the feedback purpose;
   under reduced motion it lands at once and only fades. A touch screen draws none. ---- */

:where(.smbk-list__items[data-glide]) {
  position: relative;
}

:where(.smbk-list__items[data-glide] > :not(.smbk-list__glide)) {
  position: relative;
}

.smbk-list__glide {
  position: absolute;
  top: 0;
  left: 0;
  box-sizing: border-box;
  margin: 0;
  border-radius: var(--smbk-radius-control-md);
  background: var(--smbk-color-action-ghost-background-hover);
  opacity: 0;
  pointer-events: none;
  list-style: none;
  transition-property: translate, width, height, opacity;
  transition-duration: var(--smbk-motion-feedback-duration);
  transition-timing-function: var(--smbk-motion-feedback-easing);
}

.smbk-list__items[data-glide="lit"] > .smbk-list__glide {
  opacity: 1;
}

.smbk-list__glide[data-instant] {
  transition-property: opacity;
}

@media (prefers-reduced-motion: reduce) {
  .smbk-list__glide {
    transition-property: opacity;
  }
}

@media (hover: none) {
  .smbk-list__glide {
    display: none;
  }
}

/* ---- dividers: the divider component ends each row but the last. Between text rows it keeps the list's
   spacing on both sides; between rows that can be pressed the rows touch it. ---- */

.smbk-list__item:last-child > .smbk-list__divider {
  display: none;
}

.smbk-list--type-plain .smbk-list__divider {
  margin-block-start: var(--smbk-space-text-list);
}

.smbk-list:is(.smbk-list--type-bulleted, .smbk-list--type-numbered) .smbk-list__divider {
  flex-basis: 100%;
}

/* ---- touch: every row takes an invisible press area ----
   On a touch screen an invisible area, centred on each row, takes the press out to the comfortable target in each
   direction it falls short of it, except where two rows touch: there each keeps its own edge, so the rows stay one
   press each. What is drawn and every place stay as they are. */

@media (pointer: coarse) {
  :is(a[href], button).smbk-list__row,
  .smbk-list__item[role="option"] {
    position: relative;
  }

  :is(a[href], button).smbk-list__row::after,
  .smbk-list__item[role="option"]::after {
    content: '';
    position: absolute;
    inset: min(0%, (100% - var(--smbk-size-target-comfortable)) / 2);
  }

  .smbk-list__item + .smbk-list__item > :is(a[href], button).smbk-list__row::after,
  .smbk-list__item[role="option"] + .smbk-list__item[role="option"]::after {
    inset-block-start: 0;
  }

  .smbk-list__item:has(+ .smbk-list__item) > :is(a[href], button).smbk-list__row::after,
  .smbk-list__item[role="option"]:has(+ .smbk-list__item[role="option"])::after {
    inset-block-end: 0;
  }
}

/* ---- high contrast (forced colors) ---- */
/* In Windows high-contrast mode the browser drops fills; this puts the current row and the chosen option
   back with the user's own colors. Nothing here applies outside that mode. */

@media (forced-colors: active) {
  .smbk-list__row[aria-current]:not([aria-current="false"]),
  .smbk-list__item[aria-selected="true"] > .smbk-list__row {
    forced-color-adjust: none;
    border-color: Highlight;
    background: Highlight;
    color: HighlightText;
  }

  .smbk-list__row[aria-current]:not([aria-current="false"]) :is(.smbk-list__item-title, .smbk-list__meta, .smbk-list__leading, .smbk-list__trailing),
  .smbk-list__item[aria-selected="true"] > .smbk-list__row :is(.smbk-list__item-title, .smbk-list__meta, .smbk-list__leading, .smbk-list__check) {
    color: HighlightText;
  }

  .smbk-list__row:focus-visible,
  .smbk-list__item[role="option"]:focus-visible > .smbk-list__row {
    outline-color: CanvasText;
  }

  .smbk-list__row:is(:disabled, [aria-disabled="true"]) :is(.smbk-list__item-title, .smbk-list__meta, .smbk-list__leading, .smbk-list__trailing),
  .smbk-list__item[aria-disabled="true"] > .smbk-list__row :is(.smbk-list__item-title, .smbk-list__meta, .smbk-list__leading, .smbk-list__check) {
    color: GrayText;
  }
}
```

## Tokens it reads

```json
{
  "space": {
    "0": {
      "$type": "dimension",
      "$value": "0px",
      "$description": "Raw space step, 0 px. Only for fixed distances inside a component; spacing that follows density uses space/inset, stack or inline."
    },
    "stack": {
      "md": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Gap between things stacked one above another, medium."
      },
      "sm": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Gap between things stacked one above another, small."
      }
    },
    "text": {
      "list": {
        "$type": "dimension",
        "$value": "{{space.stack.sm}}",
        "$description": "Between the items of a list (8)."
      },
      "label": {
        "$type": "dimension",
        "$value": "{{space.0}}",
        "$description": "Between a label or title and the line of supporting text right under it, inside a component. None: untrimmed line height already spaces the two lines."
      }
    },
    "inline": {
      "md": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Gap between things side by side, medium."
      },
      "lg": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Gap between things side by side, large."
      }
    },
    "inset": {
      "md": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Padding inside a component, medium."
      },
      "lg": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Padding inside a component, large."
      }
    }
  },
  "color": {
    "text": {
      "default": {
        "$type": "color",
        "$value": "{{color.grey.900}}",
        "$description": "Main text."
      },
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Secondary text: descriptions, metadata."
      },
      "disabled": {
        "$type": "color",
        "$value": "{{color.grey.500}}",
        "$description": "Text in unavailable controls."
      }
    },
    "icon": {
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Quiet icons."
      },
      "default": {
        "$type": "color",
        "$value": "{{color.grey.900}}",
        "$description": "Icons."
      },
      "disabled": {
        "$type": "color",
        "$value": "{{color.grey.400}}",
        "$description": "Icons in unavailable controls."
      }
    },
    "action": {
      "ghost": {
        "background-hover": {
          "$type": "color",
          "$value": "{{color.alpha.grey-800.15}}",
          "$description": "Ghost button: fill on hover."
        },
        "background-pressed": {
          "$type": "color",
          "$value": "{{color.action.outline.background-pressed}}",
          "$description": "Ghost button: fill while pressed, one step past hover; also the current page in pagination."
        }
      }
    },
    "border": {
      "focus": {
        "$type": "color",
        "$value": "{{color.red.600}}",
        "$description": "Keyboard focus ring on controls."
      },
      "rim": {
        "$type": "color",
        "$value": "{{color.alpha.white.90}}",
        "$description": "The light rim that makes a surface look lit: fields, chosen pills, cards."
      }
    },
    "surface": {
      "default": {
        "$type": "color",
        "$value": "{{color.grey.100}}",
        "$description": "Standard surface: cards, panels, the header."
      },
      "selected": {
        "$type": "color",
        "$value": "{{color.alpha.grey-800.10}}",
        "$description": "The current or chosen item, such as the current page in the sidebar. Translucent, so it shows by the same step on the default, raised, sunken and canvas surfaces."
      }
    }
  },
  "typography": {
    "label-lg": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "16px",
        "fontWeight": 500,
        "lineHeight": 1.4,
        "letterSpacing": "0px"
      },
      "$description": "Label LG: 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"
      }
    },
    "body-sm": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "14px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    },
    "body-md-medium": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "16px",
        "fontWeight": 500,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      },
      "$description": "Body MD one weight step bolder, for the current item, so the current item is not shown by colour alone."
    }
  },
  "size": {
    "20": {
      "$type": "dimension",
      "$value": "20px",
      "$description": "Raw size step, 20 px. Only for fixed sizes inside a component; semantic sizes (size/control, icon, mark) point here."
    },
    "control": {
      "md": {
        "$type": "dimension",
        "$value": "{{size.40}}",
        "$description": "Height of a medium control, where controls insert by default: 40 comfortable, 32 compact. Dashboard recipe: use compact density; 40 for the page’s one main action and for forms; 32 for toolbars and filters, the size used most; 28 for actions inside table rows and cards. On phones switch to comfortable density, or give small controls an invisible 44 px tap area (size/target/comfortable)."
      },
      "lg": {
        "$type": "dimension",
        "$value": "{{size.48}}",
        "$description": "Height of a large control: 48 comfortable, 40 compact. A page may use it for its main call to action; apps use md. Dashboard recipe: use compact density; 40 for the page’s one main action and for forms; 32 for toolbars and filters, the size used most; 28 for actions inside table rows and cards. On phones switch to comfortable density, or give small controls an invisible 44 px tap area (size/target/comfortable)."
      }
    },
    "icon": {
      "md": {
        "$type": "dimension",
        "$value": "{{size.20}}",
        "$description": "Icon box, medium."
      },
      "sm": {
        "$type": "dimension",
        "$value": "{{size.16}}",
        "$description": "Icon box, small."
      }
    },
    "target": {
      "comfortable": {
        "$type": "dimension",
        "$value": "{{size.44}}",
        "$description": "Comfortable touch target (44)."
      }
    }
  },
  "border-width": {
    "hairline": {
      "$type": "dimension",
      "$value": "{{border-width.1}}",
      "$description": "Thin lines: dividers and almost every edge."
    }
  },
  "radius": {
    "control": {
      "md": {
        "$type": "dimension",
        "$value": "{{radius.6}}",
        "$description": "Corners of controls, medium."
      }
    }
  },
  "focus": {
    "ring": {
      "width": {
        "$type": "dimension",
        "$value": "{{border-width.2}}",
        "$description": "Thickness of the focus ring."
      },
      "offset": {
        "$type": "dimension",
        "$value": "{{space.2}}",
        "$description": "Gap between a control and its focus ring."
      }
    }
  },
  "motion": {
    "feedback": {
      "duration": {
        "$type": "duration",
        "$value": "{{duration.150}}"
      },
      "easing": {
        "$type": "cubicBezier",
        "$value": "{{easing.expo-out}}"
      }
    }
  }
}
```

## Purpose

Rows of the same kind, one thing each: a checklist, steps to follow, a feed, a settings menu, a set of choices. Plain, bulleted and numbered lists are text. Navigation, action and selectable lists are rows with an icon, a title and a quiet line under it, and each row goes somewhere, does something or can be chosen. For the facts about one thing, a name beside each value, use the description list; for data with columns, the table.

## Anatomy

- The block, around everything: a nav for a navigation list, a div otherwise.
- The title part, which names the list. Optional, but a navigation or selectable list without one needs an accessible name of its own; the wrapper warns in development when it has none.
- The items part, the list itself: a ul, or an ol for a numbered list; a listbox for a selectable list.
- The item part, one li per row.
- In a text list, the marker part (a bullet, or a number the stylesheet counts when the marker is left empty) and the text part. A plain row holds its words with no marker.
- In the other lists, the row part inside each item: a link, a button, or a plain box whose end holds a button. Inside the row, in order: the start part (such as an avatar), the leading part (an icon), the body part holding the item title part and the quiet meta part, the trailing part (an icon), the check part (a selectable row's tick) and the end part (such as a badge or a button).
- The divider part, the divider component, ending each row but the last when the list has dividers.
- The empty part, the words shown in place of the rows when there are none.

## When to use

- A checklist or a set of points whose order does not matter: bulleted.
- Steps to follow in order: numbered.
- A short menu of pages or settings, with the current one marked: navigation.
- A feed or a list of recent things, each opened by pressing it, or each with a button of its own: action.
- A short set of choices shown all at once, one or several chosen: selectable.

## When not to use

- Names with their values, such as an account's details. Use the [description list](description-list.md).
- Data with several columns to compare or sort. Use the [table](table.md).
- A choice in a form with a few short options. Use [radios](radio.md) or [checkboxes](checkbox.md) in a [field group](field-group.md).
- A long set of choices that would push the page down. Use the [select](select.md).
- The site's main navigation. Use the [header](header.md), the [app bar](app-bar.md) or the [sidebar](sidebar.md).

## Behavior

Text rows sit a line of list spacing apart; a row that wraps keeps its lines under the text, not under the marker, and the numbers of a numbered list share one column. Rows that can be pressed touch each other, at least 40 tall at the small size and 48 at the medium size, which adds room above and below. Hovering one fills it lightly and pressing it fills it more. With the React wrapper, in a list whose rows can all be pressed, the light fill is one shape that slides from row to row with the mouse and lights the nearest row when the pointer is on a divider or past the last row, where a click presses that row; it fades in where the pointer enters and out as it leaves, lands without sliding when the reader's system asks for less motion, and steps aside while the keys move. A current or chosen row keeps its own fill over it; a touch screen shows no hover fill, and plain HTML keeps each row's own.

The current row of a navigation list sits on the default surface with a light rim, and its title is one weight bolder. Every title keeps the room of its bolder self, so nothing beside it moves as it turns bold (markup written by hand repeats the title's words in its `data-label` attribute). A chosen option takes the see-through selected fill, with a tick at its end; the tick is in every option and only shows when it is chosen, so a row never changes width. A tickless selectable list (`tickless`, the `smbk-list--tickless` class) draws no tick: its chosen row shows by the fill and a title one weight bolder, for rows that open as they are chosen. A disabled row dims its words and icons.

A link row can open its page in a new tab (`target="_blank"`, with the `rel` the page wants), for rows that lead to other sites, so the reader keeps their place in the list. Its name then ends with "(opens in a new tab)", heard by a screen reader and not seen; `newTabText` changes the words. Nothing changes in how it looks: show it with an arrow-out icon after the title (`trailingIcon="ArrowSquareOut"`).

A selectable list holds its own choice, or follows the one the page gives it and reports each change; given a name, it sends the chosen values with its form. Nothing else animates.

## Accessibility

### What it does for you

- A text list is a real list, so a screen reader says how many rows it has. Its role is written on the element, because some browsers stop calling a list a list once its bullets are gone.
- A list is named by its title. A navigation list is a navigation region named the same way.
- A plain, bulleted or numbered list takes no focus. Links and buttons inside it are Tab stops in reading order.
- A navigation row is a link, and the current one says so to a screen reader.
- An action row is a button, or a box holding one.
- A selectable list is a listbox and its rows are options, each saying whether it is chosen. The list is one Tab stop. A disabled option is skipped.
- A row is named by its title, and its quiet line describes it. A link row that opens a new tab ends its name with "(opens in a new tab)", so the change of tab is never a surprise.
- The marked rows never rely on color alone: the current row's title is bolder, and a chosen option shows a tick, or in a tickless list a bolder title.
- In a forced-color mode, the current row and a chosen option are drawn in the system's highlight colors.

### What you must do

- Give each list its own title, or an `aria-label`. On a page with several navigation lists, make the titles differ, so the regions can be told apart.
- Never put a button or link inside a row that is itself a link or a button. Put it in the end of a row that is not pressed, so each target does one thing.
- Give the new-tab words in the page's own language with `newTabText` when the page is not in English. In markup written by hand, give a row that opens a new tab both attributes and end its title with an element of the `smbk-visually-hidden` class holding a space and "(opens in a new tab)", marked `data-unseen`.

### How to test

- With the keyboard, Tab through a list of links or buttons: each is one stop, in reading order, with the focus ring showing.
- With a selectable list, Tab reaches it once. The arrow keys move between options, Home and End jump to the ends, typing the first letters of a title moves to it, and Space or Enter chooses.
- With a screen reader, a text list is announced with its number of rows, a navigation list as a navigation region by its title, the current row as current, and a selectable list as a listbox whose options say whether they are chosen.
- With text at 200% and the page 320 pixels wide, a long title wraps under its own text and rows stay whole.
- In a forced-color mode, the current row and a chosen option are still told from the rest.

### Keyboard

| Key | What it does |
|---|---|
| Tab | Moves focus to the next link or button in a row. A selectable list is one stop; a disabled option is skipped. |
| Enter | On a navigation row, follows its link. On an action row, presses its button. |
| Space | On an action row, presses its button. |
| Down Arrow or Up Arrow | In a selectable list, moves to the next or previous option. |
| Home or End | In a selectable list, moves to the first or last option. |
| Any character | In a selectable list, moves to the option whose title starts with the letters typed. |
| Space or Enter | In a selectable list, chooses the focused option. |

## Usage rules

### Do

- Number rows only when their order matters.
- Open a row in a new tab only when it leads to another site and the reader would want to come back to the list.
- Keep a row's title short and put the detail in its quiet line.
- Use dividers between rows that are pressed or chosen, where the rows touch.
- Make a selectable list tickless when choosing a row opens it, such as an inbox beside its reading pane, so the open row does not read as kept for later.

### Don't

- Put a button inside a row that is a link or a button.
- Rule off every row of a text list: its spacing already groups it.
- Use a list for names with their values.
