accessibility — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited accessibility (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Accessible UI works for people using a keyboard, a screen reader, magnification, voice control, reduced motion, or touch — not just a mouse and perfect vision. Target WCAG 2.2 Level AA unless the product has a stricter bar. Accessibility is a requirement built into every change, not a polish step at the end; retrofitting costs far more and rarely catches everything.
This skill is the full process. Run the accessibility checklist alongside it for a quick pass. For visual polish and non-a11y UI states, pair with [[ui-craft]]. For proving behavior in a running app, finish with [[browser-checks]].
Skip as the primary skill for changes with no user-facing surface (pure backend, tooling). Still apply if those changes affect auth flows, emails, or PDFs users receive.
Work in order. Later steps assume earlier foundations are solid.
Structure is free accessibility — get it right before styling.
h1 → h2 → h3, no skipped levels).<header>, <nav>, <main>, <footer>. One <main> per page.#main.<html> (and lang on inline passages in another language).ul/ol), tables (table/th/td), and forms (form, fieldset, legend)— not styled div stacks.
<a href="#"> or <div onClick> for actions.<!-- Prefer -->
<button type="button" aria-expanded="false">Menu</button>
<!-- Not -->
<div role="button" tabindex="0" onclick="...">Menu</div>The first rule of ARIA: if a native HTML element already gives the semantics and behavior you need, use it. ARIA patches gaps; it does not fix bad markup.
Reach for ARIA when:
aria-live, role="status", role="alert").Never:
role="button" on a link).role="navigation" on <nav> is redundant).When building custom widgets, follow the [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) patterns for keyboard behavior, roles, and states — don't invent interaction models.
If you can't complete the flow with keyboard alone, it's not done.
outline: none without a replacement that meets 3:1 contrast.tabindex values.programmatically focusable but not tabbable (error summaries, modal containers on open).
Expected keys (implement or preserve natively):
| Pattern | Keys |
|---|---|
| Button, link | Enter, Space (buttons); Enter (links) |
| Modal / menu | Esc closes; focus trapped inside modal |
| Tabs | Arrow keys move; Home/End jump |
| Combobox | Arrow keys open/navigate; type to filter |
| Radio group | Arrow keys move selection |
For SPAs: move focus to `h1` or main landmark on route change so screen-reader users know the view changed. Don't rely on visual swap alone.
Assistive tech reads the accessibility tree, not your CSS.
<label for> or wrapping label. Placeholderis not a label.
aria-label, or aria-labelledby.Decorative icons: aria-hidden="true".
alt text, or alt="" if purely decorative. Complex charts need a textsummary or long description.
aria-expanded, aria-selected, aria-checked,aria-current, disabled, aria-disabled, aria-invalid.
<fieldset>/<legend> or role="group" + aria-labelledby.aria-describedby (hint text, character count, format example).<input id="email" aria-describedby="email-hint email-error" aria-invalid="true" />
<p id="email-hint">We'll never share this.</p>
<p id="email-error" role="alert">Enter a valid email address.</p>Visual updates that happen without navigation must be communicated.
role="status" or aria-live="polite". Don't steal focus fornon-critical confirmations.
role="alert" or aria-live="assertive". Move focus to theerror summary or first invalid field on submit.
aria-busy="true" on the updating region; use aria-live="polite" for completionwhen content replaces a skeleton.
control — don't rely on scroll position alone.
Avoid over-announcement: one live region per logical update, not on every keystroke.
components plus their states (default, hover, focus, disabled).
(focus ring, progress) without parallax, auto-play, or vestibular triggers.
user-scalable=no or maximum-scale=1.For layout and responsive behavior beyond a11y, see [[ui-craft]].
Forms are where most real-world a11y failures show up.
required or aria-required="true".aria-describedby; set aria-invalid="true" on the field.role="alert" or tabindex="-1")listing linked errors, then let the user jump to each field.
These fail often — implement to APG spec or use a battle-tested library.
| Pattern | Must-haves |
|---|---|
| Modal / dialog | Focus trap, Esc closes, aria-modal="true", labelled by title, restore focus to trigger |
| Disclosure / accordion | aria-expanded on trigger; panel content in DOM or moved focus when opened |
| Tabs | role="tablist", roving tabindex, aria-selected, keyboard arrows |
| Combobox / autocomplete | Listbox pairing, active descendant or roving focus, announce result count |
| Data table | <th scope>, captions or aria-label, sort state exposed |
| Carousel | Pause control, no auto-advance (or respect reduced motion), slides labelled |
| Drag and drop | Keyboard alternative to reorder/select — never the only path |
If you use a component library, verify it implements these — many "accessible" components don't.
Automated tools catch roughly 30–50% of issues. "Passes axe" is necessary, not sufficient.
Automated (run in CI when possible):
Manual — do every time:
Any traps? Does Esc close overlays?
landmarks. Do controls announce name, role, and state? Are live regions working?
Document known gaps if you ship with them — don't pretend untested areas are fine.
incomplete.
(sun glare, broken arm, noisy room) affect everyone. Legal risk exists in many jurisdictions.
traps, and whether announcements actually help.
focus in your feature.
<div>/<span> instead of <button> or <a>outline: none without replacementtabindex values; tab order that jumps illogically; keyboard trapsalt textaria-hidden on focusable or interactive contenth1, ordered headings, skip link, and correct langalt~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.