better-accessibility
Review and improve focus states, keyboard support, ARIA, forms, screen readers, hit areas, and motion accessibility.
Install
npx skills add https://github.com/jakubkrehel/skills --skill better-accessibilityScreen readers
Visually hidden content, live regions, toasts, alt text and SVG.
Visually hidden content
The canonical .sr-only pattern hides content visually while keeping it in the accessibility tree:
Use 1px boxes, not 0, because some screen readers skip zero-sized elements. white-space: nowrap stops words being read as one run-together string. Never display: none or visibility: hidden, which remove the content from assistive tech entirely.
Tailwind ships this as sr-only. The skip-link recipe is in focus-and-keyboard.md [blocked].
Use it for context sighted users get visually: <span class="sr-only">Opens in new tab</span>, table captions or an icon-only control's label where aria-label isn't an option.
Choosing how to announce a change
Work down this list and stop at the first match:
- Focus moves there anyway, as with an opened modal or the first invalid field. Nothing extra needed; the focus move is the announcement.
- Tied to a specific control, such as a field error or character count:
aria-describedbyon the control, announced with the field. - Non-urgent, not tied to a control, such as a toast, "Saved", a result count or a loading state: a polite live region,
role="status". - Urgent and not tied to a control, such as a form-level failure or session expiry:
role="alert".
Live regions
Rules for reliable announcements:
- For repeated polite updates, keep a stable empty region in the DOM before changing its text. Inserting a new polite region with its content is announced inconsistently.
- Dynamically inserted
role="alert"content is usually announced, but behavior varies. Test the target browser and screen-reader combinations. - Default to polite.
assertiveinterrupts whatever the user was reading. - Keep messages short and self-contained.
aria-atomic="true"re-reads the whole region on change. - Never move focus to a toast. Announce it and leave focus where the user is working.
For loading states: set aria-busy="true" on the updating region, announce "Loading…" politely, then announce the outcome ("Loaded, 12 results").
aria-hidden
aria-hidden="true" removes an element and its whole subtree from assistive tech. Use it for decorative icons and content duplicated for visual effect. Never put it on or above a focusable element, which creates stops you can Tab to that do not exist for a screen reader. Hiding something interactive means removing it from the tab order too.
Alt text
Choose by purpose, not by what the image looks like:
A missing alt is worse than an empty one, because screen readers fall back to reading the file name.
SVG
- Decorative SVG:
aria-hidden="true". No title needed. - Meaningful inline SVG:
role="img"plusaria-label="…", or a<title>as the first child referenced byaria-labelledby. - Simple cases:
<img src="icon.svg" alt="…">is the most reliable delivery.
Video and audio
Prerecorded video needs captions (1.2.2), and prerecorded audio needs a transcript (1.2.1). Never autoplay with sound (1.4.2), and always render controls.