- What is exhaustively tested: colour contrast
- What is unit-tested: specific ARIA behaviour
- CI: tested on every push, gating every release
- php artisan aura:doctor --a11y
- WCAG 2.2: what is measured, and what is not
- Form states
- Summary: what to trust, and what not to assume
Accessibility
This page states what is verified, how it is verified, and what is not. Aura UI does not claim a systematic keyboard-navigation or screen-reader audit -- nobody has run one. What follows is what has actually been measured and tested, so you can judge for yourself.
What is exhaustively tested: colour contrast
Every colour pair Aura's own components render as a solid background, badge, alert, border,
progress bar or toggle knob is checked against Contrast::ratio() -- a plain WCAG relative-
luminance calculation, not an estimate -- in both light and dark themes:
- Text on solid backgrounds (buttons, badges, alerts, avatar/progress gradients, ...): checked against the WCAG 2.1 AA threshold for normal text, 4.5:1.
- Non-text UI (borders, bars, toggle knobs, icon-only fills): checked against the WCAG 2.1 SC 1.4.11 threshold for UI components, 3:1.
This runs as a Pest dataset test in each package -- tests/Unit/Components/ColorContrastTest.php
in both bluestarsystem/aura-ui and bluestarsystem/aura-ui-pro -- with one row per rendered
colour pair, on every commit, in both themes. Counting the dataset entries directly: 111
pairs in the Free package, 34 in Pro -- 145 in total (measured 2026-08-09; it was 104/138 before
the dark solid-surface rows were added). The dataset is the contract: whenever a
component's Blade template changes shade, the corresponding row changes with it, so the list
never drifts out of sync with what actually renders.
This is real, exhaustive coverage of contrast. It is not a claim about keyboard navigation, focus order, or screen-reader behaviour -- see the next section for what is and is not covered there.
What is unit-tested: specific ARIA behaviour
tests/Unit/Components/AccessibilityTest.php (Free package) unit-tests the actual HTML a
handful of components render, for specific ARIA wiring:
<x-aura::input>,<x-aura::select>,<x-aura::textarea>,<x-aura::floating-input>:aria-invalidandaria-describedbylinked to the error/hint text.<x-aura::checkbox>,<x-aura::radio>:aria-describedbylinked to their description text.<x-aura::toasts>:role="region"andaria-live="polite".<x-aura::modal>,<x-aura::drawer>:role="dialog",aria-modal="true", and (modal)aria-labelledbylinked to the title.<x-aura::dropdown>:role="menu",aria-haspopup="menu",role="menuitem",role="separator".<x-aura::command-palette>:role="dialog"andaria-modal="true".
That is the complete list -- this test file does not cover every component, and it tests markup, not actual assistive-technology behaviour. No full keyboard-navigation or screen-reader audit of the library has been performed. Treat this as evidence that specific, named ARIA attributes are present and wired correctly on the components listed, nothing broader.
CI: tested on every push, gating every release
All three packages (aura-ui, aura-ui-pro, aura-filament) run their full test matrix --
including the contrast dataset above -- in GitHub Actions on every push. Each repository's
release.yml workflow (Auto Release & Satis Rebuild) declares the test job as a dependency
of the release job (needs: tests), so a commit that fails the suite is never tagged or
published to Satis/Packagist. "WCAG 2.1 AA verified in CI" is true today because of this gate --
it was not true before the fixes shipped alongside this page (a solid button previously
measured 2.54:1).
php artisan aura:doctor --a11y
aura:doctor is a static-analysis command that scans your own Blade files for problems
with Aura usage. Its checks:
| Check | Runs when | What it catches |
|---|---|---|
Icon-only <x-aura::button> |
always | A button whose only content is an icon, with no aria-label, title, or .sr-only text -- announces nothing to a screen reader. |
| Unlabelled form field | --a11y |
<x-aura::input>/select/textarea/checkbox/radio/etc. with no label, aria-label, aria-labelledby, and no matching native <label for> or wrapping <label>. |
Image without alt |
--a11y |
<img> with no alt attribute and no role="presentation"/role="none". |
Positive tabindex |
--a11y |
tabindex greater than 0, which overrides the natural focus order. |
| Generic link text | --a11y |
Links whose entire text is "click here", "read more", "learn more" or similar -- meaningless out of context to someone listing links. |
| Dialog without a title | --a11y |
<x-aura::modal>/<x-aura::drawer> with no title, aria-label, or <x-slot:title> -- opens with nothing announced. |
| Heading order | --a11y |
A heading level that skips ahead within the same file (e.g. h2 straight to h4), breaking the document outline. |
| Theme contrast | --a11y |
Your own @theme colour overrides in resources/css/app.css, checked against the same 4.5:1 threshold as the package's own dataset -- see below. |
Run it:
php artisan aura:doctor --a11y
It exits non-zero if it finds an error (not just a warning), so it gates CI:
- run: php artisan aura:doctor --a11y
Add --json for machine-readable output, --path= to scan specific directories, or
--skip-setup to skip the CSS/Tailwind setup checks and run only the Blade/theme checks.
The distinctive check: your own theme
No other check here inspects your code -- this one does. If you override one of Aura's four
solid-background shades (primary-600, success-700, danger-600, warning-500) in your
@theme block, aura:doctor --a11y computes the real contrast ratio of your override against
the text Aura's components render on top of it, and fails the build if it drops below 4.5:1.
A ratio within 0.05 of the threshold is reported as a borderline warning rather than a hard
pass/fail, since the check's oklch() conversion carries a small rounding error.
What this check does not cover: it parses only the top-level @theme block in
resources/css/app.css. Aura's own dark mode is not a dark: utility system --
dark-mode.css redefines the same --color-aura-* variables a second time inside a .dark
selector, inverting the surface scale and re-pointing only the -500 accents. Your @theme
override applies in both themes unless you also write your own .dark block, and this
check has no visibility into that second block at all. It is a light-mode (and shared-value)
check only -- a customer-authored .dark override is not separately checked.
WCAG 2.2: what is measured, and what is not
The library targets 2.1 AA. Of the six criteria 2.2 added at AA, three are things an application does, not a component library: 3.2.6 Consistent Help, 3.3.7 Redundant Entry and 3.3.8 Accessible Authentication. The other three touch components, and here is exactly where each stands.
| Criterion | Status |
|---|---|
| 2.5.8 Target Size (Minimum) | Measured. Every component in the playground is scanned in a browser: each target's size, and the distance to its nearest neighbour, checked against the 24px rule and its spacing exception. axe cannot decide this criterion — it reports it as incomplete — so it is measured directly. |
| 2.5.7 Dragging Movements | Covered by design, not by a machine. The three components that drag — kanban, resizable and scheduler — each have keyboard alternatives, verified by unit test. No automated check proves the pointer path and the keyboard path do the same thing. |
| 2.4.11 Focus Not Obscured | Mitigated, not verified. The site's fixed header is compensated by scroll-padding-top, and the cookie banner makes room under the content. The library itself ships no fixed chrome, so this is mostly your layout's question. We tried to test it automatically and could not tell a genuine failure from two overlapping cards often enough to trust the result, so the check was not shipped rather than shipped noisy. |
Form states
Error and disabled states are the ones nobody looks at, and for months nothing here rendered them: 179 components were scanned in their resting state only. They now have a page of their own, scanned in both themes on every run — which is how the error message turned out to be 3.76:1 on white, and how ten components turned out to be showing an error without announcing it.
Summary: what to trust, and what not to assume
- Trust: the 145-pair contrast dataset, run in CI on every commit, in both themes, against the actual rendered shades.
- Trust, narrowly: the ARIA attributes on the specific components listed above, verified by unit test.
- Trust, for your own app:
aura:doctor --a11y's static Blade checks and its light-mode@themecontrast check. - Do not assume: that the library has been audited with a screen reader or systematically
tested for keyboard navigation. It has not. Do not assume the theme check covers your
.darkoverrides -- it does not.