Skip to the page
TACTILE UI

Kit part · Lists

List

Rows in a rack: a mark, a title, a second line and a fact or a key at the end. Chosen rows sink into a recess with their check key latched; in a running order, a row lifts out of the rack on its skirt and slides past the others, from the keyboard or by its grip.

Element
<ul> · <ol>
Needs
kit.css + kit.mjs
Rules
list.rules.md · 5
Finishes
Aluminium · Graphite · Porcelain · Black
Volumes
Tactile · Quiet
On this page
  1. Variants
  2. Every state, at once
  3. API
  4. Markup
  5. Accessibility
  6. How it’s built
  7. Rules
  8. Related parts
01

Variants

  • AOAda OkaforStudio A
  • TRTom ReyesRoom 2
People · a key at the end
  • api.lode.exampleUp · 182 ms99.98%
  • cdn.lode.exampleDown since 14:0297.10%
Status · lamps with words
Links · the whole row goes
02

Every state, at once

Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.

Aluminium

PartRestHoverFocusChosen · lifted
Selectable
  • Take 3Room mic3:42
  • Take 3Room mic3:42
  • Take 3Room mic3:42
  • Take 3Room mic3:42
Reorder
  1. IntroPiano1:12
  1. IntroPiano1:12
  1. IntroPiano1:12
  1. IntroPiano1:12

Graphite

PartRestHoverFocusChosen · lifted
Selectable
  • Take 3Room mic3:42
  • Take 3Room mic3:42
  • Take 3Room mic3:42
  • Take 3Room mic3:42
Reorder
  1. IntroPiano1:12
  1. IntroPiano1:12
  1. IntroPiano1:12
  1. IntroPiano1:12
03

API

NameKindValuesWhat it does
.tui-listclasson <ul> · <ol>The rack. Plain rows need no script.
.tui-list__rowclasson <li> · <a>A row: lead, text, then a meta and/or one key. An <a> row goes somewhere as a whole.
.tui-list__leadclassavatar · lamp · icon · check · numberThe mark at the start, 24px wide at least.
.tui-list__title · __subclassone line eachWhat the row is, and one fact more.
.tui-list__metaclassa short factRight-aligned, tabular: a time, a size, a percentage.
role="listbox" · tabindex="0"attributeon the listSelectable rows (role="option"), one tab stop. aria-multiselectable="true" to choose several.
aria-selectedattribute"true" · "false"Chosen: the row sinks into a recess and its check key latches.
.tui-list__checkclassin the leadThe check key of a selectable row.
changeeventon the listboxThe choice changed.
.tui-list--reorderclasson <ol>A running order: rows numbered by position, a grip on each.
.tui-list__numclassin the leadPrints the row’s position, “01”, from a CSS counter.
.tui-list__gripclassbutton, aria-label "Move …"Knurled; lifts its row. aria-describedby the instructions.
tui-list-reordereventdetail { from, to }A row was dropped somewhere new.
Up · Down · Home · EndkeyboardlistboxMove the active row.
Space · Enter · Ctrl+AkeyboardlistboxChoose (toggle when multiple) · choose all.
Space · arrows · EscapekeyboardgripLift · move · put back. Space again drops.
04

Markup

<!-- Plain rows. -->
<ul class="tui-list">
  <li class="tui-list__row">
    <span class="tui-list__lead"><span class="tui-avatar" role="img" aria-label="Ada Okafor">…</span></span>
    <span class="tui-list__text"><span class="tui-list__title">Mix review</span><span class="tui-list__sub">Ada Okafor · Studio A</span></span>
    <span class="tui-list__meta">14:20</span>
    <button class="tui-key tui-key--sm">Join</button>
  </li>
</ul>

<!-- Selectable: one tab stop, arrows and Space. -->
<ul class="tui-list" role="listbox" aria-label="Takes" aria-multiselectable="true" tabindex="0">
  <li class="tui-list__row" role="option" aria-selected="true">
    <span class="tui-list__lead"><span class="tui-list__check" aria-hidden="true"></span></span>
    <span class="tui-list__text"><span class="tui-list__title">Take 3</span><span class="tui-list__sub">Vocal, room mic</span></span>
    <span class="tui-list__meta">3:42</span>
  </li>
</ul>

<!-- Reorderable: a grip per row; the numbers follow the order. -->
<p id="list-help" hidden>Space lifts the row. Arrow keys move it. Space drops it; Escape puts it back.</p>
<ol class="tui-list tui-list--reorder" aria-label="Running order">
  <li class="tui-list__row">
    <span class="tui-list__lead tui-list__num" aria-hidden="true"></span>
    <span class="tui-list__text"><span class="tui-list__title">Intro</span><span class="tui-list__sub">Piano, room mics</span></span>
    <span class="tui-list__meta">1:12</span>
    <button type="button" class="tui-list__grip" aria-label="Move Intro" aria-pressed="false" aria-describedby="list-help"></button>
  </li>
</ol>
import '/kit/kit.mjs';   // wires every selectable and reorderable list, now and later

document.querySelector('[aria-label="Takes"]').addEventListener('change', (e) => keep(chosen(e.target)));
document.querySelector('[aria-label="Running order"]').addEventListener('tui-list-reorder', (e) => {
  save(e.detail.from, e.detail.to);        // indexes, 0-based
});

Needs kit.css and kit.mjs. How to install the kit.

05

Accessibility

  • A plain list is a list: rows are read with their title, second line and fact, in order.
  • Selectable lists follow the listbox pattern: one tab stop, aria-activedescendant for the active row, aria-selected for chosen ones. The check key and the recess show it twice over.
  • Reordering follows the grab-and-move model: the grip is a toggle (aria-pressed) named “Move Intro”; every lift, move, drop and cancel is announced in a live region with the new position.
  • Dragging is a second way to reorder, never the only one.
06

How it’s built

Plain CSS grid rows with inset hairlines; a delegated module adds the two behaviours, so lists added later work without a call. Moving a row by keyboard moves its neighbour instead, so the focused grip never leaves the DOM and never loses the focus. The numbers are a CSS counter, so they follow the order with no script. A lifted row takes the key’s skirt and a longer shadow and rises 2px; when it drops, it settles back into the rack over 120ms.

07

Rules

list.rules.mdIn the brain

Two lines and one key per row at most; moving a row is always possible from the keyboard, and said out loud.

5 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.

08
Reel 342:10
Mixed0:30

Table

Rows of data with printed column labels, figures right-aligned in columns, and status in lamps with words.

AOTRMH+4

Avatar

A person’s mark, struck like a coin: initials stamped into the field, or a photo set under the rim, with a status lamp mounted at five o’clock. Stacks into a group with a count.

  • parts
  • calendar.mjs
  • carousel.mjs

File tree

Folders and files as a real tree, printed like a wiring diagram: traces with an elbow into every item, chevrons that turn, and the chosen file set in a recess with its lamp lit. Written as a plain nested list.

Docs
Finish
Volume