Skip to content

Accessibility ​

thl-ui targets WCAG 2.1 AA. Every page of the three playgrounds (75 routes × React / Vue / Angular, in light and dark mode) is audited with axe-core, and none has a serious or critical violation.

sh
node scripts/a11y.mjs http://localhost:5181            # React, fails on serious/critical
node scripts/a11y.mjs http://localhost:5182 --dark     # Vue, dark mode
node scripts/a11y.mjs http://localhost:5183 --min moderate --json report.json

What components do for you ​

AreaBehavior
ColorEvery text/background pair is ≥ 4.5:1 (see accessible color pairs); bright fills get dark text
FormsField wires <label for>, aria-describedby (help / error) and aria-invalid to whatever control you put inside, including custom widgets via aria-labelledby. Errors use role="alert" and are translated
Checkbox / SwitchNative inputs, so they work with keyboard and forms. The label text is the accessible name; ariaLabel is available for icon-only use
SelectCombobox pattern: role=combobox + listbox, aria-activedescendant, arrow keys / Enter / Esc, type-ahead search
Date / time pickersTrigger is a combobox with aria-haspopup="dialog" and aria-expanded; the calendar grid has full-date aria-labels
Sliderrole=slider with value / min / max / valuetext; arrow keys, Home / End, PageUp / PageDown; range thumbs are named "Minimum" / "Maximum"
TabsRoving tabindex, arrow keys skip disabled tabs, Home / End, aria-controls / aria-labelledby
Modal / Draweraria-modal, focus trap, focus restore, Esc, scroll lock; a closed drawer is inert
Dropdown / Popoveraria-haspopup / aria-expanded / aria-controls on your trigger element, not a wrapper; Esc returns focus
TooltipShows on hover and focus, linked with aria-describedby
Progressrole=progressbar with value and a localized name
LayoutSkip link, header / nav / main / footer landmarks, aria-current="page" in the side menu, h1 page titles
Motion & RTLLogical CSS properties, so dir="rtl" mirrors the layout; :focus-visible rings use the theme color

Your responsibilities ​

  • Give icon-only buttons an aria-label: <ThlButton circle icon="heart" aria-label="Like" />.
  • Put controls inside Field with a label, or give them an aria-label.
  • Scrollable regions you build yourself need tabindex="0" and a label.
  • Custom colors: keep text ≥ 4.5:1. Override both --thl-color-x and --thl-on-x / --thl-color-x-text together.