← Front-End Developer — all chapters  ·  Kelick Tech Hub

Chapter 14 — Accessibility Foundations

Section 1 — What accessibility means

Accessibility, in web development, means that people with different abilities, using different tools, can perceive, understand, navigate, and interact with a website, and can contribute to it, on an equal basis with others. It is not a special feature added for a small minority, nor a checklist completed at the end of a project. It is a property of a well-built page: its content is available in more than one form, its controls work with more than one input method, and its structure is clear enough for both human readers and assistive technology to follow.

The word itself is worth pausing on. "Access" means the ability to reach and use something, and accessibility is about removing the barriers that prevent that reaching and using. A staircase without a ramp is inaccessible to a wheelchair user, not because the building is hostile, but because its design assumed a particular way of moving. A website that can only be used with a mouse, or that conveys information only through colour, or that hides its structure from a screen reader, makes the same kind of assumption, and it excludes visitors in the same way, even though the barrier is digital rather than physical.

Accessibility is also broader than disability in the narrow sense. It includes visitors using a screen reader because they are blind, visitors using captions because they are deaf, visitors using a keyboard because they cannot use a mouse, and visitors using voice control, switch devices, magnification, or high-contrast settings. It includes visitors whose abilities vary from day to day, or who are using a device in a difficult environment, as the next section explores. Designing for that range is not about anticipating every individual need in detail; it is about building pages that are flexible, structured, and honest about their content, so that different tools can present them in different ways.

A useful test for whether a page is accessible is to ask whether its essential tasks can be completed without seeing the screen, without using a mouse, and without relying on colour or sound alone. If a visitor can find information, fill in a form, and complete a purchase or a submission under those conditions, the page is on strong ground. If any essential task fails under one of those conditions, there is a barrier to find and remove, and the rest of this chapter teaches how to find and remove the most common ones.

Finally, accessibility is a professional responsibility, not merely a kindness. In many countries, including Nigeria, the United Kingdom, the European Union, and the United States, laws and public-sector standards require digital services to be accessible, and organisations that ignore those requirements risk legal and reputational consequences. But even where no law applies, the argument stands on its own: a page that excludes people is a page that has failed at its basic purpose, which is to communicate and to serve. Building accessibly is building well.

Check: In plain words, what does it mean for a website to be accessible?

Section 2 — Who benefits: permanent, temporary and situational

It is a common mistake to think of accessibility as serving only a fixed group labelled "disabled people," as if that group were separate from ordinary visitors. In reality, needs vary across a lifetime and across situations, and a useful framework divides them into three kinds: permanent, temporary, and situational. Understanding all three shows why accessible design benefits far more people than the first category alone suggests, and why it is worth building in from the start.

Permanent needs are those that do not change, or change slowly, over a person's life. A person who is blind, deaf, or has a motor condition that prevents mouse use will need screen reader support, captions, or keyboard access every time they use a website, not occasionally. Designing for permanent needs means building those supports into the page's structure — semantic headings, alt text, labels, keyboard operability — so they are always available, not added as an afterthought for a particular visitor. Those structural supports are the foundation of this chapter.

Temporary needs arise from an injury, an illness, or a short-term condition. A visitor with a broken arm may need keyboard access for a few weeks. A visitor recovering from eye surgery may need magnification or high contrast for a period. A visitor with a migraine may need to avoid flashing or rapidly moving content for a day. A page built accessibly serves those visitors during their temporary need, even though they may never think of themselves as disabled, and even though their need will pass. Temporary needs are a reminder that ability is not a fixed trait of a person, but a state that changes.

Situational needs arise from the environment, not from the person at all. A visitor holding a baby has only one hand free, and needs a page that works one-handed. A visitor in bright sunlight cannot read low-contrast text, and needs sufficient contrast. A visitor in a noisy room cannot hear a video's audio, and needs captions. A visitor on a slow connection cannot load a heavy video, and needs a text alternative. Each of these visitors is temporarily in a situation that mimics a disability, and accessible design — large tap targets, good contrast, captions, text alternatives — serves them too.

The practical conclusion is that accessibility is not a niche concern. It is design for human variety, in all its forms, and a page that serves permanent, temporary, and situational needs well is simply a page that serves more people, more reliably, in more circumstances. That breadth is why accessible practices, such as clear headings and good contrast, are also good design practices in general, and why they improve the experience for visitors who never encounter a barrier at all.

Check: Give one example each of a permanent, a temporary, and a situational accessibility need.

Section 3 — The four principles: POUR

The Web Content Accessibility Guidelines, known as WCAG, organise accessibility into four principles, remembered by the acronym POUR: Perceivable, Operable, Understandable, and Robust. Those four words are not jargon for its own sake; they are a useful mental checklist for judging whether a page is accessible, because each principle asks a different question about the visitor's experience, and a page that fails any one of them has a barrier, however well it does on the others.

Perceivable means that information and user interface components must be presentable to visitors in ways they can perceive. In practice, that means content is not conveyed only through sight, or only through sound, or only through colour. Text alternatives for images, captions for audio, sufficient contrast, and resizable text all serve perceivability: they ensure that a visitor who cannot see an image, hear a video, or read faint text can still perceive the information through another channel. If information exists only in a form a visitor cannot perceive, it might as well not exist for them.

Operable means that interface components and navigation must be operable — usable — by all visitors. That means everything works with a keyboard, not only with a mouse; that visitors have enough time to read and use content; that content does not cause seizures through flashing; and that navigation is consistent and predictable. A beautiful page that can only be operated with a precise mouse pointer fails operability, however perceivable its content is, because a keyboard user cannot use it at all.

Understandable means that information and the operation of the interface must be understandable. Text should be readable and written in a way its audience can follow, pages should behave predictably rather than changing unexpectedly when an element receives focus, and forms should help visitors avoid and correct mistakes, with clear labels and error messages. A page that is perceivable and operable but confusing — with unclear instructions, inconsistent navigation, or cryptic errors — still fails its visitors, because they cannot understand what to do, even though they can perceive and operate the controls.

Robust means that content must be robust enough to be interpreted reliably by a wide variety of user agents, including assistive technologies. That means using valid, semantic HTML, giving elements correct names, roles, and values, and not relying on hacks that a screen reader or a future browser may not understand. Robustness is the principle that connects accessibility to good coding practice most directly: clean, standards-based markup is more likely to work with tools you have never tested, than clever, non-standard markup is.

Holding POUR in mind while building — can it be perceived, operated, understood, and robustly interpreted — gives you a quick way to assess a feature before shipping it, and a language for discussing accessibility with colleagues. Later sections map specific techniques to these principles, so you can see how each practice serves one or more of them.

Check: What does each letter of POUR stand for, and what question does it ask?

Section 4 — Semantic HTML is the foundation

The single most important accessibility practice is also the simplest: use HTML elements for what they mean, not merely for how they look. A heading should be a heading element, a button should be a button element, a list should be a list element, and a navigation region should be a navigation element. That practice, called semantic HTML, gives assistive technology the structure it needs to present a page intelligibly, and it does so without any extra code, because the browser and the screen reader already understand those elements.

To see why semantics matter, consider a button built as a div with a click handler. Visually, it can look exactly like a button: coloured, rounded, with text. To a sighted mouse user, it behaves like a button, because clicking it triggers the handler. To a screen reader, however, it is merely a generic container with text, with no indication that it is interactive, no name that identifies its action beyond its text, and no keyboard behaviour, because a div is not focusable or activatable by keyboard by default. A visitor using a keyboard cannot tab to it or activate it with Enter or Space, and a screen reader user may not even know it exists as a control. The visual resemblance is a trap: it looks accessible, but it is not.

A real <button> element, by contrast, carries all of that behaviour for free. It is focusable by keyboard, activatable by Enter and Space, announced by screen readers as a button with its text as its name, and styled by browsers with a visible focus indicator. No JavaScript, no ARIA, and no extra attributes are needed to achieve that, because the element's semantics are built into the platform. The same is true for other native elements: a link is focusable and activatable, a form input has a label association and a value, a heading provides navigation landmarks for screen reader users who jump by heading. Native semantics are the cheapest accessibility you will ever get, because they are already implemented.

Semantic HTML also helps visitors who do not use assistive technology. Search engines use headings and landmarks to understand a page's structure. Browsers use form semantics to offer autofill and appropriate keyboards. Stylesheets can target semantic elements reliably, without relying on class names that may change. In each case, meaning in the markup produces benefits beyond accessibility, which is why semantic HTML is good practice in general, not only for accessibility.

The rule that follows is simple and worth memorising: reach for the native element first, and only build a custom control when no native element does the job, in which case you must recreate its semantics, keyboard behaviour, and focus handling deliberately, as later sections explain. Most pages need far fewer custom controls than their authors assume, and starting from semantics prevents a large class of accessibility failures before they occur.

Check: Why is a real <button> more accessible than a div styled to look like one?

Section 5 — Heading order and structure

Headings are not merely large, bold text; they are the outline of a page, and screen reader users rely on that outline to navigate, in the same way sighted visitors rely on a table of contents or on scanning headings visually. A screen reader can list all the headings on a page, jump from heading to heading, and build a mental map of the page's structure from them. If headings are used correctly, that map is clear and useful. If they are misused — skipped, out of order, or chosen for size alone — the map is misleading, and navigation becomes confusing.

The rules are straightforward. Use one <h1> for the page's main title, which describes the page as a whole. Use <h2> for its major sections, <h3> for subsections within those sections, and so on, in order, without skipping a level. A page about baking, for example, might have an <h1> "Baking Basics," <h2> sections for "Bread," "Cakes," and "Pastry," and <h3> subsections under "Bread" for "Ingredients," "Kneading," and "Baking." That hierarchy is logical: each level nests inside the one above it, and a visitor jumping by heading can follow the structure from general to specific.

Skipping a level — jumping from an <h1> directly to an <h3>, for example — breaks that logic, because the screen reader user is left wondering where the missing <h2> went, or whether a section was missed. Using a heading level for its visual size alone — choosing an <h4> because it looks smaller, even though it is a major section — is equally problematic, because the visual size and the structural level then disagree, and the screen reader's outline reflects the structure, not the appearance. If a heading needs to look different, change its appearance with CSS, and keep its level for structure. That separation of structure and presentation is a core principle of HTML and CSS, and headings are where it matters most for navigation.

Headings should also be descriptive and unique where possible. A page with several headings all reading "More information" gives a screen reader user no way to distinguish them in a headings list, while headings that name their section — "Delivery options," "Returns policy," "Contact details" — let the user jump directly to the section they need. That descriptiveness helps sighted visitors too, who scan headings to find content quickly, so writing good headings is a shared benefit, not a specialist task.

Finally, do not use headings for non-heading content, such as bold text in a paragraph, or for an entire page's text, and do not leave a page without any headings. A page with no headings offers a screen reader user no outline at all, forcing them to read through linearly to find anything. A page with a clear, ordered, descriptive heading structure offers a map, and a map is what makes a long page navigable rather than merely readable.

Check: Why should you never skip from an <h1> to an <h3>?

Section 6 — Landmarks: header, nav, main and footer

Landmarks are regions of a page that assistive technology can identify and jump between, giving screen reader users a fast way to move to the main content, the navigation, or the footer, without reading through everything in between. In HTML, landmarks are provided by semantic elements: <header>, <nav>, <main>, <footer>, <aside>, and <section> with an accessible name, among others. Using those elements correctly is a simple way to make a page's overall structure navigable, complementing the heading outline from the previous section.

The most important landmark is <main>, which should wrap the page's primary content, and which should appear only once per page. A screen reader user can jump directly to <main>, skipping repeated navigation, headers, and sidebars that appear on every page, and landing straight on the content that is unique to this page. That jump is the digital equivalent of opening a book at its first chapter rather than reading its cover and contents every time. A page without a <main> element forces the user to tab or read through all the repeated material to reach the content, which is tedious on every visit.

<nav> marks a region containing navigation links, and it should be used for major navigation blocks, such as the site's primary menu, not for every group of links on the page. A footer with a few links does not need to be a <nav>, and a list of related articles is not navigation in the landmark sense. When a page has more than one <nav>, each should have a distinct accessible name, using aria-label, so a screen reader user can distinguish "Primary navigation" from "Footer navigation" in a landmarks list. Without distinct names, multiple landmarks of the same type are indistinguishable, which defeats their purpose.

<header> and <footer> mark the introductory and concluding content of a page, or of a section within it. A page-level header typically contains the site title and primary navigation, while a section-level header contains that section's heading and introductory material. The distinction matters for screen readers, which announce the landmark's context, but the practical rule is to use header and footer for their structural roles, not as generic styling containers. An element that is merely a coloured band at the top of a page, with no introductory content, does not need to be a <header>; an element that introduces the page does.

<aside> marks content tangentially related to the main content, such as a sidebar with related links or advertisements, and <section> marks a thematic grouping, but only becomes a landmark when it has an accessible name, via aria-labelledby or aria-label. That last point is a common source of overuse: wrapping every div in a <section> without a name creates no landmark and adds no structure, while cluttering the markup. Use sections for genuine thematic groups, name them, and let landmarks reflect the page's real structure, not its styling divisions.

Check: Why should a page have exactly one <main> element?

Section 7 — Descriptive link text

Links are how visitors move through the web, and their text is how visitors decide whether to follow them. For sighted visitors, link text is read in context, surrounded by surrounding sentences that clarify its destination. For screen reader users, links are often listed out of context, in a links list that shows only the link text itself, in order, without the surrounding paragraph. If that text is vague — "click here," "read more," "this link" — the list is useless, because every link sounds the same and none says where it goes.

The rule is therefore to make link text descriptive on its own, so it makes sense even when read in isolation. Instead of "Click here to see our delivery options," write "See our delivery options," with the whole phrase as the link, or with "delivery options" as the link if the sentence structure requires it. Instead of "Read more" repeated five times on a news page, write "Read more about the new library opening," "Read more about the road closure," and so on, so each link is distinct in a list. That descriptiveness helps sighted visitors too, who scan links to decide where to go, and it helps search engines understand a link's destination, so it is good practice generally.

There are cases where a short link text is unavoidable, such as a "Read more" link in a card where space is tight. In those cases, the link can be made descriptive for assistive technology without changing its visual appearance, by including additional text that is visually hidden but available to screen readers, or by using aria-label to provide a fuller name. For example, a visually short "Read more" link can have an aria-label of "Read more about the new library opening," so a screen reader announces the full destination while sighted visitors see the short form. That technique should be used sparingly, however, because a mismatch between visible text and announced text can confuse voice-control users, who speak the visible text to activate a link. Where possible, make the visible text itself descriptive, and use hidden text only when space genuinely prevents it.

Link text should also avoid relying on the URL itself as the text, such as "https://example.com/very/long/path," because a screen reader will read that aloud character by character or in a garbled form, and a sighted visitor gains no information from a long address. Use a human-readable phrase instead, and let the href attribute carry the address. Similarly, avoid using the same link text for different destinations on the same page, because that creates ambiguity in a links list, and avoid using different text for the same destination, because that creates confusion about whether the links go to the same place.

Finally, links should be distinguishable from surrounding text by more than colour alone, as a later section explains, and they should be focusable and activatable by keyboard, which native links are by default. Descriptive text, combined with those basics, makes links a reliable wayfinding tool for every visitor, not merely for those who can see the surrounding context.

Check: Why is "click here" poor link text for a screen reader user?

Section 8 — Alt text for meaningful images

Images convey information, and alt text — the alternative text provided by an image's alt attribute — is how that information reaches visitors who cannot see the image, including screen reader users and visitors whose images have failed to load. Writing good alt text is therefore not a technical detail, but a writing task: you must decide what the image is communicating, and convey that in words, concisely and accurately, so a visitor who hears the alt text understands what a sighted visitor understands from seeing the image.

The key question to ask is: what is this image for? If it is a photograph of a product, the alt text should describe the product and its relevant features, such as "Red ceramic mug with a handle, viewed from the side." If it is a chart, the alt text should convey the chart's conclusion, not merely "A chart," because the conclusion is what a sighted visitor takes from it. If it is a portrait of a person, the alt text should name the person if they are known and relevant, and describe the setting if that matters. In each case, the alt text is not a literal description of every pixel, but a translation of the image's meaning into words, focused on what matters for the page's purpose.

Alt text should be concise. A screen reader user hears it read aloud, and a long, rambling description becomes tedious, especially if the image is one of many on a page. Aim for a sentence or a short phrase that captures the essential information, and put longer detail in surrounding text if it is needed. For example, a complex diagram might have short alt text summarising its point, with a longer description in the paragraph below it, or linked from it, so visitors who need detail can find it without forcing every visitor to hear a paragraph for every image.

Alt text should not repeat information already present in surrounding text. If a caption below an image already says "The new library on Main Street, opened in March," then alt text repeating that caption forces a screen reader user to hear the same information twice. In that case, the alt text can be shorter, or the image can be treated as decorative if the caption already conveys its meaning, a decision the next section explores. Conversely, alt text should not say "image of" or "picture of," because screen readers already announce that an element is an image, so that phrase is redundant.

Finally, alt text should never be left out for a meaningful image. An <img> without an alt attribute is not the same as an image with empty alt text: the former may cause a screen reader to announce the image's file name, which is unhelpful, while the latter deliberately signals that the image is decorative. For a meaningful image, always provide alt text, and check it by imagining the image removed: would a visitor who only hears the alt text still understand the page? If the answer is yes, the alt text has done its job.

Check: What question should you ask yourself before writing alt text?

Section 9 — Decorative images and complex images

Not every image conveys information that needs translating. Some images are purely decorative: a background flourish, a divider, a stock photograph that adds atmosphere but no content, an icon that repeats a label already present in text. For those images, the correct alt text is empty — alt="" — which tells a screen reader to skip the image entirely, rather than announcing it. That skipping is a kindness: a screen reader user should not have to listen to descriptions of decoration that add nothing to their understanding, any more than a sighted visitor should have to study every decorative flourish to read a page.

Empty alt text is a deliberate signal, not an omission. An <img> with alt="" is present in the markup, but the screen reader knows to ignore it, because the author has judged it decorative. An <img> with no alt attribute at all, by contrast, is ambiguous: the screen reader does not know whether the author forgot the alt text or whether the image is decorative, and it may announce the file name as a fallback, which is noisy and unhelpful. That distinction is why the rule is "meaningful images get descriptive alt text, decorative images get empty alt text, and no image gets no alt attribute." It is a small rule with a large effect on screen reader experience.

Complex images, such as detailed charts, graphs, maps, and diagrams, need a different approach, because a short alt text cannot convey their full information. A chart showing sales over five years, for example, cannot be summarised in a single sentence without losing the data that makes the chart useful. For such images, provide a short alt text that gives the chart's overall message — for example, "Bar chart showing sales rising each year from 2021 to 2025" — and provide a full text alternative nearby, such as a data table with the same values, or a longer description linked from the image. That combination serves both visitors who need only the message and visitors who need the detail, without forcing either to endure an inappropriate level of description.

Maps deserve special attention, because they often convey spatial information that is difficult to translate into words. A map showing store locations, for example, should be accompanied by a list of those locations with addresses, so a visitor who cannot use the map can still find the information. An interactive map should also be keyboard accessible, with its controls reachable and its locations available as text, not only as visual markers. If a map cannot be made fully accessible, its essential information must still be available in text form, because a map that is the only source of an address or a route is a barrier to anyone who cannot use it.

The underlying principle for both decorative and complex images is the same as for meaningful images: decide what information, if any, the image contributes, and ensure that information is available in a form every visitor can use. Decoration contributes none, so it should be skipped. Complex information contributes a lot, so it needs more than a short phrase. Judging that contribution honestly, image by image, is the skill that makes alt text work.

Check: What is the difference between alt="" and no alt attribute at all?

Section 10 — Form labels: the basics

Forms are where accessibility failures hurt most, because a form is a task the visitor must complete — registering, ordering, applying, contacting — and a failure in a form can prevent that task entirely. The foundation of an accessible form is labelling: every form control must have a label that identifies its purpose, so a visitor knows what to enter, a screen reader can announce the control's purpose, and a mouse or touch user can tap the label to focus the control. A form without labels is a guessing game, and guessing games are not accessible.

A label is, in HTML, a <label> element associated with a control, and that association is what makes it work for assistive technology. Without association, a piece of text next to a field is merely visual: a sighted visitor can see it and infer that it belongs to the field, but a screen reader user, navigating field by field, will hear only "edit text, blank," with no indication of what the field is for. With association, the screen reader announces "Email address, edit text," because it knows the label belongs to that field. That announcement is the difference between a usable form and an unusable one, and it costs only a correct label association, which the next section explains in detail.

Labels also benefit sighted visitors in practical ways. A label that is correctly associated enlarges the clickable area for its control: clicking the label focuses the field, or toggles a checkbox or radio button, which is especially helpful on touch screens, where small controls are hard to tap accurately. A label that is visually clear, placed consistently, and written in plain language reduces errors, because visitors are less likely to enter the wrong information when they understand what is asked. Good labels are therefore not merely an accessibility requirement; they are good form design.

Labels should be visible, not hidden, in almost all cases. A placeholder inside a field is not a label, as a later section explains, because it disappears when typing begins and is not reliably announced as a label. A label that is visually hidden but present for screen readers is better than no label, but it deprives sighted visitors of the same clarity, and it can confuse visitors with cognitive difficulties who benefit from visible labels. The default should be a visible label, above or beside its field, in a consistent position throughout the form, so visitors can learn the form's pattern once and apply it to every field.

Finally, labels should be descriptive and specific. "Name" is vague: is it a full name, a first name, a username? "Full name" is clearer. "Date" is vague: is it a birth date, a start date, a delivery date? "Date of birth" is clearer. Writing labels that answer the visitor's question — what exactly do you want from me here — reduces errors and support requests, and it is a writing skill as much as a coding skill. The next sections build on this foundation with association, grouping, and error handling, but the foundation itself is simple: every control, a visible, descriptive label, correctly associated.

Check: Why is a piece of text next to a field not enough without label association?

Section 11 — Associating labels with controls

A label becomes useful to assistive technology only when it is associated with its control, and HTML provides two ways to make that association, both of which you should know, because each suits different markup situations. The first, and most explicit, uses the for attribute on the label and the id attribute on the control, with matching values. The second wraps the control inside the label element itself. Both produce the same association; the choice between them is a matter of markup convenience and clarity.

The for and id method looks like this:

<label for="email">Email address</label>
<input id="email" type="email" name="email">

The label's for="email" points to the control's id="email", and the browser and screen reader understand that the label belongs to that control, wherever they appear in the markup. That separation is useful when the label and control cannot be adjacent in the HTML, or when a stylesheet positions them separately, because the association does not depend on proximity. The critical requirement is that the id must be unique on the page: if two controls share the same id, the association becomes ambiguous, and assistive technology may announce the wrong label or none at all. Checking for duplicate ids is therefore part of form testing.

The wrapping method looks like this:

<label>
  Email address
  <input type="email" name="email">
</label>

Here, the control is inside the label, and the association is implicit: the label belongs to the control it contains. That method is concise and avoids the need for ids, which is convenient for simple forms, but it can be less flexible when the label and control need to be in different parts of the layout, or when a control has additional associated text, such as help or error messages, that also need association. In those cases, the explicit for and id method, combined with aria-describedby for help text, is clearer.

A common mistake is to use both methods at once, wrapping a control in a label that also has a for attribute pointing elsewhere, which creates confusion about which association is intended. Another common mistake is to associate a label with the wrong control, by copying markup and forgetting to update an id, so a field labelled "Email address" is actually associated with the password field. That error is invisible to a sighted visitor, who reads the visual label, but it is glaring to a screen reader user, who hears the wrong announcement. Testing forms with a screen reader, or at least by inspecting associations in developer tools, catches those errors.

Finally, every control needs its own label, including controls that seem self-evident, such as a search box with a magnifying glass icon. An icon alone is not a label for a screen reader, unless it is given an accessible name, as later sections explain. A search field should have a label — visible, or, if space genuinely prevents it, visually hidden but present — so its purpose is announced. The habit to form is: no control without a label, and no label without a correct association, verified, not assumed.

Check: What are the two ways to associate a label with a control?

Section 12 — Grouping controls with fieldset and legend

Some form controls belong together as a group, and that grouping should be conveyed to assistive technology, not merely suggested visually. The classic example is a set of radio buttons for a single question, such as "Preferred contact method," with options for email, phone, and post. Visually, the question sits above the options, and a sighted visitor understands that the options belong to that question. A screen reader user navigating option by option, however, may hear only "Email, radio button," "Phone, radio button," without the question, unless the grouping is marked up correctly. That is what <fieldset> and <legend> are for.

A fieldset groups related controls, and its legend provides the group's label, which screen readers announce when the user enters the group and, in some cases, with each control in it:

<fieldset>
  <legend>Preferred contact method</legend>
  <label><input type="radio" name="contact" value="email"> Email</label>
  <label><input type="radio" name="contact" value="phone"> Phone</label>
  <label><input type="radio" name="contact" value="post"> Post</label>
</fieldset>

Here, the legend "Preferred contact method" is the question, and each radio button is an answer, with its own label. A screen reader user entering the group hears the legend, so they understand the question, and then hears each option in context. Without the fieldset, the options would be orphaned, and the user would have to remember the question from earlier in the page, or navigate back to find it, which is error-prone, especially in a long form.

Fieldsets are also appropriate for other groupings, such as a set of checkboxes for interests, a group of fields for an address, or a date split into day, month, and year fields. In each case, the legend names the group, and the individual controls have their own labels. For an address, for example, the legend might be "Delivery address," and the fields inside might be labelled "Street," "City," and "Postcode." That hierarchy — group label, then field labels — mirrors the visual structure, and it gives screen reader users the same context sighted visitors get from layout.

Styling fieldsets can be fiddly, because browsers apply default borders and padding to them, and legends have their own positioning quirks. Those styling challenges sometimes tempt developers to replace fieldsets with plain div elements and headings, but that loses the grouping semantics, unless the grouping is recreated with ARIA roles, which is more work and more fragile. A better approach is to keep the fieldset and legend, and to style them with CSS, accepting a little extra effort to make them look right. The semantics are worth the styling effort, because they are what make the group navigable and understandable for assistive technology.

Finally, do not overuse fieldsets. Every control does not need its own fieldset, and a form with a fieldset around each single field adds noise without meaning. Use fieldsets where controls genuinely form a group with a shared question or purpose, and where a screen reader user would benefit from hearing that group's name. That judgement — grouping where grouping is meaningful — is the same judgement you apply to headings and sections, and it reflects a general principle: markup should reflect meaning, not merely layout.

Check: Why does a group of radio buttons need a fieldset and legend?

Section 13 — Error messages and help text

A form that only tells a visitor they have made a mistake, without saying what the mistake is or how to fix it, is not an accessible form, however clear its labels are. Error messages and help text are part of a form's communication, and they must be associated with the fields they refer to, so that screen reader users encounter them in context, at the moment they need them, rather than having to hunt for them elsewhere on the page.

Help text — such as "Passwords must be at least 8 characters" — should be associated with its field using aria-describedby, which points from the field to the help element's id:

<label for="password">Password</label>
<input id="password" type="password" aria-describedby="password-help">
<p id="password-help">Passwords must be at least 8 characters.</p>

A screen reader announcing the password field will also announce the help text, so the visitor knows the requirement before typing, not after failing. That timing matters: help given after an error is less useful than help given before, because it could have prevented the error. Placing help text close to its field visually, and associating it with aria-describedby, ensures it is available both visually and programmatically, at the right moment.

Error messages need the same association, plus a way to draw attention to them. When a field fails validation, its error message should be associated with the field, again with aria-describedby, and the field should be marked as invalid, with aria-invalid="true" or with the native validation that browsers provide. The error message itself should be specific and constructive: "Enter a valid email address, such as name@example.com" is helpful, while "Invalid input" is not, because it does not say what is invalid or how to fix it. Specificity is a writing skill, and it is as important as the technical association, because a technically associated but vague error still leaves the visitor stuck.

Errors should also be summarised at the top of the form, with links to each field in error, so a visitor who submits a long form and encounters several errors can see them all at once and jump to each one, rather than discovering them one by one. That summary should receive focus when the form is submitted with errors, so keyboard and screen reader users are taken to it immediately, rather than remaining at the submit button wondering why nothing happened. That focus movement is a form of focus management, covered later in this chapter, and it is essential for error handling to feel responsive rather than silent.

Finally, errors should not rely on colour alone. A field outlined in red may signal an error to a sighted visitor, but a visitor who cannot distinguish red, or who uses a screen reader, needs the text message and the invalid marking as well. Colour can reinforce an error, but text must carry it. A form that follows those principles — help before, specific errors after, associated text, a summary with focus, and no colour-only signalling — is a form that helps every visitor succeed, rather than punishing them for failing.

Check: How does aria-describedby help a screen reader user with a form field?

Section 14 — Visible focus indicators

When a visitor navigates a page with a keyboard, they need to know which element is currently focused — which link, button, or field will respond when they press Enter or Space. That knowledge comes from the focus indicator, usually an outline around the focused element, provided by the browser by default. A visible focus indicator is therefore not a decorative detail; it is the keyboard equivalent of a mouse pointer, showing where the visitor is on the page. Without it, keyboard navigation is navigation in the dark.

Browsers provide a default focus outline, but many designers remove it with CSS, often because they find it ugly, using a rule such as :focus { outline: none; }. That removal, without providing a replacement, breaks keyboard navigation, because the visitor can still tab through the page — focus still moves — but they cannot see where it has moved to. They may press Enter expecting one action and trigger another, or they may tab repeatedly, lost, because no element shows focus. Removing the default outline is therefore one of the most harmful accessibility mistakes, and it is unfortunately common, because its effect is invisible to mouse users, who never need the outline.

The correct approach is to keep a visible focus indicator, and to style it if the default does not suit the design, rather than removing it. A custom focus style should be at least as visible as the default: a clear outline, a background change, or a border change that contrasts with the element and its surroundings, and that is visible against any background the element may sit on. The :focus-visible pseudo-class helps here, because it applies focus styles only when the browser determines focus should be visibly indicated — typically for keyboard navigation, but not for a mouse click, where the visitor already knows where they clicked. Using :focus-visible lets you provide a strong keyboard focus indicator without adding an outline to every mouse click, which was the aesthetic concern that led many designers to remove outlines in the first place.

Focus indicators should also be tested in context, not merely in isolation. An outline that is visible on a white background may be invisible on a dark image or a coloured button, and an outline that works for a link may not work for a custom control. Test by tabbing through the entire page, on every background, and checking that the focused element is always obvious. If it is not, adjust the focus style — a different colour, a thicker outline, an offset outline that sits outside the element — until it is unmistakable.

Finally, focus indicators should not be the only feedback for focus. A focused element should also be announced correctly by a screen reader, with its name and role, which native elements do automatically. But for sighted keyboard users, the visual indicator is the primary feedback, and its absence is a barrier that no amount of semantic correctness can compensate for. Keep focus visible, always, and style it with care.

Check: Why is removing the focus outline without a replacement harmful?

Section 15 — Keyboard navigation and tab order

A page must be fully operable with a keyboard alone, because many visitors cannot use a mouse, whether due to a motor condition, a temporary injury, or a preference for keyboard efficiency. Keyboard operability means that every interactive element — links, buttons, form controls, menus, dialogs — can be reached by tabbing, activated with Enter or Space as appropriate, and left again without getting stuck. It also means that the order in which elements are reached, the tab order, follows a logical sequence that matches the visual layout and the content's meaning.

By default, the tab order follows the order of elements in the HTML, which is why HTML order matters so much, as discussed in earlier chapters. If the HTML places the main content first, then the sidebar, then the footer, a keyboard user will tab through in that order, which is logical. If the HTML places a sidebar first for layout reasons, but visually displays the main content first using CSS, a keyboard user will tab to the sidebar before the main content, jumping around the screen in a confusing sequence that does not match what a sighted visitor sees. That mismatch between visual order and tab order is a common accessibility failure, and the fix is usually to order the HTML to match the visual order, rather than to reorder visually with CSS alone. A positive tabindex, such as tabindex="1", forces an element to the front of the tab order, ahead of all naturally tabbable elements, and multiple positive values create a custom order that is fragile and hard to maintain, because adding a new element requires renumbering. Positive tabindex values are therefore discouraged in modern practice. A tabindex of 0 makes a normally non-focusable element focusable in natural order, which is useful for custom controls that need keyboard access, while a tabindex of -1 makes an element focusable programmatically — for example, to receive focus when a dialog opens — but removes it from the natural tab order, so it is not reached by ordinary tabbing. Keyboard interaction patterns also matter beyond tab order. Buttons should activate with Enter and Space, links with Enter, checkboxes and radio buttons with Space, and menus and dialogs should support arrow keys and Escape where appropriate, following established patterns for those components. Recreating those patterns for custom controls is work, which is another reason to prefer native elements, which provide them for free. A custom control that can be tabbed to but cannot be activated with the expected key is only half accessible, and a visitor who tabs to it and presses Space expecting it to work, only to find nothing happens, will reasonably conclude the page is broken.

Testing keyboard navigation is straightforward and should be routine: put the mouse aside, tab through the entire page, activate every control, open every menu and dialog, fill in every form, and check that you can complete every task and return to where you started. If any task requires a mouse, or if focus disappears, jumps unexpectedly, or gets trapped, there is a keyboard barrier to fix, and fixing it benefits not only keyboard users, but also power users and visitors on devices without a precise pointer.

Check: Why should you avoid positive tabindex values?

Section 16 — Skip links

Many pages begin with repeated content — a site header, a logo, a navigation menu, a search box — that appears on every page and that a visitor must pass through to reach the page's unique content. For a mouse user, skipping that repeated content is easy: they scroll past it or click directly on the content they want. For a keyboard user, skipping it means tabbing through every link in the navigation, on every page, before reaching the content, which is tedious and time-consuming. For a screen reader user, it means listening to the same navigation on every page before hearing the content. A skip link solves that problem with a single, simple mechanism.

A skip link is a link placed at the very beginning of a page, before any other content, that jumps directly to the main content when activated. Its text is typically "Skip to main content," and its target is the main content element, identified by an id, such as <main id="main-content">. The markup looks like this:

<a href="#main-content">Skip to main content</a>
<!-- header and navigation here -->
<main id="main-content">
  <!-- page content here -->
</main>

When a keyboard user tabs to the skip link and activates it, focus moves to the main content, bypassing the navigation. When a screen reader user activates it, they are taken to the main content's start. That single link saves dozens of tab presses per page, multiplied across every page a visitor reads, which is a substantial saving over a session.

Skip links are often visually hidden until they receive focus, so they do not clutter the visual design for mouse users, who do not need them, but appear when a keyboard user tabs to them. That hiding must be done carefully: the link must be hidden visually but still focusable, using a technique that keeps it in the tab order and makes it visible on focus, rather than using display: none, which would remove it from the tab order entirely. A common pattern positions the link off-screen or clips it, and restores it to a visible position when it receives focus, so a keyboard user sees it appear at the top of the page as they begin tabbing. That appearance is a reassuring signal that the page was designed with keyboard users in mind.

A skip link is not a substitute for good landmarks or heading structure, which also help navigation, but it is a simple, effective complement to them, and it is expected by many keyboard and screen reader users, who look for it as a sign of an accessible site. Adding one is a small task — a link, an id, and a little CSS — with a large return in usability, and it is a good first accessibility improvement for a site that has none. Test it by tabbing from the top of the page: the first tab should reveal the skip link, and activating it should move focus to the main content, not merely scroll the page visually while leaving focus behind.

Check: What problem does a skip link solve for keyboard users?

Section 17 — Focus management in dialogs and menus

Focus management is the practice of deliberately moving keyboard focus to the right place at the right time, particularly when the page's content changes dynamically, such as when a dialog opens, a menu expands, or a form submission reveals new content. Without focus management, a keyboard or screen reader user can be left in a confusing place: a dialog opens visually, but focus remains on the button behind it, so tabbing continues through the page behind the dialog, which is now obscured; or a dialog closes, but focus does not return to the button that opened it, leaving the user lost somewhere else on the page. Good focus management prevents those confusions by keeping focus aligned with what is visible and relevant.

A modal dialog is the classic case. When it opens, focus should move into the dialog, typically to its first focusable element, or to the dialog itself if it has no obvious first element, so a keyboard user is immediately inside the dialog and a screen reader user hears its content. While the dialog is open, focus should be trapped within it: tabbing past its last element should return to its first, and shift-tabbing past its first should return to its last, rather than escaping to the page behind, which is now inert and should not be interactive. When the dialog closes, focus should return to the element that opened it, so the visitor resumes exactly where they left off, rather than at the top of the page or at an unrelated element. Those three steps — move in, trap, return — are the standard dialog focus pattern, and they are expected by assistive technology users.

Menus have a similar, though less strict, pattern. When a menu button is activated, focus may stay on the button, with the menu's items reachable by arrow keys, or it may move into the menu, depending on the menu's design, but in either case, the visitor must be able to reach the menu's items by keyboard, navigate among them, and close the menu with Escape, returning focus to the button. A menu that can only be opened with a mouse, or that traps focus without an Escape route, or that leaves focus in a hidden menu after closing, fails those expectations, even if its visual design is polished.

Implementing focus management correctly is fiddly, which is why native elements are preferable where they exist: the <dialog> element, for example, provides much of the modal behaviour, including focus handling and Escape closing, with far less custom code than a div-based dialog. When a custom dialog or menu is necessary, its focus behaviour must be implemented deliberately and tested with a keyboard and a screen reader, because focus bugs are invisible to mouse testing. A page that manages focus well feels coherent to keyboard users, because focus always follows their attention; a page that does not feels like a maze, where the visitor's position on the page bears no relation to what they see.

Check: What three steps make up correct dialog focus management?

Section 18 — Colour contrast

Colour contrast is the difference in brightness between text and its background, and it determines whether text is readable, especially for visitors with low vision, ageing eyes, or a screen in bright light. Text with insufficient contrast — light grey on white, for example — may look elegant to a designer with perfect vision on a high-quality monitor, but it can be illegible to a visitor with reduced contrast sensitivity, or on a phone in sunlight, or on a low-quality display. Contrast is therefore not a matter of taste; it is a matter of readability, and it has measurable standards.

The WCAG standards express contrast as a ratio, comparing the relative luminance of the text and background. For normal text, the AA standard requires a ratio of at least 4.5 to 1, which means the text must be substantially darker or lighter than its background. For large text — defined as at least 18 points, or 14 points bold — the requirement is lower, at 3 to 1, because larger, bolder text is easier to read even at lower contrast. Those numbers are not arbitrary; they are based on research into human vision and readability, and they are the thresholds used by accessibility checkers and by legal standards in many jurisdictions. Meeting them is a baseline, not an aspiration, and exceeding them, where possible, is better.

Checking contrast is straightforward with tools, which a later section covers, but the habit to form is to check every text colour against its background, in every state — normal, hover, focus, disabled, and in both light and dark themes if the site offers them — because a colour that passes on a white background may fail on a coloured button, and a colour that passes in light mode may fail in dark mode. Disabled text is a particular trap: it is often shown in light grey to indicate it is unavailable, but if that grey is too light, the text becomes unreadable, and a visitor cannot even tell what is disabled or why. Disabled text should still meet contrast requirements, or be supplemented by another indicator, such as an icon or a message, so its meaning is not conveyed by faintness alone.

Contrast also applies to non-text elements that convey meaning, such as icons, chart lines, and the boundaries of form fields. An icon that is too faint against its background may be missed, and a form field whose border is too light may be hard to find. The WCAG standard for non-text contrast is 3 to 1 for elements that must be identified, which is a lower bar than for text, but still a bar that faint, decorative-looking controls often fail. Checking those elements, not only body text, is part of a complete contrast audit.

Finally, contrast is not the only readability factor. Font size, line height, letter spacing, and font choice all affect legibility, and a page with adequate contrast but tiny, cramped text is still hard to read. Contrast is, however, the most measurable and most commonly failed factor, and it is the easiest to fix, because it usually requires only a darker text colour or a lighter background. Fixing contrast is therefore a high-return first step in any accessibility improvement effort.

Check: What contrast ratio does WCAG AA require for normal text?

Section 19 — Not relying on colour alone

Colour is a powerful way to convey information — red for error, green for success, blue for a link — but it must never be the only way that information is conveyed, because visitors who cannot distinguish those colours, or who use a monochrome display, or who print in black and white, will miss the information entirely. The rule is simple: colour can reinforce a message, but it must not carry the message alone. Every piece of information conveyed by colour must also be conveyed by another means, such as text, an icon, a pattern, or a shape.

A form error is the classic example. A field outlined in red signals an error to a visitor who can see red, but a visitor with red-green colour blindness may see no difference between the error outline and a normal outline, and a screen reader user hears no colour at all. The error must therefore also be conveyed by text — an error message, as discussed earlier — and by a non-colour indicator, such as an icon or the field being marked invalid. That redundancy ensures the error is perceivable however the visitor perceives the page, and it is why the error section emphasised text messages alongside colour.

Charts and graphs are another case where colour-only encoding fails. A pie chart with segments distinguished only by colour — red for one category, green for another — is unreadable to a visitor who cannot distinguish those colours, and it is difficult for anyone to interpret without labels. Labelling each segment directly, with its category name and value, or providing a data table alongside the chart, ensures the information is available without colour. Patterns, such as hatching or different line styles, can also distinguish segments, but labels and tables are clearer and more reliable, and they benefit sighted visitors too, who may struggle to match a colour to a legend key.

Links are a third case, already touched on earlier. A link distinguished from surrounding text only by colour — blue text among black text, with no underline — may be missed by a visitor who cannot distinguish blue from black, or who is viewing the page in greyscale. Underlining links, or otherwise distinguishing them by shape or weight, ensures they are identifiable without colour. That is why the convention of underlined links persists, even in designs that might prefer a cleaner look without underlines: the underline is an accessibility feature, not merely a stylistic choice, and removing it for aesthetics removes information for some visitors.

The general test is to view a page in greyscale, or to imagine it without colour, and ask whether all its information is still available. If an error is still clear from its text, a chart is still readable from its labels, and a link is still identifiable from its underline, the page passes. If any information vanishes with colour, that information needs another channel, and adding that channel usually improves the page for everyone, because text, icons, and labels are clearer than colour alone, even for visitors who can see colour perfectly.

Check: Why must an error not be shown by red colour alone?

Section 20 — Zoom, text resizing and reflow

Visitors with low vision often magnify a page to read it, either by zooming the whole page in the browser, or by increasing the browser's default font size. An accessible page must support both, without breaking its layout or hiding content. That support is measured by a specific WCAG test: at 200% zoom, or with text resized to 200%, the page's content must still be usable, without requiring horizontal scrolling in both directions, and without content being cut off or overlapping. That test is known as reflow, and it is a practical check you can perform yourself.

Browser zoom magnifies everything — text, images, layout — proportionally, so a page that is built fluidly will reflow at 200% zoom much as it does on a narrow phone screen: columns stack, text wraps, and content remains reachable by vertical scrolling. A page built with fixed widths, however, may require horizontal scrolling at 200% zoom, because its fixed boxes no longer fit the magnified viewport, forcing the visitor to scroll sideways to read each line, which is exhausting and error-prone. That is why the responsive techniques from Chapter 12 — fluid widths, flexible grids, and max-width containers — are also accessibility techniques: they make a page reflow at high zoom, just as they make it adapt to a phone.

Text resizing is a related but distinct test. Increasing only the font size, without magnifying images or layout, can break a page whose boxes are sized in fixed pixels, because the larger text overflows its fixed-height containers, overlapping neighbouring content or being cut off. A page built with relative units — rem for text and spacing, and flexible heights that grow with content — handles text resizing gracefully, because its boxes grow with the text inside them. That is another reason to prefer rem and flexible sizing over fixed pixels for text containers, as discussed in earlier chapters.

Testing reflow is straightforward: open your page, zoom to 200% in the browser, and try to complete its key tasks — read the content, navigate, fill in a form — without horizontal scrolling, except for content that genuinely requires two dimensions, such as a data table or a map, which are allowed to scroll in their own container. Then increase the browser's default font size to a large setting, reload, and check again for overlapping or cut-off text. If either test fails, the layout needs attention, usually by replacing fixed widths or heights with fluid ones, or by allowing a component to stack or wrap at the magnified size.

Finally, never disable zoom, as Chapter 12 warned, because zoom is a basic accessibility tool, and blocking it removes a visitor's ability to adapt the page to their vision. A page that supports zoom and reflow respects the visitor's control over their own reading experience, which is the essence of accessibility: the page adapts to the visitor, not the other way around.

Check: What must still work when a page is zoomed to 200%?

Section 21 — Touch and pointer target size

Controls on a touch screen must be large enough to tap accurately, and spaced far enough apart that a tap on one does not accidentally activate its neighbour. That requirement, discussed in Chapter 12 from a responsive perspective, is also an accessibility requirement, because visitors with motor difficulties, tremors, or large fingers, and visitors using a device one-handed or in a moving vehicle, all need larger, better-spaced targets. A tiny link or a densely packed row of buttons is not merely inconvenient; it is a barrier to accurate interaction.

WCAG provides a specific standard for target size: in its most recent version, the minimum target size for the AA level is 24 by 24 pixels, with exceptions for targets that are inline in text, or where the target's size is essential to its function, such as a single character in a text editor. That 24-pixel minimum is a floor, not a goal: larger targets are better, and many design systems aim for 44 pixels, which is a comfortable size for most thumbs and fingers. The key point is that a target smaller than the minimum, especially when surrounded by other small targets, will cause mis-taps, and mis-taps cause errors, frustration, and, for a visitor completing a purchase or a form, abandonment.

Spacing is as important as size. Two targets that are each 24 pixels, but placed only 2 pixels apart, effectively form a single, ambiguous target area, because a finger landing near their boundary may hit either. Providing clear space between targets — through padding, margins, or gaps — ensures that each target's activation area is distinct, and that a tap aimed at one target is unlikely to land on another. That spacing should be considered as part of the target's design, not as wasted space: it is the margin of error that makes accurate tapping possible.

Target size also applies to non-button controls, such as checkboxes, radio buttons, and the handles of sliders, which are often rendered small by browsers by default. Enlarging those controls, or enlarging their labels' clickable areas so that tapping the label toggles the control, makes them usable on touch screens and for visitors with motor difficulties. A checkbox that can only be toggled by tapping its tiny square is a barrier; a checkbox whose label also toggles it is accessible, because the label provides a much larger target.

Testing target size is physical, as with responsive testing: open the page on a phone, hold it as you normally would, and try to tap each control with your thumb, without zooming or taking unusual care. Note every target you miss, every accidental activation, and every control that feels too small or too close to its neighbours. Then enlarge those targets with padding and spacing, and test again. That thumb test, repeated for every key control, is a simple, reliable way to ensure a page's targets are accessible, and it complements the more formal measurement of pixel sizes in developer tools.

Check: Why is spacing between targets as important as their size?

Section 22 — ARIA: the first rule

ARIA, the Accessible Rich Internet Applications specification, provides attributes that can add accessibility information to HTML elements — roles, states, and properties that describe what an element is and how it behaves, for assistive technology. ARIA is powerful, and it is also frequently misused, because it is tempting to add ARIA attributes to fix a problem that should be fixed with native HTML instead. The first rule of ARIA, stated in the specification itself, is: do not use ARIA if a native HTML element or attribute already provides the semantics you need. That rule is worth memorising, because following it prevents a large class of accessibility errors.

The reason for the first rule is that native elements provide more than semantics: they provide keyboard behaviour, focus handling, and browser support that ARIA alone does not. Adding role="button" to a div tells a screen reader that the div is a button, but it does not make the div focusable, activatable with Enter or Space, or operable with a keyboard. To make that ARIA button truly work, you must also add tabindex, keyboard event handlers, focus styles, and pressed-state handling — in other words, you must recreate everything a native <button> provides for free, and any omission in that recreation is an accessibility bug. A native button, by contrast, provides all of it, with no extra work and no risk of omission.

ARIA is therefore best used for cases where no native element exists, or where native semantics need supplementing. A custom tab interface, a tree view, a slider, or a live region announcing dynamic updates are cases where ARIA roles and properties are necessary, because HTML has no native tab or tree element that provides those semantics. In those cases, ARIA should be used carefully, following established patterns for the component, with its keyboard behaviour and focus management implemented alongside its roles, as later sections explain. ARIA used that way fills a genuine gap, rather than duplicating a native feature badly.

A related principle is that ARIA should not override native semantics incorrectly. Adding a role that contradicts an element's nature — for example, role="button" on a link that navigates, or role="heading" on a paragraph — confuses assistive technology, because it announces an element as something it is not, and it may break the browser's built-in behaviour for that element. Similarly, adding aria-label to an element that already has a visible label can create a mismatch between the announced name and the visible text, confusing voice-control users who speak the visible text. ARIA should enhance, not contradict, the underlying HTML.

The practical habit is to ask, for each ARIA attribute you are about to add, whether a native element or attribute already does the job. If it does, use the native one. If it does not, use ARIA, but follow a documented pattern for the component, test it with a screen reader and a keyboard, and be prepared to justify its use. That discipline keeps ARIA as a precise tool for genuine gaps, rather than a spray of attributes that obscures the page's real structure.

Check: What is the first rule of ARIA, and why does it exist?

Section 23 — Accessible names for icon-only buttons

An icon-only button — a button containing only an image, an SVG, or a character such as a magnifying glass or a cross, with no visible text — presents a specific accessibility problem: its purpose is clear to a sighted visitor who recognises the icon, but a screen reader user hears only "button," with no name, unless the button is given an accessible name. That name is what a screen reader announces, and what a voice-control user speaks to activate the button, so it must be present and descriptive, even though it is not visible on screen.

The simplest way to give an icon button a name is with aria-label, which provides a text label for assistive technology without changing the visual appearance:

<button aria-label="Close dialog">
  <svg aria-hidden="true"><!-- close icon --></svg>
</button>

Here, the button's accessible name is "Close dialog," which a screen reader announces as "Close dialog, button," and which a voice-control user can speak to activate it. The SVG itself is marked aria-hidden="true", so the screen reader does not try to announce the graphic's internal details, which would be noise. That combination — a labelled button, a hidden graphic — is the standard pattern for icon buttons, and it should be used for every icon-only control, from search and menu buttons to social sharing icons.

An alternative is to include visually hidden text inside the button, which screen readers announce but sighted visitors do not see. That approach has the advantage that the text is present in the markup as real text, which some assistive technologies handle more reliably than aria-label, and it avoids a mismatch between visible and announced text, because there is no visible text to mismatch. Either approach is acceptable, provided the resulting name is descriptive: "Close," "Search," "Open menu," and "Share on X" are all better than "Button," "Icon," or no name at all, because they say what the button does, not merely that it is a button.

Icon buttons also need the other basics: they must be focusable, which native buttons are, they must have a visible focus indicator, and they must be large enough to tap, as discussed earlier. An icon button that is tiny, unfocusable, or unnamed fails on multiple counts, and fixing only its name leaves the other failures in place. Testing icon buttons with a screen reader, by tabbing to each one and listening to its announcement, is the most reliable way to catch unnamed or vaguely named buttons, because a list of "button, button, button" in a screen reader's output is immediately obvious as a problem, while it is invisible in a visual review.

Finally, consider whether an icon-only button is necessary at all. In many cases, adding a short visible text label alongside the icon — "Search," "Menu," "Close" — makes the button clearer for everyone, including sighted visitors who may not recognise an unfamiliar icon, visitors with cognitive difficulties who benefit from text, and visitors using translation tools that cannot translate an icon. Icon-only buttons should be reserved for cases where space genuinely requires them, and when they are used, their accessible names must be given with the same care as visible labels.

Check: How does aria-label give an icon-only button its name?

Section 24 — aria-hidden for decorative content

Just as decorative images should be skipped by screen readers, decorative elements in general — icons that repeat a label, duplicated content, visual flourishes, and content that is currently hidden — should be hidden from assistive technology when they add no information, or when announcing them would create confusion. The aria-hidden attribute does that: aria-hidden="true" removes an element and its descendants from the accessibility tree, so screen readers ignore them, while leaving them visible on screen for sighted visitors who benefit from them.

The most common use is for decorative icons inside a labelled control, as in the previous section. A button labelled "Search" that contains a magnifying glass icon does not need the icon announced as well, because the icon repeats the label. Marking the icon aria-hidden="true" ensures the screen reader announces only "Search, button," rather than "Search, image, button," or some garbled description of the icon's internal markup. Similarly, a decorative divider, a background pattern, or a repeated logo that adds no new information should be hidden, so the screen reader's output stays focused on content.

aria-hidden must be used with care, however, because hiding an element from assistive technology while leaving it visible and interactive is a serious error. An element that is focusable — a link, a button, a form control — must never be aria-hidden, because a keyboard user could tab to it and focus an element that a screen reader user cannot perceive, or a screen reader user could be unaware of a control that is visually present. That mismatch creates a focus trap in reverse: focus lands on something that, for assistive technology, does not exist. If an element is interactive, it must be perceivable, and if it is not perceivable, it must not be interactive. That rule is absolute, and violating it is a common ARIA error.

Another caution is that aria-hidden on a parent hides all its descendants, even if a descendant has aria-hidden="false". That means you cannot hide a container and then unhide a single element inside it; the parent's hiding wins. To hide only part of a region, apply aria-hidden to the specific elements to be hidden, not to their common parent, or restructure the markup so the hidden and visible parts are siblings. That detail surprises developers who expect aria-hidden="false" to override a parent, and it is worth remembering when hiding decorative parts of a complex component.

Finally, aria-hidden is not a substitute for display: none or visibility: hidden for content that should be hidden from everyone. If content is not relevant to any visitor, it should be removed from the layout or hidden visually as well, not merely hidden from screen readers, because a discrepancy between visual and screen reader content can confuse visitors who use both, such as a sighted screen reader user or a visitor with magnification. Use aria-hidden for content that is visually useful but programmatically redundant, and use visual hiding for content that is useful to no one.

Check: Why must a focusable element never be aria-hidden?

Section 25 — Native controls versus custom div controls

The choice between a native control and a custom control built from generic elements is one of the most consequential accessibility decisions in front-end development, because it determines how much behaviour you must recreate, and how likely you are to recreate it incompletely. A native control — a button, link, input, select, or checkbox — comes with semantics, keyboard behaviour, focus handling, and browser support built in, tested by browser vendors across many assistive technologies. A custom control — a div with click handlers, ARIA roles, and custom keyboard code — must recreate all of that, and every recreation is an opportunity for error.

Consider a toggle switch, a common custom control. Visually, it is a track with a thumb that slides when clicked. Natively, the closest element is a checkbox, which can be styled to look like a switch while retaining its checkbox semantics, keyboard behaviour, and form value. That styled checkbox is accessible with minimal extra work: it can be tabbed to, toggled with Space, announced as a checkbox with its label, and submitted as part of a form. A div-based switch, by contrast, needs a role, a checked state, keyboard handlers for Space and Enter, focus styles, and form integration, all written by hand, and all tested across browsers and screen readers. The styled native control achieves the same visual result with far less risk.

That does not mean custom controls are never appropriate. Some controls have no native equivalent — a tab interface, a tree view, a complex slider, a rich text editor — and those must be built custom, following established ARIA patterns for their type, with full keyboard support and focus management. But the decision to build custom should be deliberate, based on a genuine lack of a native equivalent, not on a preference for a particular visual style that a native control could achieve with CSS. Many visually distinctive controls — switches, custom checkboxes, styled selects — can be built on native elements with CSS, preserving their accessibility while achieving the desired look.

When a custom control is necessary, its implementation must be treated as a significant piece of work, not as a quick div with a click handler. It needs a correct role, a correct accessible name, correct states, keyboard support for all expected keys, visible focus, and testing with at least one screen reader and by keyboard alone. It should also be tested for robustness: does it work when JavaScript fails, does it degrade gracefully, does it submit its value in a form? Those questions are answered automatically for native controls, and must be answered manually for custom ones, which is why custom controls cost more to build and maintain, even when they look simple.

The habit to form is therefore: start with native, style it to match the design, and only go custom when native cannot do the job, with a clear understanding of the work that custom entails. That habit prevents the most common accessibility failures, which are not exotic edge cases, but ordinary buttons, links, and toggles built as divs because no one considered the native alternative.

Check: What behaviour must a custom div button recreate that a native button provides for free?

Section 26 — aria-expanded for toggles and disclosures

Many interface components expand and collapse: a menu button reveals a menu, an accordion header reveals a panel, a "Show more" button reveals additional content, a tree node reveals its children. For sighted visitors, the expanded or collapsed state is visible: the menu is open or closed, the panel is shown or hidden. For screen reader users, that state must be announced, otherwise they cannot tell whether activating a control opened or closed something, or whether content is currently available. The aria-expanded attribute conveys that state, and using it correctly is essential for toggle components.

aria-expanded takes a boolean value: true when the controlled content is expanded and visible, false when it is collapsed and hidden. It is placed on the control that toggles the content, not on the content itself, and it must be updated whenever the state changes, typically by JavaScript toggling the attribute alongside showing or hiding the content:

<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden><!-- links --></ul>

When the button is activated, JavaScript sets aria-expanded to true and removes the hidden attribute from the menu, so the menu becomes visible and its state is announced as expanded. When activated again, it sets aria-expanded to false and hides the menu, so its state is announced as collapsed. That synchronisation — visual state, DOM visibility, and ARIA state all changing together — is what makes the toggle understandable to every visitor. If any of those three drifts out of sync, for example if the menu is shown but aria-expanded remains false, a screen reader user will be misled about the menu's state.

The aria-controls attribute in that example, pointing from the button to the menu's id, is a supplementary hint that identifies which element the button controls, and it is good practice to include it where the relationship is not obvious from the markup. It is not, however, a substitute for aria-expanded, and it does not by itself make the toggle accessible: the state attribute is the essential part, because it is what is announced. Similarly, aria-expanded should not be used on elements that do not actually expand or collapse content, such as a link that navigates to another page, because announcing an expanded state for a navigation link would be misleading.

Toggles should also be operable by keyboard, which native buttons are, and their expanded content should be reachable by keyboard when expanded and unreachable when collapsed, which hiding with hidden or display: none ensures, because hidden content is removed from the tab order and the accessibility tree. A toggle that shows content visually but leaves it in the tab order when collapsed, or that hides content visually but leaves it tabbable, fails that expectation, however correct its aria-expanded value is. State, visibility, and tab order must all agree, and testing a toggle by keyboard and screen reader, in both states, is the way to verify that agreement.

Check: What must happen to aria-expanded when a menu opens?

Section 27 — Live regions for dynamic updates

Much of the web is dynamic: a form submission shows a confirmation, a search returns results, a chat receives a message, a cart updates its total, a timer counts down. For sighted visitors, those updates are visible as they happen, because they are looking at the page. For screen reader users, those updates are invisible unless they are announced, because a screen reader does not automatically read content that changes elsewhere on the page, while the user is focused elsewhere. Live regions solve that problem by marking a region of the page as one whose changes should be announced.

A live region is created with the aria-live attribute, which takes a politeness value: polite, assertive, or off. A polite live region announces its changes when the screen reader is idle, without interrupting the user's current reading, which suits most updates, such as a search result count, a form confirmation, or a cart total. An assertive live region interrupts the current reading immediately, which suits urgent updates, such as an error that must be acted on, or a session timeout warning. Assertive should be used sparingly, because interrupting a user is disruptive, and overusing it trains users to ignore interruptions, or causes them to lose their place in the content they were reading.

A typical live region looks like this:

<div aria-live="polite" id="status"></div>

JavaScript updates the region's text when something happens — for example, setting it to "3 results found" after a search — and the screen reader announces that text, because the region is live. The region should be present in the page from the start, even if empty, because a region added dynamically at the moment of the update may not be recognised as live by all screen readers. Its content should also be concise and meaningful on its own, because it is announced out of context: "Item added to cart" is clear, while "Added" alone is ambiguous, and "3" alone is meaningless.

Live regions must be used judiciously, because announcing every change on a page would be noisy and overwhelming. A timer that updates every second should not be a live region, or it would announce every second, drowning out everything else. A frequently updating stock price, a typing indicator, or a progress percentage should either not be live, or should be live with throttling, announcing only significant changes, such as a completed task, rather than every intermediate value. The judgement is about importance: announce changes the user needs to know about to continue their task, and let unimportant changes pass silently, to be discovered when the user navigates to them.

Finally, live regions are not a substitute for focus management. If an update requires action — for example, an error that must be corrected — moving focus to the error, or to a summary, may be more appropriate than merely announcing it, because focus gives the user a place to act from, while an announcement alone leaves them to find the error themselves. Live regions and focus management complement each other: live regions announce, focus management positions, and together they make dynamic content accessible.

Check: When should a live region be polite rather than assertive?

Section 28 — Accessible tables

Tables present data in rows and columns, and their accessibility depends on preserving the relationships between those rows, columns, and their headers, so that a screen reader user navigating cell by cell understands what each value means. A sighted visitor sees a table's structure at a glance: the header row at the top, the header column at the side, and the grid that connects them. A screen reader user encounters the table one cell at a time, and without proper markup, they hear only values — "12, 34, 56" — with no indication of what those values represent. Proper table markup supplies that indication.

The foundation is the <th> element for header cells, as opposed to <td> for data cells. A header cell is not merely a bold data cell; it is a cell that labels a row or a column, and screen readers use it to announce context as the user navigates. When a user moves to a data cell, the screen reader announces its row and column headers alongside its value, so "34" becomes "Price, Widget, 34," with the headers providing the meaning. That announcement depends on headers being marked with <th>, and on their scope being clear, which is where the scope attribute helps: scope="col" for a header that labels a column, and scope="row" for a header that labels a row. For simple tables, browsers can infer scope, but stating it explicitly is clearer and more reliable.

A table should also have a <caption>, which is its title, placed as the first child of the table, describing what the table is about: "Monthly sales by region, 2025," for example. The caption is announced when the user enters the table, giving them context before they navigate its cells, in the same way a heading gives context to a section. Without a caption, a user entering a table with no introduction may not know what data it contains, especially if the table is one of several on a page. A caption is therefore not decorative; it is the table's introduction, and it should be concise and descriptive.

Complex tables, with multiple header rows, merged cells, or nested headers, need additional care, using id and headers attributes to associate each data cell with its specific headers, because simple scope may not capture the full relationship. Those tables are rare in most sites, and if a table is that complex, it is worth asking whether it can be simplified, split into smaller tables, or presented as a list or chart with a data table alternative, because complexity in a table is complexity for every visitor, not only for screen reader users. Finally, tables should be used for data, not for layout. Using a table to arrange a page's columns — a practice from the early web — confuses screen readers, which announce it as data with headers and relationships that do not exist, forcing users to navigate a layout as if it were data. Modern CSS layout, with flexbox and grid, has replaced layout tables entirely, and any remaining layout table should be replaced. A table's semantics should mean "this is data," and using it for anything else undermines that meaning for everyone.

Check: Why must header cells use <th> rather than bold <td> cells?

Section 29 — Lists, navigation and semantic grouping

Lists are one of the most useful semantic structures in HTML, because they convey that a set of items belongs together, and screen readers announce that grouping, including how many items a list contains, as the user navigates it. A navigation menu marked as a list — a <ul> with <li> items, each containing a link — is announced as a list of, say, six items, so the user knows how many destinations there are and can navigate them as a group. A menu built as a series of ungrouped links, or as div elements, loses that grouping information, and the user encounters the links as isolated items with no sense of their total or their relationship.

The same principle applies to other groupings. A list of features, a set of steps in a process, a group of related articles, or a breadcrumb trail showing a page's location in a site hierarchy are all lists in meaning, even if they are styled to look like cards, steps, or a horizontal trail. Marking them as lists — ordered <ol> for sequences where order matters, such as steps, and unordered <ul> for sets where order does not matter, such as features — preserves their meaning for assistive technology, while CSS controls their visual appearance. That separation of meaning and appearance is the same principle as headings and semantic controls, applied to grouping.

Navigation landmarks and lists work together. A <nav> element containing a list of links gives a screen reader user two levels of structure: the landmark says "this is navigation," and the list says "this navigation contains these items." That combination is more navigable than either alone, because the user can jump to the navigation landmark, then navigate its list, counting items and moving between them efficiently. A navigation region without a list, or a list without a landmark, provides only half that structure, and is less efficient to navigate, especially on a site with many links.

There are also cases where a list should not be used, because the items do not form a meaningful group. A set of unrelated paragraphs, or a layout grid of cards that are not a list in content terms, should not be forced into a list merely to gain list semantics, because that would mislead a screen reader user about their relationship. The test is whether the items would make sense as a list if read aloud as a list: "Features: fast, reliable, secure" makes sense; "Page: header, hero, footer" does not, because those are sections, not list items. Applying that test honestly keeps list semantics meaningful, rather than diluting them through overuse.

Finally, lists should be properly nested and closed, with each item in an <li>, and with no stray text directly inside a <ul> or <ol> outside an item. Invalid list markup can confuse screen readers, which may announce incorrect item counts or fail to recognise the list at all. Valid, semantic lists are a small investment with a large return in navigability, and they are a habit worth forming for every grouping on a page, from navigation to features to steps.

Check: Why should a navigation menu be marked up as a list?

Section 30 — Language, page title and document basics

Two small document-level details have an outsized effect on accessibility: the page's language, declared with the lang attribute, and its title, declared with the <title> element. Both are easy to get right, easy to forget, and consequential when forgotten, because they affect how assistive technology presents the page from the very first moment a visitor arrives.

The lang attribute, placed on the <html> element, tells browsers and screen readers which language the page is written in, so they can pronounce its text correctly, apply the right hyphenation and spelling rules, and choose the appropriate voice or Braille table. A page in English declared as <html lang="en"> will be read with English pronunciation; the same page without a lang attribute may be read with a default voice that mispronounces words, or with a voice for the wrong language, making it difficult to understand. That mispronunciation is not a minor annoyance; for a screen reader user, pronunciation is comprehension, and a wrong language setting can render a page effectively unreadable, even though its text is perfectly clear visually.

If a page contains a passage in a different language — a quotation in French on an English page, for example — that passage should have its own lang attribute, such as <span lang="fr">, so the screen reader switches voice for that passage and then switches back. That switching is automatic when the markup is correct, and it is a courtesy that makes multilingual content accessible, rather than forcing a single voice to struggle through multiple languages. The same principle applies to any embedded content in another language, from a video title to a product name, where the language differs from the page's main language.

The <title> element, in the page's head, provides the page's title, which is announced when the page loads, shown in the browser tab, and used in bookmarks and search results. For a screen reader user, the title is often the first thing they hear on arriving at a page, and it tells them whether they are on the right page before they begin navigating it. A descriptive, unique title — such as "Delivery options — Example Shop" — confirms the page's identity immediately, while a vague title — such as "Home" or "Page" — or a title that is the same on every page of a site, leaves the user uncertain about where they are, and forces them to navigate the page to discover its purpose. Titles should therefore be unique per page, descriptive of that page's content, and ordered with the specific page first and the site name second, so the most distinguishing information is heard first.

Together, language and title are the page's introduction to assistive technology, and getting them right costs only two lines of markup. Forgetting them, by contrast, undermines comprehension and orientation from the start, which is why they are checked first in many accessibility audits, and why they belong in every page template, not added as an afterthought to individual pages.

Check: Why does a missing lang attribute make a page harder for a screen reader user?

Section 31 — Motion, flashing and vestibular safety

Motion on a page can be more than distracting; for some visitors, it can cause physical discomfort, disorientation, or, in rare cases, seizures. Visitors with vestibular disorders — conditions affecting balance and spatial orientation — may experience nausea, dizziness, or disorientation from large, sweeping, or parallax motion, from autoplaying videos, or from content that moves unexpectedly. Visitors with photosensitive epilepsy may be at risk from flashing content, particularly flashes that occur more than three times per second and that cover a large area of the screen. The most important safeguard against seizures is to avoid flashing content that exceeds the WCAG thresholds: flashes more than three times per second, especially red flashes or high-contrast flashes over a large area, should not be present on a page. That rule is absolute for content that the author controls, and it applies to animations, videos, and interactive effects. If a page must contain flashing content for a legitimate reason — for example, a video of a concert with strobe lighting — it should carry a warning before the content, and ideally an alternative version without flashing, so visitors at risk can choose safely. Most pages, however, can simply avoid flashing altogether, which is the safest and simplest approach.

For vestibular safety, the safeguards are broader and more nuanced. Large parallax scrolling, where background and foreground move at different speeds, can trigger disorientation, because it mimics the visual cues of movement while the visitor is stationary. Autoplaying videos that move across the screen, carousels that slide continuously, and animations that simulate depth or rotation can have similar effects. The prefers-reduced-motion media query, covered in Chapter 13, is the primary tool for respecting visitors who have asked for reduced motion, by disabling or simplifying those effects for them. But even for visitors who have not enabled that setting, motion should be used sparingly, with large or continuous motion reserved for cases where it genuinely serves the content, and with a way to pause or stop it, such as a pause button on a carousel or a video.

A further consideration is motion triggered by scrolling. Scroll-triggered animations that move content as the visitor scrolls can be disorienting, because the visitor's scrolling action produces unexpected movement elsewhere on the page, breaking the connection between their action and the page's response. If scroll animations are used, they should be subtle, should not move content large distances, and should respect reduced motion by being disabled for those visitors. Testing scroll animations with reduced motion enabled, and with a vestibular perspective in mind — asking whether the motion could make someone dizzy — is a useful discipline, even if it feels unfamiliar at first.

Finally, motion safety should be considered at the design stage, not after a page is built, because removing a large motion effect late in a project can require redesigning a component. Asking, for each proposed animation, whether it could cause discomfort, whether it is essential, and whether it can be paused or reduced, is a small addition to the design process that prevents harm and improves the experience for visitors who are sensitive to motion, as well as for visitors who simply find excessive motion distracting.

Check: Why must flashing content more than three times per second be avoided?

Section 32 — Captions, transcripts and audio description

Video and audio content must be accessible to visitors who cannot hear or see it, and that requires alternatives in other forms: captions for those who cannot hear, transcripts for those who cannot hear or who prefer to read, and audio description for those who cannot see the visual content. Providing those alternatives is not an optional extra for video; it is what makes video content accessible at all, because a video without captions is silent to a deaf visitor, and a video without audio description is a series of unexplained sounds to a blind visitor.

Captions are text versions of a video's spoken content, synchronised with the video so they appear as the words are spoken, and they should also include important non-speech sounds, such as "[music playing]" or "[door slams]," where those sounds carry meaning. Captions differ from subtitles in that subtitles typically translate spoken content into another language, while captions are in the same language and include sound information. Captions should be accurate, synchronised, and complete, covering all spoken content, not merely the main dialogue, because a caption that omits a speaker's words or lags behind them is confusing and unhelpful. Automatic captions generated by a platform are a starting point, but they should be reviewed and corrected, because their errors — misheard words, missing punctuation, wrong speaker attribution — can change meaning.

Transcripts are text versions of an entire audio or video piece, including spoken content and descriptions of important visual or sound information, presented as a document that can be read independently of the media. A transcript serves visitors who cannot play the media at all, visitors who prefer to read or search text, and visitors using assistive technology that cannot handle video. It also serves search engines and visitors on slow connections, who may prefer to read a transcript rather than stream a video. A transcript should be placed near the media, clearly linked, and should be complete, not a summary, because a summary omits details that a visitor may need.

Audio description is a spoken narration of a video's visual content — actions, settings, facial expressions, on-screen text — inserted into pauses in the dialogue, so a blind visitor can follow the story or the explanation. It is most important for videos where visual information carries meaning that is not spoken, such as a drama, a demonstration, or a presentation with slides. For a video that is essentially a talking head with no important visual action, audio description may be less critical, because the spoken content already conveys the meaning, but for any video where seeing matters, description is necessary. Providing audio description requires planning during production, because it needs pauses in the dialogue to fit the narration, which is another reason to consider accessibility at the start of a video project, not after it is finished.

Together, captions, transcripts, and audio description ensure that a video's information is available through hearing, sight, and reading, in combination, so no visitor is excluded by the medium alone. A video that offers all three is a video that welcomes every visitor, and a video that offers none is a video that excludes many, however polished its visuals or audio may be.

Check: What is the difference between captions and a transcript?

Section 33 — Autoplay, audio and unexpected sound

Content that plays automatically — a video that starts on page load, background music, an advertisement with sound — can be startling, disruptive, and, for some visitors, a barrier. A screen reader user, for example, relies on audio to navigate a page, and unexpected sound from autoplay competes with the screen reader's voice, making it difficult to hear either. A visitor in a quiet office, a library, or a shared space may be embarrassed by sudden sound, and a visitor with a cognitive difficulty may be distracted or distressed by unexpected audio. For those reasons, autoplay, especially autoplay with sound, should be avoided or carefully controlled.

The WCAG standard addresses this directly: if audio plays automatically for more than three seconds, there must be a mechanism to pause or stop it, or to control its volume independently of the overall system volume. That mechanism must be easy to find and operate, not hidden in a menu or requiring precise mouse control. In practice, the safest approach is to not autoplay sound at all, and to let the visitor start media deliberately, by pressing play, knowing what will happen. That deliberate start respects the visitor's control over their environment, which is a core accessibility principle: the page should not impose sound, motion, or other sensory input on a visitor who did not request it.

Autoplaying video without sound is less disruptive than video with sound, but it can still be problematic, particularly if it moves, flashes, or distracts from the page's content, as discussed in the previous section on motion. A video that autoplays silently in a hero section, for example, may draw attention away from the page's heading and actions, and it may cause vestibular discomfort for sensitive visitors. If silent autoplay is used, it should be subtle, should not contain flashing, and should be pausable, with a clear pause control. Better still, it should be replaced with a static image and a play button, so the visitor chooses to watch, rather than having motion imposed on them.

Background audio is a particular trap, because it is often added for atmosphere, with no consideration for visitors who need quiet to use a screen reader or to concentrate. Background music that cannot be easily stopped, or that has no volume control, fails the WCAG requirement and fails the visitor's need for control. If background audio is genuinely part of a page's purpose — for example, a music site — it should still have prominent, accessible controls, and it should not start until the visitor has indicated they want it, by entering that section or pressing play.

The general principle is control: visitors should control when media plays, how loud it is, and when it stops, through controls that are accessible by keyboard, clearly labelled, and easy to find. A page that respects that control is a page that respects its visitors' environments, attention, and assistive technology, and it avoids the common failure of a page that shouts before it has been invited to speak.

Check: Why is autoplay with sound especially harmful for screen reader users?

Section 34 — Placeholders are not labels

A placeholder is the faint text inside a form field that suggests what to enter, such as "name@example.com" inside an email field, or "Search" inside a search box. Placeholders are useful as examples, but they are not labels, and treating them as labels is a common accessibility error that causes several problems, even though it may look tidy to a designer who wants a clean, minimal form.

The first problem is disappearance. Placeholder text vanishes as soon as the visitor starts typing, so a visitor who forgets what a field was for, or who is interrupted mid-form, cannot check the placeholder again without deleting their input. That is particularly problematic in long forms, or for visitors with memory or cognitive difficulties, who may need to refer back to a field's purpose while typing. A visible label, by contrast, remains present throughout, so the visitor can always check what is asked, however long they take.

The second problem is announcement. Screen readers do not reliably announce placeholder text as a field's label, and even when they do, they may announce it only once, or in a way that is easily confused with the field's value. A field whose only identification is its placeholder may therefore be announced as blank, or as having a value it does not have, leaving a screen reader user unsure what to enter. A proper label, associated as discussed earlier, is announced reliably as the field's name, every time the field is encountered, which is the behaviour a form needs.

The third problem is contrast and readability. Placeholder text is typically shown in a light grey, with lower contrast than normal text, to distinguish it from entered values. That low contrast can make it hard to read for visitors with low vision, and it can be mistaken for a pre-filled value, leading a visitor to submit a form thinking a field is already completed, when it is in fact empty. A label, shown in normal text contrast, does not have those problems, because it is clearly a label, not a value, and it is clearly readable.

Placeholders still have a legitimate use, as examples or hints, alongside a proper label. An email field labelled "Email address" with a placeholder "name@example.com" shows both the purpose and an example format, which is helpful. A search field labelled "Search" with a placeholder "Search products, articles, help" shows the scope of the search. In those cases, the placeholder supplements the label, rather than replacing it, and the form remains accessible. The rule to remember is therefore: placeholder as hint, label as identity, never placeholder alone. A form that follows that rule is clearer for every visitor, not only for those using assistive technology.

Check: Name two problems with using a placeholder as a field's only label.

Section 35 — Required fields and clear indication

A form must make it clear which fields are required and which are optional, so visitors do not waste time guessing, submit a form only to be told a field they thought was optional is required, or omit a field they did not realise was necessary. That clarity must be conveyed in more than one way, because a single indicator — such as a red asterisk — may be missed or misunderstood by some visitors, just as colour alone is insufficient for errors.

The most common indicator is an asterisk, *, placed after a required field's label, with an explanation at the top of the form, such as "* Required fields." That explanation is important, because an asterisk without it is merely a symbol whose meaning must be guessed, and a visitor unfamiliar with the convention may not know it means required. The explanation should be present, visible, and placed where a visitor encounters it before filling in the form, so they understand the convention from the start, rather than discovering it through trial and error.

For screen reader users, the asterisk alone is not sufficient, because it may be announced as "star" without meaning, or it may be missed entirely if it is added via CSS rather than in the label text. A more reliable method is to use the required attribute on the control itself, which browsers and screen readers understand natively: a required field is announced as required, and browsers will prevent submission until it is filled, with a message that, while browser-dependent, at least indicates the field is needed. Combining the required attribute with a visible asterisk and an explanation gives both visual and programmatic indication, which is the robust approach. The aria-required="true" attribute can also be used, particularly when native validation is not used, but the native required attribute should be preferred where possible, because it provides both announcement and validation.

Optional fields should also be indicated, especially in a form where most fields are required. Marking optional fields as "(optional)" in their labels prevents a visitor from assuming every unmarked field is required, or from filling in optional fields unnecessarily. That clarity is particularly helpful in long forms, where the distinction between required and optional can significantly affect how long the form takes and how much personal information a visitor feels obliged to provide. A form that marks only required fields, without marking optional ones, leaves ambiguity in a mostly-required form, while a form that marks optional fields explicitly removes it.

Finally, required indication should be consistent throughout a form, and should not change meaning between sections. If an asterisk means required in one section, it must mean required everywhere, and if a field is required, its indication should be present in its label, not only in a separate instruction elsewhere. Consistency in indication, like consistency in navigation and labelling, reduces cognitive load and errors, and it is a small detail that makes a form feel trustworthy and professional, rather than confusing and arbitrary.

Check: Why is an asterisk alone not enough to indicate a required field?

Section 36 — Avoiding keyboard traps

A keyboard trap occurs when a visitor using a keyboard enters a component or a region of a page and cannot leave it by keyboard alone, because focus cycles within the component indefinitely, or because the only way out requires a mouse. That is a serious accessibility failure, because it can leave a visitor stuck, unable to continue using the page, and unable to reach content beyond the trap, including, in extreme cases, the browser's own controls if the trap is severe. The WCAG standard states the rule plainly: if focus can be moved to a component with a keyboard, it must also be possible to move focus away from that component with a keyboard, using standard methods, unless the trap is a deliberate, documented exception, such as a modal dialog that traps focus intentionally until it is closed.

The most common accidental trap involves embedded content, such as a media player, a map, a game, or a third-party widget, that captures keyboard input for its own controls and does not release it. A visitor tabbing into such a widget may find that arrow keys, which previously moved focus, now control the widget, and that Tab no longer moves to the next element, because the widget has intercepted it. That trap is often invisible to mouse users, who can simply click elsewhere to escape, but it is a dead end for keyboard users, who have no mouse to click with. Another common trap involves custom components, such as a carousel, a tab interface, or a date picker, that implement arrow-key navigation but forget to allow Tab to exit. A carousel that lets a visitor move between slides with arrow keys, but that does not let Tab move past the carousel to the next section, traps the visitor inside the carousel, forcing them to cycle through slides indefinitely. The fix is to ensure that Tab, and Shift+Tab for reverse movement, always move focus out of a component to the next focusable element in the page, unless the component is a modal dialog, where trapping is intentional and Escape provides the exit. A modal traps focus intentionally, so a visitor cannot tab to the page behind it while it is open, but it must provide an Escape key or a close button that releases the trap and returns focus to the opener. A modal without an Escape route, or without a visible close control that is keyboard accessible, is not a deliberate trap; it is an accidental one, because the visitor cannot leave it by any keyboard method. That is why dialog focus management, covered earlier, always includes an exit, and why testing a dialog by keyboard must include closing it and confirming focus returns correctly.

Testing for traps is simple: tab through the entire page, including every component and embedded widget, and check that you can always move forward and backward, and that you never get stuck in a loop from which Tab cannot escape, except in a modal that you can close with Escape. If you find a trap, the fix is usually to restore Tab's normal behaviour, or to add an explicit keyboard exit, and to document that exit for visitors who need it.

Check: What makes a keyboard trap different from a modal dialog's intentional focus trap?

Section 37 — Custom focus styles that remain visible

Earlier, this chapter warned against removing the browser's default focus outline without a replacement. This section covers the positive side of that advice: how to design a custom focus style that is visible, attractive, and consistent with a site's design, so that keeping focus visible feels like a design choice rather than a compromise. A well-designed custom focus style can be more visible and more beautiful than the default, and it can reinforce a site's brand, while still serving keyboard users clearly.

A good custom focus style has three qualities: it is obvious, it contrasts, and it is consistent. Obvious means a visitor tabbing through a page can immediately tell which element is focused, without hunting for a subtle change. A 2-pixel solid outline, offset slightly from the element so it does not merge with its border, is obvious. A 1-pixel change in background colour, or a faint shadow, is not, because it may be missed, especially on a busy background or at a glance. Contrast means the focus style stands out against the element and its surroundings, in both light and dark contexts, and against images if the element sits on one. A dark outline on a dark button, or a light outline on a light background, fails contrast, however well-designed it looks in isolation. Consistency means the same focus style, or a coherent family of styles, is used throughout the site, so visitors learn to recognise focus quickly, rather than encountering a different indicator on every component.

The :focus-visible pseudo-class, introduced earlier, is the key to designing a focus style that serves keyboard users without annoying mouse users. By applying a strong focus style only on :focus-visible, you ensure that keyboard navigation shows a clear indicator, while mouse clicks, which already show the visitor where they clicked, do not add an unnecessary outline. That distinction addresses the aesthetic concern that originally led many designers to remove outlines: they were seeing outlines on mouse clicks, where they felt redundant, and removing them for all input, where they are essential for keyboard. :focus-visible separates those cases, and it is now well supported in modern browsers, making it the recommended approach for custom focus styles.

Custom focus styles should also consider the element's shape and context. A circular button needs a circular or rounded focus indicator, not a rectangular one that looks mismatched. A link inside a paragraph needs an outline that does not obscure the surrounding text, perhaps with an offset so it sits outside the text. A card that is itself a link needs a focus style that indicates the whole card, not just its text. Those details require testing in context, by tabbing through real pages with real content, rather than by designing a focus style in isolation and assuming it works everywhere.

Finally, custom focus styles should be tested with the same rigour as other styles: tab through the page, check every component, check light and dark themes, and check high-contrast mode if possible, where the browser may override colours. A focus style that passes those tests is a design asset, not a burden, and it demonstrates that accessibility and aesthetics can reinforce each other, rather than competing.

Check: How does :focus-visible help design a better focus style?

Section 38 — Testing with a screen reader

Automated tools can catch many accessibility errors, but they cannot tell you what a page actually sounds like to a screen reader user, or whether its structure makes sense when heard rather than seen. Testing with a screen reader — listening to a page as a screen reader user would hear it — is therefore an essential part of accessibility work, even if you do it only briefly and with a free screen reader, because it reveals problems that no automated check can detect, such as vague link text in a links list, headings that do not form a sensible outline, or a form whose labels are announced incorrectly.

The most accessible screen readers to start with are those built into operating systems: VoiceOver on macOS and iOS, Narrator on Windows, and TalkBack on Android. NVDA, a free, open-source screen reader for Windows, is also widely used and is a good choice for testing on a Windows machine. Each has its own keyboard commands for navigating by heading, by link, by landmark, and by form control, and learning even a few of those commands — jump to next heading, list links, list landmarks — will let you test a page's structure quickly, without becoming an expert screen reader user. The goal is not to simulate blindness, which is impossible in a brief test, but to hear how your page's structure is announced, and whether that announcement makes sense.

A basic screen reader test follows a simple script. First, listen to the page's title and its first headings, to check orientation: does the title identify the page, do the headings outline its content? Second, list the headings and the links, to check whether they are descriptive out of context, as discussed earlier. Fourth, fill in a form, listening to each field's label, help text, and error message, to check whether the form is understandable without sight. That script, run on a key page, will reveal most structural accessibility problems, even to a beginner tester.

Screen reader testing also reveals the difference between a page that is technically accessible and a page that is pleasantly accessible. A page with correct headings but verbose, repetitive alt text may pass automated checks yet be tedious to listen to, because every decorative image is described at length. A page with correct labels but a confusing tab order may be usable yet frustrating, because focus jumps around illogically. Hearing those frustrations yourself, even briefly, builds empathy for screen reader users and motivates fixes that automated tools would never suggest, such as shortening alt text, improving heading wording, or reordering content for a more logical listening experience.

Finally, screen reader testing should be done early and often, not only at the end of a project, because structural problems found late are more expensive to fix. Adding a screen reader check to your routine — after building a new component, after changing a form, after restructuring a page — catches problems while the component is still fresh in your mind, and it builds a habit of listening to your pages, not merely looking at them, which is a habit that will improve every page you build.

Check: What are the first three things to listen for in a screen reader test?

Section 39 — Testing by keyboard alone

Keyboard testing is the counterpart to screen reader testing, and it is even simpler to perform, because it requires no additional software: you put the mouse aside, use only the keyboard, and try to complete every task on a page. That test reveals whether a page is operable without a mouse, whether its tab order is logical, whether its focus indicators are visible, and whether any component traps focus or requires a mouse-only interaction, such as hovering to reveal a menu or dragging to reorder items.

A thorough keyboard test follows the page's key tasks in order. Start at the top of the page and tab forward through every interactive element, noting the order in which they are reached and whether that order matches the visual layout. Activate each link and button with Enter or Space, as appropriate, and check that each does what it promises. Open each menu, dialog, and disclosure, navigate within it with arrow keys where appropriate, close it with Escape, and check that focus returns sensibly. Fill in each form, using Tab to move between fields, Space to toggle checkboxes, and arrow keys for radio buttons and selects, and submit the form, checking that errors are announced and focus moves to them. Then tab backward with Shift+Tab, to check that reverse navigation works as well as forward navigation, because visitors do not only move forward through a page.

During that test, watch for specific failures. Focus disappearing — tabbing to an element that shows no focus indicator, or to a hidden element — indicates a focus or visibility problem. Focus jumping — tabbing from one element to a distant, unrelated element, skipping intervening content — indicates a tab order problem, often caused by HTML order differing from visual order, or by a positive tabindex. A control that cannot be activated with the expected key — a button that does not respond to Space, a link that does not respond to Enter — indicates a custom control problem. A component that cannot be left by keyboard indicates a trap, as discussed earlier. Each of those failures is a concrete bug to fix, with a concrete test to verify the fix.

Keyboard testing also reveals the experience of keyboard navigation, not merely its possibility. A page where every task is possible, but where reaching the main content requires tabbing through fifty navigation links on every page, is technically keyboard accessible yet practically unusable, which is why a skip link, covered earlier, matters. A page where focus indicators are faint or inconsistent is technically focusable yet hard to use, which is why custom focus styles matter. Testing by keyboard alone, with attention to that experience, not merely to possibility, is what turns a page from keyboard-accessible in theory to keyboard-usable in practice.

Finally, keyboard testing should be done on real pages, with real content, not only on isolated components, because a component that works alone may fail in context, when its tab order interacts with surrounding content, or when its focus management conflicts with another component's. Integrating keyboard testing into your regular workflow — after building a feature, before shipping a page — ensures that keyboard operability remains a property of the whole site, not merely of its individual parts.

Check: What should you do with the mouse during a keyboard test, and why?

Section 40 — Automated tools and their limits

Automated accessibility tools — such as axe, Lighthouse, WAVE, and the accessibility panels in browser developer tools — are valuable for catching a large proportion of common accessibility errors quickly, including missing alt text, insufficient contrast, missing labels, incorrect heading order, and invalid ARIA. Running such a tool on a page takes seconds, and its report gives a concrete list of issues to fix, with explanations and suggested remedies, which is an excellent starting point for an accessibility audit, especially for a beginner who is still learning what to look for.

Automated tools have clear limits, however, and understanding those limits is essential to using them well. A tool can check whether an image has alt text, but it cannot judge whether that alt text is accurate, descriptive, or helpful; an image with alt text reading "image" will pass the check, even though it is useless to a screen reader user. A tool can check whether a form field has a label, but it cannot judge whether that label is clear, specific, or correctly worded. A tool can check contrast ratios, but it cannot judge whether a page's overall design is readable, or whether its language is plain. In general, automated tools catch errors of presence — missing attributes, invalid markup, measurable ratios — but not errors of quality — vague wording, illogical order, confusing interaction — and quality errors are often the ones that most affect real visitors.

Automated tools also cannot test keyboard operability fully, screen reader experience, focus management, or the logic of a dynamic interaction, because those require human judgement and interaction. A tool may report that a custom control has a correct role, but it cannot tell whether that control can be operated with the expected keys, or whether its focus moves sensibly when it opens a dialog. A tool may report no errors on a page that is nevertheless unusable by keyboard, because its tab order is illogical, or by screen reader, because its headings form a nonsensical outline. The correct relationship to automated tools is therefore: use them early and often, as a first pass that catches the easy, measurable errors, and then follow up with manual testing — keyboard, screen reader, zoom, and visual review — to catch the quality and interaction errors they cannot. A page that passes automated checks but fails manual testing is not accessible, while a page that passes manual testing but has a few automated warnings may still be accessible, if those warnings are false positives or minor issues. The tool's report is a guide, not a verdict, and its "100% accessible" score, if it offers one, should never be taken as proof of accessibility, because no automated tool can prove a page is accessible; it can only prove that it found no errors of the kinds it checks for. Running a tool after each significant change, or as part of a build process, catches regressions early, when they are cheap to fix, and it builds a habit of checking accessibility continuously, rather than treating it as a one-time gate before launch. That habit, combined with manual testing, is what produces genuinely accessible pages, not merely pages that pass a tool's checks.

Check: Why can an automated tool not prove a page is fully accessible?

Section 41 — Checking contrast in practice

Knowing the contrast standards from earlier in this chapter is one thing; checking a page's actual contrast, in all its states and contexts, is another, and it requires a practical workflow that goes beyond reading colour values from a stylesheet. Contrast is a property of a specific text colour against a specific background, at a specific size and weight, in a specific state, and a page may have dozens of such combinations, each needing verification. A systematic check prevents the common failure where body text passes but button text, link text, or disabled text fails, unnoticed.

The workflow begins with an inventory: list every text colour used on the page, and the background it sits on, in each state. Body text on the page background, headings on their backgrounds, links in normal, hover, and visited states, button text on button backgrounds in normal, hover, focus, and disabled states, form labels, help text, error text, and placeholder text all count, because each is text a visitor must read. That inventory can be built by inspecting the stylesheet, or by using a browser extension or developer tool that extracts colour pairs, but it should be complete, because an unchecked combination is an unchecked risk. Enter the foreground and background colours, and the tool calculates the ratio and tells you whether it passes AA for normal and large text. For large text, remember the lower threshold of 3 to 1, but do not use that threshold as an excuse to use low contrast for normal text by making it slightly larger; large text is defined specifically, and most body text does not qualify. If a pair fails, adjust it by darkening the text or lightening the background, in small steps, until it passes, and then check the adjusted colours in context, because a darker text colour may change the design's feel, and a lighter background may affect other elements on the same background. Text over an image, text over a gradient, or text over a patterned background has a varying background, and its contrast depends on the lightest or darkest part of that background behind the text. In those cases, the safest approach is to add a solid overlay behind the text, or a text shadow, or to place the text on a solid panel, so its contrast is predictable. Testing text over images by eye, on different images and at different screen sizes, is also necessary, because an image that is dark in one place may be light in another, and text that passes over the dark part may fail over the light part.

Finally, contrast checking should be repeated when a design changes, when a new theme is added, or when content is updated, because a colour that passed in one context may fail in another. Integrating contrast checks into a design system's documentation — listing each colour pair and its ratio — makes those rechecks easy, and it signals to future developers that contrast is a maintained property of the system, not a one-time fix. A page whose contrast is checked systematically, in every state, is a page whose text is readable to the widest range of visitors, which is the point of the exercise.

Check: Why must contrast be checked in every state, not just for body text?

Section 42 — Disabled controls: disabled versus aria-disabled

A disabled control — a button that cannot currently be used, a field that is not applicable in the current context — presents a specific accessibility challenge, because it must communicate both that it exists and that it cannot be used, without misleading visitors about why, or trapping them in a state they cannot escape. HTML provides two mechanisms for disabling, and understanding the difference between them is essential to handling disabled controls correctly.

The native disabled attribute, applied to a button, input, or select, makes the control non-interactive: it cannot be focused, cannot be activated, and, for form controls, its value is not submitted with the form. For a sighted mouse user, that fading is clear, but for a keyboard user, a disabled control that cannot be focused is invisible in the tab order, which can be confusing: a visitor tabbing through a form may wonder why a control they saw visually is skipped, or may not realise it exists at all. For a screen reader user, a disabled control may be announced as disabled if it is encountered, but if it cannot be focused, it may be skipped entirely in normal navigation, so the user may not know it exists or why it is disabled.

The aria-disabled="true" attribute, by contrast, marks a control as disabled for assistive technology, while keeping it focusable and, for custom controls, potentially still interactive in a limited sense. A control with aria-disabled can be tabbed to, so a keyboard user encounters it, hears that it is disabled, and can understand its presence and state, rather than having it silently skipped. That focusability is an advantage for discoverability, but it requires additional handling: the control's activation must be prevented by JavaScript, because aria-disabled alone does not prevent clicking or submitting, unlike the native disabled attribute. For native controls, combining disabled with an explanation elsewhere is often simpler, while for custom controls, aria-disabled with JavaScript prevention is the standard pattern. A button disabled because a form is incomplete should be accompanied by text explaining what is missing, or the form should allow submission and then show errors, which is often a better pattern, because it lets the visitor discover what is needed through the error messages, rather than guessing why a button is inactive. A field disabled because it does not apply in the current context should be accompanied by an explanation of why, or should be hidden entirely if it is irrelevant, rather than left as a faded mystery. Fading alone, without explanation, is not communication; it is merely a visual hint that some visitors cannot perceive.

Finally, disabled text must still meet contrast requirements, as discussed earlier, because a disabled control whose label is too faint to read cannot be understood, even if its disabled state is correctly marked. A disabled style that is merely a lighter version of the normal style, with contrast below the standard, fails readability, and should be adjusted to a style that is clearly disabled — perhaps with a different border, an icon, or a pattern — while keeping its text readable. A disabled control that is explained, correctly marked, and readable is a control that respects its visitors, even in its unavailability.

Check: What is the main difference between disabled and aria-disabled for keyboard users?

Section 43 — Time limits and giving visitors enough time

Some pages impose time limits: a session that expires after inactivity, a form that must be completed within a set time, a carousel that advances automatically, a notification that disappears after a few seconds. Those limits can be necessary for security or for practical reasons, but they can also be barriers, because visitors who read slowly, who use assistive technology that takes longer to navigate, who are interrupted, or who need more time to understand content may be unable to complete a task within the limit, through no fault of their own. The WCAG standard for timing is that, for each time limit set by the content, the visitor must be able to turn it off, adjust it to at least ten times the default, or extend it, with a warning before it expires and an opportunity to extend it with a simple action. That standard recognises that a fixed, unextendable limit discriminates against slower readers and assistive technology users, whose navigation inherently takes longer, not because they are less capable, but because their tools and methods require more steps. A session timeout that logs a visitor out after ten minutes, without warning, and discards their form data, is a classic example of a limit that fails that standard, because it punishes visitors who need more time, and it destroys their work in the process.

Practical implementation of adjustable timing involves several elements. First, warn the visitor before a limit expires, with a clear message and a simple way to extend it, such as a button that says "Keep me signed in" or "I need more time," which is keyboard accessible and announced to screen readers. Second, preserve the visitor's work when a limit does expire, where possible, so a timed-out session does not discard a partially completed form, but allows the visitor to resume after re-authenticating. Third, avoid unnecessary limits altogether: a form that does not need a time limit should not have one, and a carousel that advances automatically should have pause controls and should not advance so quickly that its content cannot be read. Automatic movement, such as a carousel or a slideshow, deserves special attention, because it is a time limit in disguise: content that moves or disappears on a timer imposes a reading deadline, even if no formal timeout exists. A carousel that advances every five seconds gives a visitor five seconds to read each slide, which may be insufficient for a slow reader, a screen reader user, or a visitor who is distracted. Providing pause, previous, and next controls, and stopping automatic advancement when the visitor interacts with the carousel or hovers over it, gives the visitor control over that timing, which is the same principle as adjustable session limits, applied to moving content.

Finally, time limits should be tested with the visitors they affect in mind: try completing a form with a screen reader, or with only a keyboard, and see whether the limit allows enough time, or whether it creates pressure that leads to errors. A page that gives visitors enough time is a page that respects their pace, rather than imposing the author's pace on them, and that respect is a fundamental part of accessibility.

Check: What three options must a time limit offer under WCAG?

Section 44 — Consistent navigation and identification

Visitors learn a site's navigation once and then rely on it on every page, expecting the same links in the same order, with the same names, in the same place. That consistency is an accessibility feature, because it reduces cognitive load and navigation effort, particularly for visitors with cognitive difficulties, visitors using screen readers who memorise a navigation structure, and visitors using keyboard navigation who tab through a familiar order. A site whose navigation changes unpredictably from page to page forces visitors to relearn it on every page, which is tiring and error-prone, and it can cause visitors to miss content or to take wrong turns.

Consistent navigation means that navigation mechanisms repeated on multiple pages — the primary menu, the footer links, the search box, the breadcrumb trail — appear in the same relative order and with the same labels on every page where they appear, unless a change is initiated by the visitor, such as expanding a submenu. That does not mean every page must have identical navigation; a section page may have additional section navigation, and a form page may have a simplified header, but the core navigation that is repeated should be consistent in order and naming. A link labelled "Products" on one page should not become "Our range" on another, if it goes to the same destination, because that renaming creates ambiguity about whether the links are the same, and it breaks the visitor's learned map of the site.

Consistent identification is the companion principle: components that have the same functionality across a site should be identified consistently, with the same names, icons, and labels. A search button that is a magnifying glass icon on one page and the word "Search" on another, for the same search function, creates confusion about whether they are the same function, and it forces visitors to relearn the component's identity. Similarly, a "Submit" button that becomes "Send" on another form, for the same action, or an error icon that changes shape between pages, undermines the visitor's ability to recognise familiar components quickly. These principles also apply to page structure. Headings, landmarks, and content order should follow a consistent pattern across a site, so a visitor who learns that the main content follows the navigation on one page can rely on that pattern on the next. A site where one page puts the sidebar before the content and another puts it after, without a clear reason, forces visitors to reorient on every page, which is particularly burdensome for screen reader users, who navigate by structure, and for keyboard users, who navigate by tab order. Consistency in structure, like consistency in navigation, is a kindness that costs little to maintain once established, but that pays off on every visit. Navigate several pages in sequence, by keyboard and by screen reader, and check whether the navigation order, labels, and structure feel familiar from page to page, or whether each page feels like a new site to learn. If the latter, the site's templates and components need alignment, so that consistency becomes a property of the system, not an accident of individual pages.

Check: Why should a link to the same destination keep the same text on every page?

Section 45 — Error prevention and recovery

Preventing errors is better than correcting them, and accessible forms and interactions should be designed to help visitors avoid mistakes in the first place, and to recover easily when mistakes do happen. That principle applies to all visitors, but it is particularly important for visitors with cognitive difficulties, visitors using assistive technology, and visitors in a hurry, for whom an error can be confusing, time-consuming, or, if it discards work, devastating. Error prevention and recovery are therefore not merely usability niceties; they are accessibility requirements for interactions that have significant consequences.

The most important error prevention technique is confirmation for irreversible or significant actions. Deleting an account, submitting a payment, sending a message to many recipients, or discarding a long form are actions whose consequences cannot easily be undone, and they should require a confirmation step — a dialog asking "Are you sure?" with the action clearly described, and with options to confirm or cancel that are both keyboard accessible and clearly labelled. That confirmation gives the visitor a chance to catch a mistake before it becomes irreversible, and it is particularly valuable for visitors who may activate a control accidentally, through a mis-tap or a keyboard slip. A second technique is review before submission. For a long form, a purchase, or a legal agreement, showing the visitor a summary of what they entered or are about to do, with a chance to edit it before final submission, helps them catch errors in their own input, such as a wrong address, a wrong quantity, or a misunderstood question. That review step should be accessible itself, with its summary in text, its edit controls clearly labelled, and its focus managed so a keyboard user can navigate the summary and return to the form to correct an error. A review step that is itself confusing defeats its purpose, so it must be designed with the same care as the form it reviews.

A third technique is reversibility: where possible, allow visitors to undo an action, rather than requiring them to avoid it perfectly. An email that can be unsent within a few seconds, a deleted item that can be restored from a trash folder, a changed setting that can be reverted with a single click — each of these turns a potential disaster into a minor inconvenience, and each reduces the anxiety of using a site, because visitors know a mistake is recoverable. Reversibility is not always possible for legal or technical reasons, but where it is possible, it should be provided, and where it is not, confirmation becomes even more important.

Finally, error recovery should preserve work. A form that discards all entered data when a single field fails validation forces the visitor to start again, which is punishing, especially for a visitor who took a long time to complete it with assistive technology. Preserving entered values, highlighting only the fields in error, and focusing the first error, as discussed earlier, turns recovery into a small correction rather than a restart. That preservation is a simple technical choice — repopulating fields on re-render — with a large human benefit, and it is a hallmark of a form designed with its visitors' effort in mind.

Check: Why is a confirmation step especially important for irreversible actions?

Section 46 — Plain language and readable text

Accessibility is not only about code and controls; it is also about language, because a page whose text is difficult to understand excludes visitors with cognitive difficulties, visitors with low literacy, visitors reading in a second language, and visitors who are tired, stressed, or in a hurry. Plain language — writing clearly, concisely, and in a way that its audience can understand on first reading — is therefore an accessibility practice, and it is one that benefits every visitor, because clear writing is easier for everyone to read, regardless of ability.

Plain language begins with audience awareness. Who will read this page, what do they already know, and what do they need to do after reading it? A page for the general public should not assume technical knowledge, should explain jargon on first use, and should avoid unnecessary complexity in sentence structure. A page for specialists can assume more, but should still be clear, because even specialists appreciate writing that gets to the point. Writing for the audience, rather than for the author's convenience, is the first step, and it requires thinking about the reader before writing, not after.

Concrete techniques for plain language include using short sentences, active voice, and common words where possible, without oversimplifying to the point of inaccuracy. A sentence such as "Your application has been received and will be reviewed within five working days" is clearer than "Your submission has been duly noted and shall be processed in due course," because it says what happened and when, in words the reader knows. Headings should be descriptive and in plain language too, so a visitor scanning headings can understand a page's structure without decoding jargon. Lists should be used for sets of items, steps, or options, because they are easier to scan and remember than dense paragraphs.

Readability also involves presentation, not only wording. Text should be set in a readable font size, with sufficient line height and paragraph spacing, as discussed in earlier chapters, because dense, tiny text is hard to read even when its wording is plain. Long passages should be broken into sections with headings, so visitors can find the part they need without reading everything. Key information — deadlines, amounts, actions required — should be prominent, not buried in a paragraph, because visitors scanning for that information should find it quickly. Those presentation choices are part of plain language in practice, because they affect whether a visitor can use the text, not merely whether they can decode its words.

Finally, plain language should be tested with real readers where possible, by asking someone unfamiliar with the subject to read a page and explain what it means and what they should do next. If they cannot, the page needs rewriting, however technically correct its wording may be. That test, like keyboard and screen reader testing, is a human test that automated tools cannot replace, because readability is ultimately about human understanding, not about a score. A page that passes that test is a page that communicates, which is the purpose of writing on the web in the first place.

Check: Why is plain language an accessibility issue, not just a style preference?

Section 47 — Charts, data visualisation and text alternatives

Charts, graphs, and other data visualisations present information visually, often in ways that are powerful for sighted visitors — a rising line, a comparison of bars, a distribution of points — but that can be inaccessible to visitors who cannot see the chart, or who cannot distinguish its colours, or who use a screen reader that cannot interpret a complex graphic. Making a visualisation accessible therefore requires providing its information in another form, typically as text, so that its meaning is available to every visitor, not only to those who can see it.

The most reliable text alternative for a chart is a data table containing the same data, placed near the chart and clearly associated with it, so a screen reader user can navigate the table cell by cell and understand the values, trends, and comparisons that the chart shows visually. A chart showing monthly sales, for example, should be accompanied by a table with months and sales figures, so the data is available in a structured, navigable form. That table should be a real HTML table, with headers and caption, as discussed earlier, not merely a list of numbers in a paragraph, because a table's structure helps a screen reader user understand the data's organisation.

In addition to a data table, a chart should have a short text summary of its main message, in alt text or in surrounding text, stating the conclusion a sighted visitor would draw from it. For example, "Sales rose steadily from January to June, peaking in June," gives a visitor who cannot see the chart the chart's headline finding, without requiring them to navigate the full table to discover it. That summary and the table together serve different needs: the summary for visitors who need the message, the table for visitors who need the detail, and both for visitors who need to verify the message against the data.

Visual design of charts also matters for accessibility. Colour should not be the only way to distinguish data series, as discussed earlier: series should also be distinguished by labels, patterns, or line styles, so a visitor who cannot distinguish colours can still tell them apart. Text in charts — axis labels, legends, annotations — should meet contrast requirements and be large enough to read, including when the chart is viewed on a small screen or at high zoom. Interactive charts, which allow visitors to hover over points for values, should also be keyboard accessible, with their data points reachable by tabbing and their values announced, or with the data table as a keyboard-accessible alternative, so a keyboard user is not excluded from the chart's information.

Finally, consider whether a chart is the right presentation at all for the data and audience. A simple comparison of two numbers may be clearer as text than as a chart, and a complex chart with many series may be clearer as several smaller charts or as a table with a summary. Choosing the simplest presentation that conveys the information accurately is an accessibility decision as well as a design decision, because simplicity reduces the barriers that complex visualisations create. A chart that is simple, labelled, and accompanied by text is a chart that serves its purpose for every visitor.

Check: Why should a chart be accompanied by both a summary and a data table?

Section 48 — Running an accessibility audit

An accessibility audit is a systematic review of a site's accessibility, combining automated tools, manual testing, and review against standards, to find barriers, prioritise them, and plan fixes. Running an audit is a skill in itself, because it requires choosing what to test, how to test it, and how to report findings in a way that leads to action, rather than a long list of issues that overwhelms the team and is never addressed. A good audit is focused, practical, and tied to real visitor tasks, not merely to a checklist of abstract criteria. Testing every page of a large site in depth is rarely feasible, so an audit typically covers a representative sample — the home page, key landing pages, a form, a search results page, a product or article page, and any page with complex interactions — plus the site's templates, because a template error affects every page that uses it. For each sampled page, the audit should test the key tasks a visitor would perform there — finding information, completing a form, making a purchase — because a page that passes a checklist but fails a task is still inaccessible in practice. That task focus keeps the audit grounded in visitor experience, rather than in abstract compliance.

Next, the audit combines methods. Manual testing follows: keyboard navigation, screen reader navigation, zoom and reflow, contrast checks, and visual review for colour-only information, focus visibility, and plain language. Each method catches different issues, and together they give a fuller picture than any one alone. Findings should be recorded with specifics: the page, the element, the barrier, the visitor affected, and the severity — whether it blocks a task, makes it difficult, or is a minor annoyance — because severity guides prioritisation, and specifics guide fixing. A finding that says "form has accessibility issues" is unactionable; a finding that says "the email field on the contact form has no associated label, blocking screen reader users from completing the form" is actionable.

Prioritisation is the next step, and it should be based on impact, not merely on ease of fixing. Barriers that block a task — a form that cannot be completed by keyboard, a page with no headings — should be fixed first, even if they are harder to fix than minor issues, because they exclude visitors entirely. Barriers that make a task difficult — low contrast, vague link text — come next, and minor issues — a decorative image with verbose alt text — last. That impact-first ordering ensures the audit leads to the greatest improvement for visitors, rather than to a quick win on easy issues that leaves blockers in place.

Finally, an audit should end with a plan: who will fix each issue, by when, and how the fix will be verified, by retesting with the same methods that found the issue. An audit without a plan is merely a report, and a report without follow-up is soon forgotten. Scheduling a re-audit after fixes, and integrating accessibility checks into ongoing development, as discussed in the next section, turns a one-time audit into a sustained improvement, which is what accessibility requires, because sites change continuously, and new barriers appear with every new feature.

Check: Why should an audit prioritise blockers over easy minor fixes?

Section 49 — Keeping a site accessible over time

Accessibility is not a project with a finish line; it is a property that must be maintained as a site changes, because every new feature, every redesign, every content update, and every third-party widget can introduce new barriers, even on a site that was previously accessible. A site that passes an audit today may fail tomorrow after a new component is added without labels, a new colour is introduced without contrast checking, or a new video is published without captions. Maintaining accessibility therefore requires processes, not merely a one-time effort, and those processes should be built into how a site is designed, built, and published.

The most effective process is to include accessibility in the definition of done for every feature: a feature is not complete until it has been checked for labels, keyboard operability, focus visibility, contrast, and screen reader announcement, as appropriate to its type. That check can be a simple checklist, completed by the developer who built the feature, and reviewed by a colleague, rather than a formal audit for every change. The key is that the check happens before release, when fixes are cheap, rather than after, when they require rework and may be deprioritised. A button component that is natively a button, with a focus style, a label, and a sufficient target size, will produce accessible buttons wherever it is used, without each developer needing to remember those details. A form field component with a label association, help text, and error handling built in will produce accessible forms by default. Investing in accessible components, and documenting their accessibility properties, is therefore a high-leverage way to maintain accessibility at scale, because it makes the accessible choice the default choice, rather than a choice that must be remembered each time.

Content processes matter too. Editors and content creators should be trained to write alt text, to use headings correctly, to write descriptive link text, and to provide captions and transcripts for media, because much of a site's accessibility depends on content, not only on code. A content management system that prompts for alt text when an image is uploaded, that warns when a heading level is skipped, or that requires a transcript for a video, helps editors follow those practices, by making them part of the publishing workflow rather than relying on memory. Those prompts should be helpful, not obstructive, and they should explain why the information is needed, so editors understand its purpose rather than filling it in mechanically.

Finally, regular re-auditing — a lighter version of the full audit, on a schedule or after major changes — catches regressions that processes miss, and it provides a record of a site's accessibility over time, which is useful for reporting, for legal compliance, and for motivating continued investment. A site that is audited, fixed, and then never checked again will drift, while a site that is checked regularly, with its findings acted on, will stay accessible, or improve, as it grows. That ongoing attention is the difference between accessibility as a launch feature and accessibility as a sustained commitment, and it is the commitment that visitors deserve.

Check: Why is accessibility maintenance a process, not a one-time fix?

Section 50 — Chapter 14 in one paragraph

Accessibility means building pages that every visitor can perceive, operate, and understand, through semantic HTML that gives structure and behaviour for free, through headings, landmarks, and descriptive links that make navigation possible by screen reader and keyboard, through alt text that translates images into words and empty alt text that skips decoration, through labels, grouping, and error messages that make forms completable, through visible focus, logical tab order, skip links, and focus management that make keyboard navigation coherent, through contrast, colour-independent information, zoom and reflow, and generous targets that make content readable and controls usable, through careful ARIA used only where native HTML cannot help, with accessible names, expanded states, and live regions that announce dynamic change, through tables, lists, language, and titles that preserve meaning, through captions, transcripts, and control over motion and sound that respect different senses and sensitivities, and through testing — by keyboard, by screen reader, by automated tools with their limits understood, and by audit and maintenance — that keeps those practices honest over time, because accessibility is not a feature to add at the end, but a way of building that, practised consistently, produces pages that are clearer, more robust, and more humane for every visitor, whatever their abilities, devices, or circumstances, and that is a standard worth holding every page you build to from now on. That standard is also a practical one, because the techniques in this chapter are not exotic or expensive: they are mostly choices about markup, wording, colour, and testing that can be made at the moment a component is built, when they cost little, rather than retrofitted later, when they cost much more. A developer who writes a label with every field, who checks contrast before choosing a colour, who tabs through a page before shipping it, and who listens to that page with a screen reader even briefly, will produce accessible pages almost as a side effect of good craftsmanship, while a developer who treats accessibility as a separate audit at the end will keep rediscovering the same failures. The habits matter more than any single technique, because habits travel with you from project to project, while techniques are forgotten between projects unless they are practised. Keep those habits, apply them to every page, and let accessibility become, not a checklist you complete, but the way you build: with structure that can be navigated, with content that can be perceived in more than one way, with controls that can be operated in more than one way, and with language that can be understood by the people it is written for. That is the work of this chapter, and it is work worth doing well, on every page, for every visitor.

Check: In your own words, summarise what makes a page accessible.

Practical task

Audit a page you have built, or a page of your choice, for accessibility: check its headings and landmarks, its images' alt text, its links' text, its forms' labels and errors, its keyboard navigation and focus visibility, its contrast, and its zoom behaviour to 200%. Fix at least three barriers you find, using only native HTML where possible, and retest by keyboard alone and, if you can, with a screen reader. Write a short report listing each barrier, who it affected, how you fixed it, and how you verified the fix, and keep that report as a template for future audits.

Chapter questions

  1. Explain, in plain words, why semantic HTML is the foundation of accessibility, using a button as your example.
  2. A form field has a placeholder but no label. Why is that a problem, and how do you fix it?
  3. Describe how you would test a page by keyboard alone, and name three failures that test can reveal.
  4. Why can an automated accessibility tool not prove a page is fully accessible, and what manual tests should follow it?
  5. A site offers a dark theme and a light theme. What accessibility checks must you repeat for each theme, and why?