Accessibility
tekivex-ui targets WCAG 2.1 AA, with WAI-ARIA Authoring Practices (APG)
patterns implemented throughout. AAA is an aspiration we work toward, not a
blanket guarantee — this page explains exactly what that means, what we found
when we audited ourselves, and what we changed.
The audit (July 2026)
Section titled “The audit (July 2026)”We ran an internal, adversarially-verified audit of every interactive component against its WAI-ARIA APG pattern: five independent reviewers proposed findings, and each finding was then re-verified against the source with a default verdict of refuted — only confirmed defects were kept.
The result was 35 confirmed violations (4 high, 26 medium, 5 low). We
published the full findings in
docs/A11Y-AUDIT.md
and walked back the library’s earlier unqualified “WCAG 2.1 AAA” claim, because
it wasn’t true. As of v3.32.0, all 35 findings are fixed — nothing
deferred — each with regression tests.
What changed, release by release
Section titled “What changed, release by release”v3.29.0 — the 4 HIGH findings (keyboard-only users were blocked)
Section titled “v3.29.0 — the 4 HIGH findings (keyboard-only users were blocked)”| Component | What was broken | What changed |
|---|---|---|
TkxDropdown | A non-interactive trigger (icon/text) made the menu impossible to open by keyboard (WCAG 2.1.1) | The trigger wrapper is promoted to a focusable menu button (Enter / Space / ArrowDown) — but only when the trigger isn’t already interactive, so passing a real <button> never produces a duplicate control |
TkxMenu | Arrow keys moved only a visual highlight; screen readers announced nothing | Every item has a stable id and the menu panel exposes aria-activedescendant, so AT announces each traversed item |
TkxCascader | The option tree was completely mouse-only | Tree items are focusable; Enter/Space selects, ArrowRight drills in, ArrowLeft steps back |
TkxCarousel | Auto-advancing slides had no keyboard-reachable pause (hover-only) — WCAG 2.2.2 Pause, Stop, Hide | A persistent, keyboard-accessible Play/Pause toggle renders whenever autoPlay is on |
v3.30.0 — all 26 MEDIUM findings, across 20 components
Section titled “v3.30.0 — all 26 MEDIUM findings, across 20 components”Dialog focus management
TkxCommandPaletteandTkxColorPickernow trap Tab inside their dialogs and restore focus to the opener on every close path — Escape, selecting an item, backdrop or outside click.TkxDrawergained an accessible-name fallback: a newariaLabelprop, with a"Drawer"default, so an untitled drawer is never a nameless dialog.
Keyboard-operable custom controls
TkxColorPicker’s saturation/brightness area, hue track, and alpha track — previously mouse-only<div>s — are real ARIA sliders:role="slider",aria-valuemin/max/now/valuetext, Arrow keys (Shift+Arrow and PageUp/PageDown step by 10, Home/End jump), and a visible focus ring. The 2D area maps ArrowLeft/Right to saturation and ArrowUp/Down to brightness.TkxStepper’s clickable steps andTkxPopover’s non-interactive triggers are focusable and Enter/Space-operable (real<button>triggers stay untouched — no duplicate controls).TkxSpeedDialreturns focus to the FAB when closed from the keyboard, and itsaria-controlsnow points at the real, visiblerole="menu"(the hidden duplicate menu is gone).
Combobox contracts
- Searchable
TkxSelectmoves the full combobox contract (aria-expanded,aria-controls,aria-activedescendant,aria-autocomplete) onto the search input that actually holds focus — previously it sat on a button the user had already left. Multi-select tag ✕ and Clear-all are real sibling<button>s (no invalid interactive-in-interactive nesting), and Backspace on the trigger removes the last tag. TkxMentionsexposes the active suggestion viaaria-activedescendant.TkxDatePickerwiresaria-controlsto its dialog and ships a full roving-tabindex calendar keyboard model: focus enters the grid on open (or ArrowDown from the input), Arrow keys move the focused day — including across month boundaries — Enter/Space selects, Escape returns focus to the input.TkxRatingexposes arrow-key changes viaaria-activedescendanton the radio group.
Correct semantics
TkxDropdownselectable items dropped the invalidaria-selected(not a supported state onmenuitem) for the APG-correctmenuitemradio/menuitemcheckbox+aria-checked.TkxCarouselhidden and cloned slides areinert— keyboard focus can no longer land on off-screen slides — and the thumbnail strip dropped its faketablist/tabroles for a labeled group of buttons witharia-current.TkxAccordioncollapsed panels leave the accessibility tree (aria-hidden+inertonce the collapse animation ends), so content behind a closed disclosure is neither read nor Tab-reachable.TkxTag’s remove button is a sibling of the clickable body, not a descendant — two independent tab stops, no nested interactive elements.
Widget plumbing
TkxTabsderives its tab count from the actual tabs — arrow navigation no longer silently no-ops unless a consumer hand-passedtabCount— and arrow keys, Home, and End all skip disabled tabs.TkxSlidersetsaria-valuetextfrom yourformatValue, so AT announces “$50” rather than “50”.TkxTablesetsaria-rowcountin virtual mode so windowedaria-rowindexvalues have a declared total.TkxDataGridgained its keyboard model (completed in v3.32.0, below).
v3.31.0 — the 5 LOW findings + localized a11y labels
Section titled “v3.31.0 — the 5 LOW findings + localized a11y labels”TkxCommandPalette’s search combobox has a real accessible name (was placeholder-only), localizable via the newsearchCommandslocale key.TkxAccordionwraps each trigger inrole="heading"with a newheadingLevelprop (default 3) for screen-reader heading navigation, and the redundantrole="button"on the native button was removed.TkxNumberInputis a properrole="spinbutton"— itsaria-valuemin/max/nowpreviously sat on a textbox role where AT ignores them entirely. Addsaria-valuetextfor formatted values andaria-readonly.TkxSlider’s visible label now actually names the thumb viaaria-labelledby(alabel[for]can’t activate arole="slider"div, so the old association was dangling). Range thumbs keep distinguishable “start”/“end” names.TkxFieldno longer emits danglingaria-describedbyidrefs (hint id while an error is shown; error id whenisInvalidhas no error string).
Also in v3.31.0: the seven a11y label keys — selectNoOptions,
dropdownMenu, commandPalette, dataGrid, pagination, dismiss,
searchCommands — got native translations in all 44 locales, using
platform-conventional terminology per language.
v3.32.0 — the last two deferrals, closed
Section titled “v3.32.0 — the last two deferrals, closed”TkxDataGrid— the complete APG Grid keyboard model:- True per-cell roving tabindex: exactly one cell holds
tabIndex=0; Tab enters the grid directly on the active cell and Tab leaves it (the container is no longer a tab stop). The active cell survives virtual-window unmounts via remembered coordinates. - PageUp/PageDown move by a viewport-sized page of rows.
- Virtualization-aware navigation: Arrow/Page/Ctrl+Home/Ctrl+End work in absolute row space — moving past the rendered window scrolls the virtual viewport and focuses the target once it renders. Ctrl+End reaches the true last row even when it isn’t rendered.
- Plus, from v3.30.0: four-direction arrows, Home/End + Ctrl variants,
treegrid ArrowRight/ArrowLeft expand/collapse,
aria-colcount, and editors/checkboxes/filters are never hijacked.
- True per-cell roving tabindex: exactly one cell holds
TkxCascader— full tree-structure semantics on its flat-column DOM:aria-level/aria-setsize/aria-posinseton every treeitem,aria-expandedon parents tracking the drill-in path, andaria-ownsfrom each expanded parent to its child column’srole="group"(guarded against dangling idrefs).
One deliberate non-change
Section titled “One deliberate non-change”TkxDatePicker does not use role="grid" markup for its calendar: the
DOM is a flat 42-button CSS grid, and role="grid" without real row structure
would be invalid ARIA. The audit listed it as optional; the calendar is
fully keyboard-operable and each day button is individually labeled, which is
the part that matters to users.
How it’s kept honest
Section titled “How it’s kept honest”- Every fix has regression tests — the suite grew from 2,058 to 2,168
tests across the four releases (
tests/a11y-high-fixes.test.tsx,tests/a11y-low-fixes.test.tsx, and per-component suites). - The audit document preserves the original violation text of all 35 findings alongside what shipped, so the fixes are independently checkable.
- This was an internal audit. It is thorough, but it is not a substitute for testing with real assistive-technology users, and we don’t claim third-party certification. External audit-firm outreach is in progress.
New props from this work
Section titled “New props from this work”| Component | Prop | Default | Purpose |
|---|---|---|---|
TkxDrawer | ariaLabel?: string | "Drawer" | Accessible name when no title is set |
TkxAccordion | headingLevel?: number | 3 | ARIA heading level wrapping each item trigger |
Both are additive — no breaking changes were introduced by any of the four releases.