Loader — accessibility
Keyboard
- Not focusable and not interactive.
- Inside
<Button loading>the button stays focusable but is disabled for activation, so a second Enter cannot fire the action twice. Focus is not moved.
ARIA
- With
label:role="status"on the wrapper plus the label in.acrm-sr— a polite announcement, so it does not interrupt. - Without
label: no role at all, and the drawing isaria-hidden. That is the correct shape inside a button, which already has its own name. - Never leave a standalone loader unlabelled: a silent spinner is invisible to a screen reader, and the user is left with no signal that anything is happening.
role="status"is a live region — do not mount several labelled loaders at once in one view.
Focus
- Nothing focusable, so no ring of its own. In a loading button the button's own foundation ring still applies (
outline: 2px solid var(--accent); outline-offset: 1px).
Contrast
Computed from packages/ui/src/tokens/tokens.css:
- Default colour is
--fg-muted— 2.85:1 light, 3.63:1 dark against the canvas. Below the 3:1 non-text threshold in the light theme. - Inside a primary or danger button it becomes
--fg-on-accent(white) on the accent fill, which is well clear. - The
.linf-trackrail iscurrentColorat 22%, i.e. deliberately near-invisible; the moving segment is what carries the signal.
Known limits
- The animation runs continuously and does not honour
prefers-reduced-motion. - On the page ground the muted stroke is under 3:1 in light theme, so the loader is easy to miss.
- Nothing announces that the wait ended — the
statusregion goes away silently. - The size knob is not inheritable, so a "make everything here smaller" rule on an ancestor has no effect and fails quietly.
Source: docs/loading/loader.a11y.md · GitLab