Frontend Handbook
Written

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.

01

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 a role="button" still needs tabIndex and 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>
Save
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}>
Save
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)

02

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), lang on <html> so the right voice is used, lang on parts in another language, and a title on every <iframe>.

Try it: the same page built three ways

Build the same page with

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 h4 because 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)

03

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, -1 makes 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-visible shows 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.

Skip to content

Today's special: rye with caraway

Read the reviews

Focus visited (from real focus events)

nothing yet

Tab order the browser will use

  • Do: Unplug the mouse for five minutes and use your page with the keyboard only.

    Avoid: outline: none on :focus with no other style.

Read more on web.dev:Keyboard focus(opens in a new tab)

04

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 keydown handling for Enter and Space.
  • Reflect state in ARIA: aria-expanded on disclosure buttons, aria-pressed on toggles, aria-current="page" in navigation.
  • A live region (role="status" or aria-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.title on 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)

Add an item or open the delivery details.
  • Do: Keep one empty live region mounted and change its text; see practice 2 for aria-expanded panels.

    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)

05

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 need role="img" and a <title> or aria-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

How the image is used
Bar chart: sales were flat from January to March, then rose 40% in April.

Screen reader (simplified)

  • OKGood: short, natural, and says what matters in this context.

Which alternatives does my media need?

Media
Timing
  • 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)

06

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-scheme for dark mode, prefers-contrast and 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

#767676
#ffffff

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

NameAda
Emailada@

Error shown with an icon and text

NameAda
Emailada@Enter a full email address, like ada@example.com.
  • 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)

07

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 rem or em so 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-height and let text wrap; test at 200% zoom and at 320 px wide.

    Avoid: Fixed height with overflow: hidden on anything that holds text.

Read more on web.dev:Typography(opens in a new tab)

08

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

Your system settingprefers-reduced-motion: reduce
  • 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)

09

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 with aria-describedby; add autocomplete so 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

Version

We send the receipt here.

How do you want your order?

Screen reader (simplified)

Focus a field to hear it.
  • 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)

10

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.

Privacy notice
Read from the component (simplified)
TabRoleAccessible nameName 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)