Accessibility
The shared accessibility contract every wdj component upholds.
Principles
Real semantics first
Buttons are <button>, links are <a>, switches are checkboxes with role="switch". Native behaviour is preserved before ARIA is added.
Keyboard complete
Every interactive element is reachable and operable with a keyboard, in a logical order, with Escape closing overlays.
Visible focus
A high-contrast focus ring (:focus-visible) is never removed. Focus returns to the trigger when a dialog closes.
Respects preferences
All motion is disabled under prefers-reduced-motion: reduce.
ARS + accessible name mapping
| Pattern | How wdj implements it |
|---|---|
| Dialog (modal) | Native <dialog> + showModal(): focus trap, inert background, Escape, focus return. |
| Disclosure / menu | <details>/<summary> with role="menu" items and outside-click close. |
| Autocomplete | role="combobox", aria-expanded, aria-controls, arrow-key navigation over role="option". |
| Switch | input[type=checkbox][role=switch] - space toggles, state is announced. |
| Tooltip | role="tooltip" linked via aria-describedby; shown on hover and focus. |
| Notifications | Toast region is aria-live="polite"; errors use role="alert". |
| Form errors | aria-invalid + aria-describedby pointing at text with role="alert". |
Contrast
HTMX & focus
When a fragment is swapped, focus is not moved unless the response marks an element with data-wdj-focus. This avoids stealing focus from the user during background updates while still letting validation summaries take focus when needed.