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:
| Token | Value | On white | Ratio | Verdict (normal text) |
|---|---|---|---|---|
--moments-semantic-colour-text-base-lightest → core-colour-neutral-500 | #989795 | #ffffff | 2.91:1 | Fails |
--moments-semantic-colour-text-base-lighter → core-colour-neutral-400 | #bab8b5 | #ffffff | 1.97:1 | Fails |
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:
- Darken
text-base-lightest/text-base-lighteruntil 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). - Restrict these tokens to large text only (≥ 24px, or ≥ 18.66px bold), where 3:1 is sufficient.
- 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>viafor/id(axelabel). - Select — removed the invalid
aria-readonlyattribute from the trigger<button>(axearia-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).