Use elements for their meaning, not appearance: <header>, <nav>, <main>, <article>, <section>, <aside>, <footer> for structure; <button> for clickable actions; <a> for navigation; <label> tied to inputs by id. Semantic HTML gives screen readers landmarks to navigate by, gives keyboard users native focus/activation, and gives search engines structure. Pair with ARIA only when no semantic element fits — and prefer 'no ARIA' over 'wrong ARIA.'
Category
Accessibility
ARIA, keyboard navigation, screen readers, WCAG.
9 questions
alt provides a text alternative for an image: it's read by screen readers, shown if the image fails to load, used by search engines, and helps when images are disabled. Write it to convey the image's purpose/content; use empty alt="" for purely decorative images.
Use semantic HTML first (`button`, `label`, `nav`); reach for ARIA only when no semantic element fits. Ensure keyboard operability, visible focus, sufficient contrast, and screen-reader-friendly names. Test with VoiceOver/NVDA and a keyboard.
WCAG is the W3C standard organized around four principles — Perceivable, Operable, Understandable, Robust (POUR) — with three conformance levels (A, AA, AAA). AA is the practical legal/industry target. You implement it with semantic HTML first, ARIA only to fill gaps, keyboard operability, sufficient contrast, visible focus, accessible names, and testing with axe + a real screen reader.
Build on semantic HTML so the screen reader gets role/name/state for free, ensure full keyboard operability, give every control an accessible name, manage focus on dynamic changes, announce async updates with aria-live, and verify by actually navigating with VoiceOver/NVDA — not just running axe.
Toasts appear without user action, so screen readers won't notice them unless you use an ARIA live region. Put a permanent live-region container in the DOM (role='status'/aria-live='polite' for info, role='alert'/aria-live='assertive' for errors) and inject toast text into it. Don't move focus to the toast, and keep it on screen long enough to be read.
An accessible modal uses role='dialog' aria-modal='true' with an accessible name, renders in a portal, traps Tab focus inside while open, moves focus in on open and restores it to the trigger on close, closes on Escape and overlay click, and makes background content inert. The native <dialog> element gives most of this for free.
Make accessibility the default, not a review-time gate: accessible design-system components, automated linting + axe in CI, accessible-by-default patterns, training, and manual audits (keyboard, screen reader) for what automation can't catch. Bake it into the process, not heroics.
Build a11y into the design system, not into individual screens. Use semantic HTML + accessible primitives (Radix, React Aria), enforce with linting (eslint-plugin-jsx-a11y) and automated audits (axe in CI + Lighthouse), test with keyboard + a real screen reader (NVDA/VoiceOver), and own metrics like keyboard coverage and contrast pass rate. Treat a11y like a feature with a budget and a tracking dashboard, not a pre-launch checklist.