List
List — the structured row list: a settings list, a file list, an inbox.
Examples
-
Wi-Fi Connect automatically to known networks
-
Bluetooth
<ul class="list list-divided" role="list">
<li>
<div class="list-content">
<span class="list-title" id="wifi-label">Wi-Fi</span>
<span class="list-support">Connect automatically to known networks</span>
</div>
<div class="list-trailing">
<input class="switch" type="checkbox" role="switch"
aria-labelledby="wifi-label" checked />
</div>
</li>
<li>
<div class="list-content">
<span class="list-title" id="bt-label">Bluetooth</span>
</div>
<div class="list-trailing">
<input class="switch" type="checkbox" role="switch" aria-labelledby="bt-label" />
</div>
</li>
</ul>When the row holds its own control, the row itself is not interactive. A button inside a link is invalid HTML and a control inside a clickable row is a target inside a target.
<ul class="list list-divided" role="list">
<li>
<a class="list-action" href="#report">
<span class="list-content">
<span class="list-title">quarterly-report.pdf</span>
<span class="list-support">2.4 MB · edited yesterday</span>
</span>
<span class="list-trailing">Shared</span>
</a>
</li>
<li>
<a class="list-action" href="#notes">
<span class="list-content">
<span class="list-title">notes.md</span>
</span>
</a>
</li>
</ul>.list-action goes on the <a> and is the row: it carries the padding, the hover and the focus ring, so the whole width is the target. The <li> becomes a bare wrapper.
Tokens 20
Level 2, declared on .list itself. Set any of them on that selector to restyle this component without touching the skin.
| Token | Default |
|---|---|
| --list-action-current-color | var(--color-primary) |
| --list-action-hover-bg | var(--color-surface-sunken) |
| --list-action-radius | var(--radius-field) |
| --list-content-gap | var(--space-1) |
| --list-divider-color | var(--color-border-subtle) |
| --list-divider-width | var(--border-width) |
| --list-leading-color | var(--color-on-surface-muted) |
| --list-row-align | center |
| --list-support-color | var(--color-on-surface-muted) |
| --list-support-font-size | var(--font-size-xs) |
| --list-title-color | var(--color-on-surface) |
| --list-title-font-size | var(--font-size-sm) |
| --list-title-font-weight | var(--font-weight-medium) |
| --list-trailing-color | var(--color-on-surface-muted) |
| --list-trailing-font-size | var(--font-size-sm) |
| --list-trailing-gap | var(--space-2) |
| --list-row-gap | var(--space-3) |
| --list-row-min-size | 3rem |
| --list-row-padding-block | var(--space-3) |
| --list-row-padding-inline | var(--space-4) |
Variants and states
Variants
.list-divided.list-lg.list-sm
Inside it
.list-action.list-title.list-content.list-leading.list-spaced.list-support.list-trailing
State it reads
Read from the platform, never mirrored into a class that could disagree with it.
:disabled:focus:hover
Before you ship it
What you have to do 1 requirements
- Put
role="list"on the list. The reset strips markers from any<ul>or<ol>that carries a class, and in WebKit that also drops the list semantics: VoiceOver stops announcing "list, 8 items" and reads eight unrelated lines instead.role="list"restores it. This is not optional decoration — it is the one attribute without which the component is worse than a bare<ul>. Use<ol class="list" role="list">where the order is meaningful; the role is the same. Name a trailing control from the row. A switch or checkbox in.list-trailinghas no visible label of its own — the row's title is its label. Point at it:aria-labelledby="the-title-id", or wrap the title in a<label for>. Without this the control announces as "switch" and nothing else. Nothing in CSS can supply it. An icon in.list-leadingis decoration.aria-hidden="true"on the<svg>; the row's text is the name.
src/css/components/list.css · npx mostlycss add list