Role and semantics
Section titled “Role and semantics”ListItem.Listis a<ul>withrole="list"; this stops some browsers from turning the list into plain text when the list style is reset. Rows are<li>, and a screen reader announces “list, 3 items”. Give it anaria-labelwhen the page has more than one list.- Outside a list,
ListItemrenders a<div>; an<li>is only valid inside a<ul>. - With
render, the<li>wraps the link or button, so the list structure stays valid. The link name is the row text: title, subtitle and meta. - On an interactive row
selectedsetsaria-current="true". On a static row the selected state is visual only; do not show a static row as selected. - On an interactive row
disabledsetsaria-disabled="true"andtabindex="-1"; clicks are ignored. - Put navigation rows in
<nav aria-label>. Do not use a button row as a toggle;aria-pressedis not set.
Keyboard
Section titled “Keyboard”| Key | Behaviour |
|---|---|
| Tab | A static row takes no focus. An interactive row is one stop; in a static row the separate button in trailing is its own stop. |
| Enter | Opens a link row; activates a button row. |
| Space | Activates a button row. A link does not respond to Space. |
Focus is on the link or button given through render. The ring is 2 px wide and 2 px away, and shows for keyboard focus only. The selected bar is not a focus indicator. A disabled row takes no focus.
Screen reader output
Section titled “Screen reader output”| Example | Read |
|---|---|
| Link row | “Settings Account and appearance, link” |
| Selected link row | “Settings Account and appearance, link, current” |
| Static row | “List item Secondary text”, in the list “1 of 3” |
| Disabled link | “Notifications, link, unavailable” |
A link name can get long. When the title is enough, move the subtitle to meta or to a separate static row.
Contrast
Section titled “Contrast”Text is at least 4.5:1 in every mode; values are computed from the bigbang output.
| Pair | Light mode | Dark mode |
|---|---|---|
color-text-default / color-surface-default |
16.16:1 | 17.18:1 |
color-text-subtle / color-surface-default |
8.77:1 | 10.27:1 |
color-text-default / color-surface-selected |
15.05:1 | 13.98:1 |
color-text-subtle / color-surface-selected |
8.17:1 | 8.36:1 |
color-border-selected / color-surface-selected (bar) |
5.56:1 | 7.11:1 |
color-focus-ring / color-surface-selected |
5.56:1 | 7.11:1 |
color-text-disabled / color-surface-default (disabled, exempt) |
2.52:1 | 2.80:1 |
The divider (color-border-subtle, 1.22:1) is decorative: rows are also separated by space. Disabled text falls under the inactive-component exception of WCAG 1.4.3.
WCAG 2.2
Section titled “WCAG 2.2”| Criterion | How ListItem meets it | What the product team does |
|---|---|---|
| 1.3.1 Info and relationships | The list is ul/li with role="list"; the selected row has aria-current. |
Name several lists with aria-label. |
| 1.4.1 Use of colour | Selected is given by surface, bar and aria-current. |
Do not show a selected state with colour alone on a static row. |
| 1.4.3 Contrast (minimum) | Text is at least 8.17:1. | Do not change colours with className. |
| 1.4.11 Non-text contrast | The focus ring and the bar are at least 5.56:1. | - |
| 2.1.1 Keyboard | An interactive row is a link or a button; Tab, Enter and (on a button) Space work. | Do not make a row clickable with onClick; use render. |
| 2.4.4 Link purpose | The link name is the title. | Write a meaningful title. |
| 2.4.7 Focus visible | The ring is around the row. | Do not remove outline with className. |
| 2.5.8 Target size | An sm row is at least 8 + 20 + 8 = 36 px tall. |
Keep buttons in trailing at 24 px or more (Button sm is 32 px). |
| 4.1.2 Name, role, value | The link or button carries its own role; aria-current and aria-disabled give the state. |
Use a real a or button for render. |