Getting started

Accessibility

Known outstanding accessibility issues in Moments that wait on a design or token decision.

Every story is audited with axe on each pull request (see the Accessibility tests workflow), in both the web-component and plain-HTML forms. Known violations are recorded in a11y/baseline.json, so CI fails only on new ones — the existing backlog stays visible and shrinkable without blocking every PR.

This page outlines the known, outstanding issues that are intentionally not changed in code yet because they are design/token decisions rather than markup bugs. Correctness bugs (missing input-label associations, invalid ARIA attributes) are fixed as they are found and are not listed here.


1. Colour contrast — pending a token / design decision

WCAG 2.1 AA (§1.4.3) requires a contrast ratio of 4.5:1 for normal-size text (3:1 for large text — ≥ 24px, or ≥ 18.66px bold). Two of the light secondary-text tokens fall below that when used for small text:

TokenValueOn whiteRatioVerdict (normal text)
--moments-semantic-colour-text-base-lightest → core-colour-neutral-500#989795#ffffff2.91:1Fails
--moments-semantic-colour-text-base-lighter → core-colour-neutral-400#bab8b5#ffffff1.97:1Fails

Two smaller cases also appear: a mid-grey at ~3.54:1 (#888888) and an accent blue on its tint (#0066ff on #ebf3ff) at 4.32:1, just under the threshold.

Where these show up (small-text usages)

  • Date Picker — weekday headers (Mo, Tu, …), text-base-lightest
  • Section Header — eyebrow
  • Rich Input fields (URL, Search, Password, Number, Money, Email, Colour, Phone) — visible label, help/details text, placeholder, required marker
  • Phone Field — dial-code prefix
  • Select — displayed value and placeholder
  • Drawer — label
  • Code / OTP Field — label
  • Avatar — initials

Why it is not changed here

These are core/semantic colour tokens. Changing them is a design decision that must go through Tokens Studio and stay aligned with Figma, so it is deliberately left to design rather than patched in component CSS. Options for design to weigh:

  1. Darken text-base-lightest / text-base-lighter until they reach 4.5:1 on the surfaces they sit on (this is the systemic fix — it clears the majority of the backlog at once).
  2. Restrict these tokens to large text only (≥ 24px, or ≥ 18.66px bold), where 3:1 is sufficient.
  3. Reassign the affected components to a darker existing text token.

2. Tabs — aria-controls across the shadow-DOM boundary

Each tab carries aria-controls="panel-<id>" pointing at the panel the consumer renders in the light DOM, and each panel labels itself from the tab via aria-labelledby="tab-<id>". Because the tab strip lives in the component's shadow root while the panels live in the light DOM, the IDREF cannot resolve across the boundary, so axe reports aria-valid-attr-value.

The attribute is intentional — the tab ↔ panel wiring is part of the component's public contract and is covered by an interaction test — so it is left in place pending a cross-root ARIA (or light-DOM) decision rather than removed.


3. Lists in popovers — role="listbox" without an accessible name

moments-list renders <ul role="listbox">, and a listbox must have an accessible name (axe aria-input-field-name). In the Popover Padding options demo the list is shown without one. Resolving this properly needs a small component decision — expose a naming API (e.g. an aria-label / label property applied to the internal <ul>), and review whether type="menu" lists should use role="menu" / role="menuitem" rather than listbox / option — so it is left documented rather than patched ad-hoc in the story.


Recently fixed

For reference, these were genuine correctness bugs (not design decisions) and have been corrected:

  • Input labels — the plain-HTML variants of the Rich Input field stories now associate each <label> with its <input> via for / id (axe label).
  • Select — removed the invalid aria-readonly attribute from the trigger <button> (axe aria-allowed-attr).
  • Switch — the toggle and keyboard story now provides its label as slotted content so the native control has an accessible name (axe label).

On this page