Accessibility
Semantic HTML and ARIA, structure, keyboard focus, dynamic content, images and media, contrast, type, motion, forms and inclusive design, each with a live demo.
Digital accessibility (a11y) means people with disabilities can use what you build in a meaningful and equivalent way: people who are blind and use a screen reader, who can't use a mouse, who have low vision, color blindness, hearing loss, motion sensitivity or a cognitive disability. Most of it comes from using HTML as intended and checking a few rules. This page covers the ideas that matter most, each with a demo; the demos read their own markup to show what assistive technology gets.
Based on the content-design modules of web.dev's Learn Accessibility (opens in a new tab) course, shortened to its main concepts. The screen reader output shown here is simplified; real ones differ in wording.
Semantic HTML and ARIA
The browser turns your HTML into an accessibility tree: every element gets a role (what it is), a name (what it's called) and states (checked, expanded…). Screen readers and voice control read that tree, not your pixels.
- Native elements come with all three plus keyboard support: a
<button>is focusable and works with Enter and Space for free. - ARIA attributes (
role,aria-*) only change the tree. They add no behavior, so arole="button"still needstabIndexand key handlers. - The five rules of ARIA: (1) don't use ARIA if HTML can do it; (2) don't add ARIA that repeats HTML; (3) every ARIA control must work with a keyboard; (4) never hide focusable elements with
aria-hidden; (5) every interactive element needs an accessible name. - Use ARIA for what HTML can't say:
aria-expanded,aria-current,aria-describedby, live regions, and custom widgets that follow the ARIA Authoring Practices.
Try it: a clickable div, a div patched with ARIA, and a button
All three look the same. Click each, then try to reach it with Tab and press Enter or Space.
Clickable div
the wrong way, on purpose<div onClick={save}>Save</div>- Role
- …
- Name
- …
- Reached by Tab
- no
- Activated
- 0 by pointer, 0 by keyboard
Screen reader (simplified)
div patched with ARIA
<div role="button" tabIndex={0} onClick={save} onKeyDown={enterOrSpace}>- Role
- …
- Name
- …
- Reached by Tab
- no
- Activated
- 0 by pointer, 0 by keyboard
Screen reader (simplified)
Native button
<button onClick={save}>Save</button>- Role
- …
- Name
- …
- Reached by Tab
- no
- Activated
- 0 by pointer, 0 by keyboard
Screen reader (simplified)
Do: Reach for the element that already means what you need:
<button>,<a href>,<input type="checkbox">,<details>,<dialog>.Avoid: A
<div onClick>: invisible to the keyboard and announced as plain text.
Read more on web.dev:ARIA and HTML(opens in a new tab)
Structure and the document
Sighted people skim a page by its layout; screen reader users skim by its landmarks and headings, jumping between them from a list. If the structure is only visual, there is nothing to jump to.
- Landmarks come from
<header>,<nav>,<main>,<aside>,<footer>, and a named<section>or<form>. Give repeated ones a label (aria-label="Main"). - Headings form the outline: one
h1, then levels in order without skipping. Pick the level for the structure and style it with CSS. - Lists (
ul,ol,dl) say how many items there are; data tables need<th scope>and a<caption>. - The document: a unique, descriptive
<title>with the specific part first (it's the first thing announced),langon<html>so the right voice is used,langon parts in another language, and atitleon every<iframe>.
Try it: the same page built three ways
Reading the sample…
Do: Check the outline with a headings or landmarks extension, or your screen reader's rotor, as part of review.
Avoid: Choosing
h4because it "looks right": style the correct level instead.
Read more on web.dev:Content structure(opens in a new tab)The document(opens in a new tab)
Keyboard focus
Many people never use a mouse: they move focus with Tab and Shift+Tab, act with Enter and Space, and move inside widgets with the arrow keys. They need to reach everything, in a sensible order, and always see where they are.
- Focus order follows the DOM. Keep the source order the same as the visual order instead of fixing it with
tabindex. tabindex="0"adds an element to the order,-1makes it focusable only by script (a skip-link target, a dialog). Avoid positive values: they jump to the front of the whole page.- A skip link as the first stop lets keyboard users pass the navigation on every page.
- Never remove the outline without a replacement. A visible focus indicator needs 3:1 contrast;
:focus-visibleshows it for keyboard users only. - Manage focus when the page changes under the user: move it into an opened dialog and back to the opener on close (see practice 8, Accessible Modal), and to the new heading after a client-side route change.
Try it: tab through a small site
Press Tab from the settings above to move through the sample site.
Today's special: rye with caraway
Read the reviewsFocus visited (from real focus events)
Tab order the browser will use
Do: Unplug the mouse for five minutes and use your page with the keyboard only.
Avoid:
outline: noneon:focuswith no other style.
Read more on web.dev:Keyboard focus(opens in a new tab)
JavaScript and dynamic content
When JavaScript changes the page, sighted users see it; screen reader users only hear about it if the page tells them. State changes belong on the element, and status messages go through a live region.
- Put click handlers on native controls (they get keyboard support); custom ones need
keydownhandling for Enter and Space. - Reflect state in ARIA:
aria-expandedon disclosure buttons,aria-pressedon toggles,aria-current="page"in navigation. - A live region (
role="status"oraria-live="polite";role="alert"only for urgent errors) must be in the page before its text changes, or nothing is announced. - In a single-page app, update
document.titleon every route change and move focus to the new content; the framework won't do it all for you.
Try it: what a screen reader hears when the page changes
Sourdough loaf · €4.50
Basket: 2 items
Screen reader log (simplified: live region changes and the button on activation)
Do: Keep one empty live region mounted and change its text; see practice 2 for
aria-expandedpanels.Avoid: Adding the live region together with its first message, or announcing every keystroke.
Read more on web.dev:JavaScript(opens in a new tab)
Images, video and audio
Every image and media file needs a text alternative that does the same job for someone who can't see or hear it. The right alt text depends on why the image is there.
- Informative: say what matters in this context, in under about 150 characters, without "image of". Functional (in a link or button): name the action or destination. Decorative:
alt="", or a CSS background. - Complex images (charts, maps): a short alt plus the details nearby, in a
<figcaption>, a table or a linked description. Inline SVGs needrole="img"and a<title>oraria-label. - Captions are the speech and meaningful sounds in sync, for people who can't hear; subtitles translate for people who can. Transcripts help everyone review and search, and search engines can read them.
- Audio description narrates important visuals between the dialogue; plan it in the script so the video says what it shows.
Try it: write alt text for an image, and check what media needs
Screen reader (simplified)
- OKGood: short, natural, and says what matters in this context.
Which alternatives does my media need?
- CaptionsRequired (WCAG A)Speech and meaningful sounds, in sync (1.2.2).
- Audio descriptionRequired (WCAG AA)Narrates important visuals between the dialogue (1.2.5).
- TranscriptRecommendedRead at your own pace, search, and indexed by search engines (1.2.8, AAA).
- Sign languageRecommended1.2.6, AAA.
Do: Write alt text like you'd describe the image to someone over the phone, for this page's purpose.
Avoid: File names, keyword lists, or text baked into images instead of real text.
Read more on web.dev:Images(opens in a new tab)Video and audio(opens in a new tab)
Color and contrast
Low vision and color blindness affect hundreds of millions of people, and everyone struggles to read in bright sunlight. Text needs enough contrast, and meaning must never depend on color alone.
- WCAG contrast ratios run from 1:1 to 21:1. Normal text needs 4.5:1; large text (24px, or 18.5px bold) 3:1; icons, borders and focus indicators 3:1. The enhanced level is 7:1. Logos and disabled controls are exempt.
- Pair color with something else: an icon and text for errors, underlines for links in text, patterns or labels in charts.
- Respect user settings:
prefers-color-schemefor dark mode,prefers-contrastand forced colors for high-contrast modes, and check both themes. - A grayscale view of a design is a quick test of "color alone".
Try it: a contrast checker, and an error shown by color only
Normal text: our bakery opens at seven every morning.
Large text: fresh bread
Contrast ratio 4.54:1
- Normal text (AA) ≥ 4.5:1Pass
- Large text, 24px or 18.5px bold (AA) ≥ 3:1Pass
- Normal text, enhanced (AAA) ≥ 7:1Fail
- Icons, borders, focus rings (AA) ≥ 3:1Pass
Color alone
Error shown by color only the wrong way, on purpose
Error shown with an icon and text
Do: Pick text and background tokens that pass in both light and dark themes, and check them in DevTools.
Avoid: Light gray placeholder-style text for real content, or red and green as the only difference.
Read more on web.dev:Color and contrast(opens in a new tab)
Typography
Readable type helps people with low vision and dyslexia, and everyone else. Above all, text must be able to grow: people zoom to 200% or more and change spacing, and the layout must keep up.
- Use a plain, familiar typeface; avoid ornate, handwritten or all-caps text for long reading, and limit bold and italics.
- Size text in
remoremso the user's default size applies; never block zoom in the viewport meta tag. - Let boxes grow with their text: no fixed heights on text containers. WCAG asks content to survive line height 1.5, paragraph spacing 2×, letter spacing 0.12em and word spacing 0.16em.
- Keep lines under about 80 characters, align text left (justified text leaves uneven gaps), and break content up with headings and lists.
Try it: make the text bigger and wider spaced
Fixed height, overflow hidden the wrong way, on purpose
Weekend baking class
Learn to shape and bake a country loaf in three hours, with flour, aprons and coffee included.
Saturdays at nine. Bring a container to take your bread home.
All text visible
Grows with its text (min-height)
Weekend baking class
Learn to shape and bake a country loaf in three hours, with flour, aprons and coffee included.
Saturdays at nine. Bring a container to take your bread home.
All text visible
Do: Use
min-heightand let text wrap; test at 200% zoom and at 320 px wide.Avoid: Fixed
heightwithoverflow: hiddenon anything that holds text.
Read more on web.dev:Typography(opens in a new tab)
Animation and motion
Motion can cause dizziness and nausea for people with vestibular disorders, distract people with ADHD, and flashing can trigger seizures. Keep animation meaningful, and let people turn it off.
- Never flash more than three times a second; check video and effects with a tool such as PEAT if in doubt.
- Anything that moves, blinks or scrolls on its own for more than 5 seconds needs a pause, stop or hide control.
- Honour
prefers-reduced-motion: reduce: replace movement with a fade or no animation, and start parallax, auto-playing carousels and video only when motion is allowed.
Try it: a moving banner that respects reduced motion
- Fresh croissants every morning
- Rye with caraway on Fridays
- Order cakes two days ahead
- Free coffee with any loaf
Reduced motion: the same items, standing still.
Do: Write motion as an enhancement:
@media (prefers-reduced-motion: no-preference){ … }.Avoid: Auto-playing animation with no way to stop it.
Read more on web.dev:Animation and motion(opens in a new tab)
Forms
Forms are where people get stuck: sign-up, checkout, search. Every field needs a visible label, and every error must be found, understood and fixed without relying on sight or color.
- A
<label for>that is always visible; placeholders disappear as you type and often fail contrast. - Group related options with
<fieldset>and<legend>; link hints witharia-describedby; addautocompleteso browsers and password managers can fill fields. - On error, say what is wrong and how to fix it in text next to the field, set
aria-invalid, and move focus to an error summary that links to each field. - Don't change context on focus or input (jumping to another page, submitting on select), and confirm success in words.
- For long forms, split them into steps with a clear progress indicator (see practice 10, Multi-step Form).
Try it: the same form, built two ways
Screen reader (simplified)
Do: Validate on submit (or on leaving a field), and keep the user's input when showing errors.
Avoid: A red border as the only sign of an error, or errors listed only at the top with no link to the field.
Read more on web.dev:Forms(opens in a new tab)
Patterns, design systems and design
Accessibility scales through teams: build it into shared components once, and into design before any code exists.
- Start from proven patterns (ARIA Authoring Practices, GOV.UK, USWDS, Inclusive Components), but test any copied code yourself with your own browsers and screen readers.
- A design system is the best place to fix things once: names, focus styles, contrast tokens and keyboard behavior in every button, menu and dialog.
- Inclusive design: offer a comparable experience, consider the situation (one hand, bright sun, slow network), use familiar conventions, give people control, and offer more than one way to do things.
- Personas that include permanent, temporary and situational disabilities keep these needs in view; annotations hand landmarks, headings, focus order and names to developers.
- Test with automated tools (they find about a third of issues), then by hand with the keyboard and a screen reader, and above all with disabled people; simulators are no substitute.
Try it: accessibility annotations on a component
Get the weekly menu
New breads and the week's specials, every Monday.
| Tab | Role | Accessible name | Name from |
|---|
Do: Put accessibility notes in the design file and acceptance criteria, so they are built, not bolted on.
Avoid: Treating a passing automated audit as proof that a page is accessible.
Read more on web.dev:Patterns, components and design systems(opens in a new tab)Design and user experience(opens in a new tab)Automated testing(opens in a new tab)Manual testing(opens in a new tab)Assistive technology testing(opens in a new tab)