Mode d’emploi
- Copiez la commande et examinez le paquet avant de l’installer dans votre environnement agent.
- Consultez le document original pour les outils, identifiants et environnements requis.
- Décrivez les entrées et le livrable souhaité ; comparez la sortie aux sources.
npx skills add https://github.com/paragonhq/stealth-skills --skill a11yAvant de commencer
- L’installation n’inclut ni abonnement au modèle, ni quota API, ni accès aux fichiers.
- Étoiles et installations reflètent l’activité, pas un test indépendant de qualité ou de sécurité.
Rendu depuis le dépôt source en conservant titres, exemples, code, tableaux, liens et images.
a11y
Accessible interfaces are not a separate thing from good interfaces. Almost every rule here also makes the interface better for someone with a mouse and perfect vision. Treat it as craft, not compliance.
HTML first
The element carries the behaviour. Use it:
| Want | Use | Not |
|---|---|---|
| Something that does an action | <button type="button"> | <div onClick> |
| Something that navigates | <a href> | <button> that calls router.push |
| A list | <ul>/<ol> with <li> | divs |
| A form | <form> with <button type="submit"> | onClick on a button outside a form |
| A checkbox, radio, switch | <input type="checkbox">, role="switch" on a button | custom divs |
| A section with a heading | <section aria-labelledby> | a div with a bold paragraph |
| Emphasis | <strong>, <em> | <b>, <i> or a span with font-weight |
| A dialog | <dialog> or a primitive with role="dialog" | a fixed div |
A native element gets keyboard behaviour, focus, a role and a name for free. Every custom replacement has to earn all four back.
Names
Every control has an accessible name. In order of preference:
- Visible text inside it (
<button>Save changes</button>). - A visible
<label for>(inputs). aria-labelledbypointing at visible text.aria-label, only for icon-only controls, and it says what the control does, not what the icon is ("Copy link", not "Clipboard icon").
Images: alt that says what the image conveys, or alt="" if decorative. Icons inside labelled controls are aria-hidden.
Names are unique in context. Five "Edit" buttons in a list need "Edit Q3 report", either visibly or via aria-label.
Keyboard
- Everything interactive is reachable with Tab in visual order.
tabindexis0or-1, never positive. - Custom widgets follow the ARIA pattern for their role: arrows within a menu, tabs, listbox, radio group; Tab leaves the widget.
- Escape closes the topmost layer only.
- Focus moves into a dialog when it opens, is trapped while open, and returns to the trigger when it closes.
- Content behind a modal is
inert. - A skip link is the first focusable element and is visible on focus.
- Single-key shortcuts do not fire while focus is in a text field; modifier shortcuts do not clash with the browser's.
- Roving tabindex or
aria-activedescendantfor composite widgets, so one Tab stop per widget.
Focus visible
:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }
:focus:not(:focus-visible) { outline: none; }- Visible on every surface colour. Test on the darkest and the lightest.
- Matches the element's radius.
- Never
outline: nonewithout a replacement of at least 3:1 contrast against both the element and its background. - Rows and cards that are links show focus on the whole row, not on the text.
ARIA, sparingly
The first rule of ARIA: do not use ARIA if HTML does it. Then:
aria-expandedon the trigger of anything that expands.aria-controlspointing at it.aria-current="page"on the active nav link;aria-current="location"for in-page nav.aria-pressedfor toggle buttons;aria-checkedfor switches and checkboxes;aria-selectedfor tabs and options.aria-busyon regions that are loading;aria-disabledwhen a disabled control must stay focusable.aria-describedbyfrom an input to its error and hint.aria-live="polite"on a region that announces toasts and status.assertiveonly for errors that block.aria-hidden="true"on decorative icons, on visual duplicates (a word split into spans for animation gets anaria-labelon the parent), and on the backdrop.- Never
role="button"on a div when a button would do. Neveraria-labelon adivwith no role.
Forms
- Every field has a visible label. Placeholders are hints.
- Required fields are marked, and the mark is explained once.
- Errors: appear after blur or submit, sit under the field, are linked with
aria-describedby, and the first invalid field receives focus on submit. - Error text says what is wrong and how to fix it, in words; colour and an icon accompany it, never replace it.
- Groups of radios and checkboxes are in a
<fieldset>with a<legend>. autocompleteset so browsers and assistive tech can fill them.- Submit works with Enter from any field.
Colour and contrast
- Text 4.5:1; large text and UI boundaries 3:1; in both themes.
- Colour is never the only signal: status has an icon or word, links are underlined in running text, charts have patterns or labels.
- Placeholders and disabled text may sit below 4.5:1 only if nothing depends on reading them.
- Focus rings and selection colours checked against every surface.
prefers-contrast: morebumps borders to--fg-2and removes translucency.forced-colors: active(Windows High Contrast): borders on controls that relied on background,outlinefor focus, nobackground-imagefor meaning.
Motion
prefers-reduced-motion: reduce: transitions and animations to near zero, smooth-scroll off, parallax off, autoplaying video paused, but nothing hidden. Elements that would have animated in are simply present.- Nothing flashes more than three times a second.
- Autoplaying motion longer than five seconds has a pause control.
Structure
- One
h1per page; headings in order without skipping levels; a heading for every region a screen-reader user would want to jump to. - Landmarks: one
main,header,footer,nav(labelled if there are several),aside,search. - Lists are lists. A row-based UI is a
<ul>witharia-label. - Tables are
<table>with<th scope>, or a grid with proper roles. Not divs. langon the document and on any passage in another language.- Reading order in the DOM matches visual order. CSS
orderandflex-direction: row-reversebreak it.
Size and zoom
- Targets 24px minimum by WCAG, 44px in practice on touch.
- The page works at 200% browser zoom and at 320px width with no horizontal scroll and no lost content.
- Text spacing can be increased (line-height 1.5, letter-spacing 0.12em, word-spacing 0.16em) without clipping.
- Never disable pinch zoom.
Timing
- Toasts stay at least 5 seconds, longer with an action, and pause on hover and focus.
- Session timeouts warn with time to extend.
- Nothing requires a timed response to use.
Five-minute test
- Unplug the mouse. Tab through the screen. Can you see where you are? Can you do everything?
- Turn on VoiceOver (⌘F5) or NVDA. Navigate by headings (VO+⌘+H). Do the headings describe the page? Tab to controls: does each say its role, name and state?
- Open a dialog. Is focus inside? Does Escape close it and put focus back?
- Zoom to 200%. Does it still work?
- Turn on reduced motion. Is anything missing?
- Run axe. Fix every "serious" and "critical". Read the "moderate" ones; most are real.
Marks of inaccessible UI
| Sign | Fix |
|---|---|
div onClick | button |
outline: none | :focus-visible ring |
| Icon button with no name | aria-label |
| Placeholder as label | visible label |
| Error in red text only | icon + text + aria-describedby |
| Modal without focus trap | dialog primitive, inert behind |
| Custom select | native <select> or a listbox primitive |
| Hover-only affordance | visible always, or on focus too |
| Motion that hides content under reduced motion | content visible, motion removed |
| Colour-only status | icon or word |
From the catalogue
- Sara Soueidan — accessibility and SVG, written by someone who builds.
- ARIA Authoring Practices — the keyboard and role spec for each pattern.
- Inclusive Components — components built accessible from the start.
- axe, Polypane — test in the browser you already have open.
- Nielsen Norman Group — usability research that overlaps almost entirely with this list.
- More at stealth.pm/tools.




