Timeline — accessibility
Keyboard
- Group headings are
<summary>elements, so Tab reaches them and Enter or Space folds the section. There is no bespoke disclosure widget. - A row that opens something is a real
<button>insideli.il-li; a row that does not is a<div>and takes no tab stop — nothing promises an interaction that does not exist. - The task state inside
.tcard(.tl-state, the task list's glyph button) is its own control with its own name andaria-pressed; clicking it does not open the row. - The timeline has no shortcuts of its own, and no arrow-key cursor: it is a document to read, not a grid to drive.
ARIA
- The rail is
role="list"withrole="listitem"rows — the row itself is a button, and an element has only one role, hence the wrapper. - Every lead glyph carries a
label, so the event type is announced as text and is never colour alone. An unknown type falls back to its category name, never to nothing. - The byline is drawn twice (full name and initials, switched by
@container) and both visible variants arearia-hidden; the name reaches assistive technology through a plain.acrm-srcopy. That is deliberate: an earlier version relied onaria-labelon a bare<span>, which ARIA allows an implementation to ignore, and before that the author disappeared from the tree entirely in the narrow layout. Now the accessible tree does not depend on the container width and the name is never read twice. - A single task row and a hand-written entry compose an explicit
aria-label, because they carry their own controls; without it the state button's label bleeds into the row name and a screen reader announces a call to action that clicking the row does not perform. is-nowand.il-meta.nextare visual only.
Focus
- Summaries and row buttons take the foundation ring from
ds.css(outline: 2px solid var(--accent); outline-offset: 1px);timeline.cssdoes not switch it off. - Focus ring
--accent: 4.44:1 light, 3.83:1 dark. - Folding a section removes its rows from the tab order; focus stays on the summary, which is correct.
Contrast
Computed from packages/ui/src/tokens/tokens.css:
- Titles are ink on the canvas — 17.59:1 light, 14.53:1 dark.
- Byline, content and past times use the muted tone — 2.85:1 light, 3.63:1 dark: below AA, the deliberate cost of "hierarchy by colour alone" inherited from
item-list. - A future time is full ink, so the one thing the user must not miss is the one thing that is not muted.
- The today tint is
--surface-75on the canvas — 1.03:1. The block is marked by its tint alone, so for a user who cannot see it the section is identified only by its headingDnes— which is why the heading must always be there. - The axis line is a 1px muted rule and carries no information.
Known limits
- Secondary text sits below AA throughout, by inheritance from
item-list. - The today block is distinguished by a 1.03:1 tint; the heading carries the meaning.
- Nothing announces rows arriving from lazy loading or from the live stream.
is-now— the single nearest row on the axis — has no accessible marker at all.
Source: docs/timeline/timeline.a11y.md · GitLab