Guias de acessibilidade para WCAG 2.0 (A)
Aprenda como identificar e corrigir problemas de acessibilidade comuns sinalizados pelo Axe Core — para que suas páginas sejam inclusivas e utilizáveis por todos. Consulte também o nosso Guias de validação HTML.
Screen readers announce definition lists in a specific way, conveying the relationship between terms (<dt>) and their descriptions (<dd>). When a <dl> element contains invalid direct children — such as <p>, <span>, or <li> elements — or when <dt> and <dd> elements appear in the wrong order, assistive technology cannot reliably parse the list. This primarily affects blind and deafblind users who depend on screen readers to understand content structure.
For example, a screen reader might announce a definition list by saying “definition list with 3 items,” then reading each term followed by its definition. If the markup is malformed, the screen reader may skip items, miscount them, or fail to associate terms with their definitions.
This rule maps to WCAG Success Criterion 1.3.1: Info and Relationships (Level A), which requires that information, structure, and relationships conveyed visually are also available programmatically. A properly structured <dl> ensures the semantic relationship between terms and definitions is preserved in the accessibility tree.
How to Fix It
Follow these rules when building definition lists:
-
Direct children of
<dl>must be limited to:<dt>,<dd>,<div>,<script>, or<template>elements. No other elements (like<p>,<span>,<li>, or plain text nodes) should appear as direct children. -
Ordering matters: One or more
<dt>elements must come before one or more<dd>elements. A<dd>should never precede a<dt>within a group. -
Using
<div>as a wrapper: You may wrap a<dt>/<dd>group in a<div>for styling purposes, but each<div>must contain a complete group (at least one<dt>followed by at least one<dd>). -
No stray content: Don’t place bare text or non-allowed elements directly inside the
<dl>.
Examples
Incorrect: Invalid direct child element
The <p> element is not a valid direct child of <dl>.
<dl>
<p>Beverage Types</p>
<dt>Coffee</dt>
<dd>A black hot drink made from roasted beans</dd>
</dl>
Incorrect: Wrong order of <dt> and <dd>
The <dd> element must follow the <dt>, not precede it.
<dl>
<dd>A black hot drink made from roasted beans</dd>
<dt>Coffee</dt>
</dl>
Incorrect: <dd> without a preceding <dt>
Every <dd> must be associated with at least one <dt>.
<dl>
<dd>An orphan definition with no term</dd>
</dl>
Correct: Basic definition list
<dl>
<dt>Coffee</dt>
<dd>A black hot drink made from roasted beans</dd>
<dt>Milk</dt>
<dd>A white cold drink</dd>
</dl>
Correct: Multiple definitions for a single term
<dl>
<dt>Coffee</dt>
<dd>A black hot drink made from roasted beans</dd>
<dd>A stimulating beverage containing caffeine</dd>
</dl>
Correct: Using <div> to wrap groups
Wrapping <dt>/<dd> groups in <div> elements is valid and useful for styling.
<dl>
<div>
<dt>Coffee</dt>
<dd>A black hot drink made from roasted beans</dd>
</div>
<div>
<dt>Milk</dt>
<dd>A white cold drink</dd>
</div>
</dl>
Correct: Multiple terms sharing one definition
<dl>
<dt>Latte</dt>
<dt>Café au lait</dt>
<dd>A coffee drink made with espresso and steamed milk</dd>
</dl>
Description lists follow a specific structural hierarchy in HTML. The <dl> element defines the list, and within it, <dt> elements represent terms while <dd> elements provide their corresponding descriptions. When <dt> or <dd> elements exist outside of a <dl>, the browser has no context to establish the relationship between terms and definitions. This makes the content semantically meaningless to assistive technologies.
Screen reader users are most affected by this issue. Screen readers announce description lists with specific cues — for example, telling users they’ve entered a list, how many items it contains, and the relationship between terms and descriptions. When <dt> and <dd> elements lack a <dl> parent, these announcements don’t occur, and users who are blind or deafblind lose important structural context. Keyboard-only users and users with mobility impairments who rely on assistive technologies are also affected, as their tools may not properly navigate orphaned list items.
This rule relates to WCAG 2.0, 2.1, and 2.2 Success Criterion 1.3.1: Info and Relationships (Level A), which requires that information, structure, and relationships conveyed through presentation can be programmatically determined. A description list’s structure conveys a meaningful relationship between terms and definitions, so the proper HTML hierarchy must be in place for that relationship to be communicated to all users.
How to fix it
-
Wrap orphaned
<dt>and<dd>elements inside a<dl>parent element. -
Ensure proper ordering —
<dt>elements should come before their associated<dd>elements. -
Only place
<dt>and<dd>elements (or<div>elements that group<dt>/<dd>pairs) as direct children of<dl>. -
Each
<dt>should have at least one corresponding<dd>, and vice versa, to form a complete term-description pair.
Examples
Incorrect: <dt> and <dd> without a <dl> parent
<dt>Coffee</dt>
<dd>A hot, caffeinated beverage</dd>
<dt>Milk</dt>
<dd>A cold, dairy-based drink</dd>
This is invalid because the <dt> and <dd> elements are not wrapped in a <dl>. Screen readers will not recognize these as a description list, and users will miss the term-definition relationships.
Correct: <dt> and <dd> wrapped in a <dl>
<dl>
<dt>Coffee</dt>
<dd>A hot, caffeinated beverage</dd>
<dt>Milk</dt>
<dd>A cold, dairy-based drink</dd>
</dl>
Correct: using <div> to group term-description pairs inside <dl>
HTML allows <div> elements as direct children of <dl> to group each <dt>/<dd> pair, which can be useful for styling:
<dl>
<div>
<dt>Coffee</dt>
<dd>A hot, caffeinated beverage</dd>
</div>
<div>
<dt>Milk</dt>
<dd>A cold, dairy-based drink</dd>
</div>
</dl>
Incorrect: <dd> nested inside an unrelated element
<div>
<dd>This description has no list context</dd>
</div>
A <dd> inside a <div> (or any non-<dl> parent) is invalid. Replace the <div> with a <dl> and add a corresponding <dt>:
<dl>
<dt>Term</dt>
<dd>This description now has proper list context</dd>
</dl>
The <title> element is the very first piece of information screen reader users hear when a page loads. It’s also what appears in browser tabs, bookmarks, and search engine results. When a page has no title — or has an empty or generic one like “Untitled” — screen reader users are forced to read through the page content to figure out its purpose. For users who navigate between multiple open tabs or use their browser history to find a previous page, missing or vague titles create serious barriers.
This rule relates to WCAG 2.4.2 Page Titled (Level A), which requires that web pages have titles describing their topic or purpose. Because this is a Level A criterion, it represents a minimum baseline for accessibility. The rule also aligns with Trusted Tester guideline 12.A and EN 301 549.
The users most affected by missing or poor page titles include:
- Screen reader users (blind and deafblind users), who rely on the title as the first orientation cue when a page loads
- Users with cognitive disabilities, who benefit from clear, descriptive titles to understand where they are
- Keyboard-only users and anyone navigating between multiple tabs, who use titles to distinguish pages
How to fix it
-
Add a
<title>element inside the<head>of every HTML document. -
Write meaningful text inside the
<title>— it must not be empty or contain only whitespace. - Make each title unique across your site so users can distinguish between pages.
- Put the most unique information first. If you include a site or brand name, place it at the end (e.g., “Contact Us – Acme Corp” rather than “Acme Corp – Contact Us”). This way, screen reader users hear the distinguishing content immediately instead of listening to the same brand name on every page.
-
Align the title with the page’s
<h1>heading. They don’t need to be identical, but they should be closely related since both describe the page’s purpose. - Avoid placeholder text like “Untitled,” “Page 1,” or “New Document.”
Beyond accessibility, descriptive titles improve SEO since search engines use them to filter, rank, and display results.
Examples
Incorrect: missing <title> element
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<h1>Our Products</h1>
</body>
</html>
This page has no <title> element at all. Screen reader users will hear no identifying information when the page loads.
Incorrect: empty <title> element
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<h1>Our Products</h1>
</body>
</html>
A <title> element is present but contains no text, which is equivalent to having no title.
Incorrect: generic or placeholder title
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
</head>
<body>
<h1>Our Products</h1>
</body>
</html>
While technically not empty, a placeholder title provides no useful information about the page’s content.
Correct: descriptive and unique title
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Our Products – Acme Corp</title>
</head>
<body>
<h1>Our Products</h1>
</body>
</html>
The title clearly describes the page content, places the unique information first, and includes the site name at the end for context. It also closely matches the <h1> heading on the page.
Why This Is an Accessibility Problem
In HTML, the id attribute is designed to be a unique identifier for a single element in the document. When two or more elements share the same id, the browser has no reliable way to determine which element is being referenced. This becomes a critical accessibility barrier when that id is used to create relationships between elements — such as linking a <label> to a form input, or connecting a description to a widget via aria-describedby.
Assistive technologies like screen readers rely on these id-based relationships to communicate information to users. When duplicates exist, the screen reader will typically resolve the reference to the first element in the DOM with that id, which may not be the intended target. This means:
- A blind or deafblind user may hear the wrong label for a form field, or no label at all.
-
An ARIA relationship like
aria-labelledbyoraria-describedbymay point to the wrong content, giving users incorrect or missing context. -
Interactive components that depend on
aria-owns,aria-controls, oraria-activedescendantmay break entirely.
This rule relates to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that all user interface components have accessible names and roles that can be programmatically determined. Duplicate id values used in ARIA or label associations directly undermine this requirement by creating ambiguous or broken programmatic relationships.
How to Fix It
-
Identify all duplicate
idvalues that are referenced by ARIA attributes (aria-labelledby,aria-describedby,aria-controls,aria-owns,aria-activedescendant, etc.) or by a<label>element’sforattribute. -
Rename the duplicate
idvalues so that each one is unique within the document. -
Update any references to those
idvalues in ARIA attributes orforattributes to match the new unique values. - Verify that each relationship still works correctly by testing with a screen reader or the axe accessibility checker.
Examples
Incorrect: Duplicate id on elements referenced by for
In this example, two inputs share the same id of "email". The second <label> intends to reference the second input, but both for attributes resolve to the first input.
<label for="email">Personal Email</label>
<input type="email" id="email">
<label for="email">Work Email</label>
<input type="email" id="email">
A screen reader user tabbing to the second input would hear no label or the wrong label, making it impossible to know what information to enter.
Correct: Unique id values for each input
<label for="personal-email">Personal Email</label>
<input type="email" id="personal-email">
<label for="work-email">Work Email</label>
<input type="email" id="work-email">
Incorrect: Duplicate id referenced by aria-labelledby
<span id="section-title">Shipping Address</span>
<div role="group" aria-labelledby="section-title">
<!-- shipping fields -->
</div>
<span id="section-title">Billing Address</span>
<div role="group" aria-labelledby="section-title">
<!-- billing fields -->
</div>
Both groups would be announced as “Shipping Address” because the browser resolves both aria-labelledby references to the first <span> with id="section-title".
Correct: Unique id values for each referenced element
<span id="shipping-title">Shipping Address</span>
<div role="group" aria-labelledby="shipping-title">
<!-- shipping fields -->
</div>
<span id="billing-title">Billing Address</span>
<div role="group" aria-labelledby="billing-title">
<!-- billing fields -->
</div>
Incorrect: Duplicate id used in aria-describedby
<p id="hint">Must be at least 8 characters.</p>
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="hint">
<p id="hint">Re-enter your password to confirm.</p>
<label for="confirm-password">Confirm Password</label>
<input type="password" id="confirm-password" aria-describedby="hint">
Correct: Unique id values for each description
<p id="password-hint">Must be at least 8 characters.</p>
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="password-hint">
<p id="confirm-hint">Re-enter your password to confirm.</p>
<label for="confirm-password">Confirm Password</label>
<input type="password" id="confirm-password" aria-describedby="confirm-hint">
When a form field has more than one <label> element pointing to it (either via the for attribute or by nesting), assistive technologies have no reliable way to determine which label is the correct one. This inconsistency means that users who are blind, have low vision, or are deafblind may hear the wrong label, an incomplete label, or a confusing combination of labels when interacting with a form. Users with mobility impairments also benefit from properly associated labels, since a single clear <label> expands the clickable area of the associated input.
This rule relates to WCAG 2.0, 2.1, and 2.2 Success Criterion 3.3.2: Labels or Instructions (Level A), which requires that labels or instructions are provided when content requires user input. Multiple conflicting labels undermine this requirement because the user cannot reliably receive a single, clear label for the field.
How to Fix
Ensure that each form field has only one <label> element associated with it. You can associate a label with a field in one of two ways — but use only one label per field:
-
Explicit association — Use the
forattribute on the<label>matching theidof the input. -
Implicit association — Wrap the input inside the
<label>element.
If you need to provide additional descriptive text beyond the label, use aria-describedby to point to supplementary instructions rather than adding a second <label>.
If you have a situation where one label must be visually hidden, hide the redundant label using CSS (display: none or visibility: hidden) so it is fully removed from the accessibility tree, and remove its for attribute. Using aria-hidden="true" alone on a <label> is not sufficient to prevent all screen readers from associating it with the field.
Examples
Incorrect: Two explicit labels for one input
Both <label> elements use for="username", causing unpredictable screen reader behavior.
<label for="username">Username</label>
<label for="username">Enter your username</label>
<input type="text" id="username" />
Incorrect: One explicit and one implicit label
The input is both wrapped in a <label> and referenced by another <label> via for.
<label for="email">Email</label>
<label>
Email address:
<input type="text" id="email" />
</label>
Incorrect: Nested labels
Labels should never be nested inside each other.
<label>
Enter your comments:
<label>
Comments:
<textarea id="comments"></textarea>
</label>
</label>
Correct: Single explicit label
One <label> with a for attribute matching the input’s id.
<label for="username">Username</label>
<input type="text" id="username" />
Correct: Single implicit label
The input is wrapped inside a single <label>.
<label>
Email address:
<input type="text" id="email" />
</label>
Correct: Label with supplementary instructions using aria-describedby
When you need to provide extra guidance beyond the label, use aria-describedby instead of a second label.
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="password-hint" />
<p id="password-hint">Must be at least 8 characters with one number.</p>
Correct: Using the title attribute as a label
When a visible label is not appropriate (rare cases), the title attribute can serve as an accessible name.
<textarea id="search" title="Search terms"></textarea>
Correct: Select inside a single label
<label>
Choose an option:
<select id="options">
<option selected>Option A</option>
<option>Option B</option>
</select>
</label>
When a <frame> or <iframe> has tabindex="-1", the browser removes it from the sequential keyboard navigation order. This means that any focusable elements inside the frame — such as links, buttons, form controls, or other interactive elements — become completely unreachable via the keyboard. If the frame also has scrollable content, keyboard users cannot scroll it either, since focus can never enter the frame to begin with.
This creates a serious barrier for people who rely on keyboards to navigate, including blind users who use screen readers and people with mobility disabilities who cannot use a mouse. Content trapped inside an inaccessible frame is effectively hidden from these users, even though it may be fully visible on screen.
Related WCAG Success Criteria
This rule maps to WCAG 2.1 Success Criterion 2.1.1: Keyboard (Level A), which requires that all functionality be operable through a keyboard interface without requiring specific timings for individual keystrokes. When focusable content is locked inside a frame with tabindex="-1", this criterion is violated because keyboard users cannot access or interact with that content.
This is a Level A requirement — the most fundamental level of accessibility — meaning it must be met for a page to be considered minimally accessible.
How to Fix It
-
Remove
tabindex="-1"from any<frame>or<iframe>that contains focusable content. Without an explicittabindex, the browser will handle focus naturally and allow keyboard users to tab into the frame. -
Use
tabindex="0"if you need to explicitly include the frame in the tab order. -
Only use
tabindex="-1"on frames that genuinely contain no focusable or interactive content. Even then, be cautious — if the frame’s content changes later to include interactive elements, the negative tabindex will silently create an accessibility barrier.
As a general best practice, avoid using tabindex="-1" on frames entirely. It’s easy for frame content to change over time, and a negative tabindex can turn into an accidental accessibility issue after a routine content update.
Examples
Incorrect: Frame with focusable content and tabindex="-1"
The button inside this iframe is unreachable by keyboard because tabindex="-1" prevents focus from entering the frame.
<iframe
srcdoc="<button>Click me</button>"
tabindex="-1"
title="Interactive widget">
</iframe>
Correct: Frame with focusable content and no negative tabindex
Removing tabindex="-1" allows keyboard users to tab into the frame and reach the button.
<iframe
srcdoc="<button>Click me</button>"
title="Interactive widget">
</iframe>
Correct: Frame with focusable content and tabindex="0"
Using tabindex="0" explicitly places the frame in the natural tab order.
<iframe
srcdoc="<button>Click me</button>"
tabindex="0"
title="Interactive widget">
</iframe>
Correct: Frame with no focusable content and tabindex="-1"
When a frame contains only static, non-interactive content (no links, buttons, or form controls), using tabindex="-1" is acceptable because there is nothing inside that requires keyboard access.
<iframe
srcdoc="<p>Hello world</p>"
tabindex="-1"
title="Static content display">
</iframe>
Screen reader users rely on frame titles to understand what each embedded region of a page contains. Many screen readers offer a feature that lists all frames on a page by their titles, allowing users to jump directly to the one they need. When frames lack titles or share identical titles, this feature becomes useless — users are left guessing which frame is which, or the screen reader falls back to unhelpful information like “frame,” “javascript,” a filename, or a URL.
This issue primarily affects users who are blind, deafblind, or who navigate with assistive technologies. It relates to WCAG 2.0, 2.1, and 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that all user interface components have an accessible name that can be programmatically determined. When frames share a title, their accessible names fail to uniquely identify them, violating this criterion. The rule is also covered by Trusted Tester guideline 12.D, which requires that the combination of accessible name and description for each <iframe> describes its content, as well as EN 301 549 section 9.4.1.2.
How to Fix It
-
Add a
titleattribute to every<frame>and<iframe>element. - Make each title unique across the page. No two frames should share the same title.
- Make each title descriptive. The title should clearly summarize the content or purpose of the frame. Avoid generic labels like “frame” or “untitled.”
-
Match the frame’s inner document
<title>to thetitleattribute on the frame element when possible. Some screen readers replace the frame’stitleattribute with the inner document’s<title>element, so keeping them consistent ensures a reliable experience.
Tips for Writing Good Frame Titles
- Keep titles brief but informative — describe what the frame contains, not just that it exists.
- Put the most distinctive information first. If you include a brand name, place it at the end so users don’t have to hear it repeatedly while scanning a list of frames.
- Replace placeholder titles like “untitled” or “page” with meaningful descriptions.
- If the framed content has a visible heading, consider aligning the frame title with that heading for consistency.
Examples
Incorrect: Frames with Duplicate Titles
In this example, two iframes share the same title, making it impossible for screen reader users to tell them apart.
<iframe src="/news.html" title="Company Updates"></iframe>
<iframe src="/events.html" title="Company Updates"></iframe>
Incorrect: Frame with an Empty Title
An empty title provides no useful information to assistive technology users.
<iframe src="/contact.html" title=""></iframe>
Incorrect: Frames with No Title
Without any title attribute, screen readers fall back to announcing unhelpful information like the URL or “frame.”
<iframe src="/navigation.html"></iframe>
<iframe src="/main-content.html"></iframe>
Correct: Frames with Unique, Descriptive Titles
Each frame has a distinct title that clearly describes its content.
<iframe src="/news.html" title="Latest Company News"></iframe>
<iframe src="/events.html" title="Upcoming Events Calendar"></iframe>
Correct: Frame Title Matching the Inner Document Title
For the best experience, align the title attribute with the <title> element in the framed document.
<!-- Parent page -->
<iframe src="/contact.html" title="Contact Form"></iframe>
<!-- contact.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Contact Form</title>
</head>
<body>
<h1>Contact Us</h1>
<form>
<label for="email">Email</label>
<input type="email" id="email" name="email">
<button type="submit">Send</button>
</form>
</body>
</html>
Correct: Hiding Decorative or Non-Content Frames
If a frame is purely decorative or contains no meaningful content for users (such as a tracking pixel), hide it from assistive technology entirely instead of giving it a misleading title.
<iframe src="/tracking-pixel.html" title="" aria-hidden="true" tabindex="-1"></iframe>
Note that this approach should only be used when the frame genuinely contains no content that any user would need to access.
Screen reader users can pull up a list of all frames on a page and navigate between them using their titles. When frames lack accessible names, users hear generic labels like “frame,” “JavaScript,” or a raw URL — none of which convey the purpose or content of the frame. This forces users to enter each frame just to figure out what it contains, which is time-consuming and frustrating.
This issue primarily affects users who are blind or deafblind and rely on assistive technology, but it also impacts users with mobility disabilities who navigate by frame lists or landmarks.
Relevant WCAG Success Criteria
This rule relates to WCAG 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that all user interface components have a name that can be programmatically determined. Frames are interactive containers that users navigate into and out of, so they must have an accessible name.
It is also explicitly required by Section 508 (§1194.22(i)), which states that frames shall be titled with text that facilitates frame identification and navigation.
How to Fix It
Give every <iframe> and <frame> element a clear, descriptive accessible name using one of these methods:
-
titleattribute — The most common approach. Add atitlethat briefly describes the frame’s content. -
aria-labelattribute — Provides an accessible name directly on the element. -
aria-labelledbyattribute — References another element’s text as the accessible name.
Tips for Writing Good Frame Titles
- Be brief and descriptive. A title like “Product search results” is far more useful than “frame1” or “content.”
- Make each title unique. If you have multiple frames, each one should have a distinct title so users can tell them apart.
-
Match the frame’s internal document title. Some screen readers replace the frame’s
titleattribute with the<title>element inside the framed document. For consistency, keep them the same or very similar. - Avoid placeholder text. Titles like “untitled” or “iframe” provide no useful information.
- Put unique information first. If you include a brand name, place it after the descriptive content so users hear the most useful information immediately.
Examples
Incorrect: <iframe> Without an Accessible Name
The frame has no title, aria-label, or aria-labelledby, so screen readers cannot describe it.
<iframe src="https://example.com/video-player"></iframe>
Incorrect: Empty title Attribute
An empty title is the same as having no title at all.
<iframe src="https://example.com/video-player" title=""></iframe>
Correct: Using the title Attribute
<iframe src="https://example.com/video-player" title="Product demo video player"></iframe>
Correct: Using aria-label
<iframe src="https://example.com/map" aria-label="Store location map"></iframe>
Correct: Using aria-labelledby
<h2 id="chat-heading">Live Chat Support</h2>
<iframe src="https://example.com/chat" aria-labelledby="chat-heading"></iframe>
Correct: Multiple Frames With Unique Titles
When a page contains several frames, each one should have a distinct, descriptive title.
<iframe src="/navigation" title="Site navigation menu"></iframe>
<iframe src="/content" title="Main article content"></iframe>
<iframe src="/ads" title="Sponsored advertisements"></iframe>
Correct: Matching the Frame Title to the Document Title
For the best experience, align the title attribute on the <iframe> with the <title> element inside the framed document.
<!-- Parent page -->
<iframe src="contact-form.html" title="Contact us form"></iframe>
<!-- contact-form.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Contact us form</title>
</head>
<body>
<h1>Contact Us</h1>
<form>
<label for="email">Email</label>
<input type="email" id="email">
<button type="submit">Send</button>
</form>
</body>
</html>
Screen readers rely on distinct sound libraries for each language, with pronunciation rules, intonation patterns, and phonetics tailored to that specific language. When a user navigates to a page, the screen reader checks the lang attribute on the <html> element to determine which sound library to load. If no lang attribute is present, the screen reader falls back to whatever default language the user configured during setup. For someone who speaks multiple languages and browses websites in different languages, this creates a serious problem — the screen reader will attempt to read foreign-language content using the wrong pronunciation rules, producing garbled or incomprehensible speech.
This issue primarily affects blind and deafblind users who rely on screen readers, but it also impacts users with cognitive disabilities who may use text-to-speech tools. When text is read aloud with the wrong language rules, even simple words become unrecognizable, effectively making the content inaccessible.
Related WCAG Success Criteria
This rule maps to WCAG 2.0, 2.1, and 2.2 Success Criterion 3.1.1: Language of Page at Level A — the most fundamental level of conformance. This criterion requires that the default human language of each web page can be programmatically determined. It is also referenced by the Trusted Tester guidelines (11.A), EN 301 549, and RGAA.
How to Fix It
Add a lang attribute with a valid language code to the opening <html> element. The value should represent the primary language of the page content.
You can use a simple two-letter language code (like en for English, fr for French, or de for German), or you can be more specific with a regional dialect code like en-US for American English or fr-CA for Canadian French. A full list of valid language subtags is available in the IANA Language Subtag Registry.
If sections of your page contain content in a different language than the primary one, use the lang attribute on those specific elements so screen readers can switch sound libraries mid-page.
For languages written right to left (such as Arabic or Hebrew), also include the dir attribute with a value of rtl to indicate the text direction.
Examples
Incorrect: Missing lang attribute
This will trigger the rule because the <html> element has no lang attribute:
<html>
<head>
<title>My Website</title>
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
Correct: lang attribute with a valid language code
<html lang="en">
<head>
<title>My Website</title>
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
Correct: Using a regional dialect code
<html lang="en-US">
<head>
<title>My Website</title>
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
Correct: Handling inline language changes
When a portion of the page is in a different language, use the lang attribute on the containing element:
<html lang="en">
<head>
<title>My Website</title>
</head>
<body>
<p>The French word for hello is <span lang="fr">bonjour</span>.</p>
</body>
</html>
Correct: Right-to-left language with dir attribute
<html lang="ar" dir="rtl">
<head>
<title>موقعي</title>
</head>
<body>
<h1>مرحبا</h1>
</body>
</html>
Screen readers rely on language-specific sound libraries to pronounce text accurately. Each language has its own pronunciation rules, intonation patterns, and phonetic characteristics, so screen readers load the appropriate library based on the declared language of the document. When the <html> element has no lang attribute or contains an invalid value — such as a misspelling or a fabricated code — the screen reader cannot determine which library to use. It defaults to whatever language the user configured during setup, which means content written in French might be read aloud using English pronunciation rules. The result is speech that sounds like a confusing, unintelligible accent.
This issue primarily affects blind users and deafblind users who rely on screen readers, but it also impacts users with cognitive disabilities who may use text-to-speech tools to aid comprehension. When pronunciation is wrong, understanding drops dramatically.
Related WCAG Success Criteria
This rule maps to WCAG Success Criterion 3.1.1: Language of Page (Level A), which requires that the default human language of each web page can be programmatically determined. This is a Level A requirement — the most fundamental level of accessibility — meaning it must be satisfied for basic compliance under WCAG 2.0, 2.1, and 2.2. It is also required by the Trusted Tester methodology, EN 301 549, and RGAA.
How to Fix It
-
Add a
langattribute to the opening<html>element. - Set its value to a valid BCP 47 language tag that represents the primary language of the page.
-
Double-check the spelling of the language code. Common valid codes include
"en"(English),"fr"(French),"es"(Spanish),"de"(German),"ja"(Japanese), and"ar"(Arabic). -
You can optionally include a regional subtag, such as
"en-US"for American English or"fr-CA"for Canadian French. -
For XHTML documents served as XML, use
xml:langinstead of or in addition tolang.
If the language changes within the document for specific passages, use the lang attribute on the appropriate element to indicate the switch. For right-to-left languages, also include the dir="rtl" attribute.
Examples
Incorrect: Missing lang attribute
<html>
<head>
<title>My Page</title>
</head>
<body>
<p>Welcome to my website.</p>
</body>
</html>
Without a lang attribute, screen readers cannot determine the page language and will default to the user’s configured language, which may not match the content.
Incorrect: Invalid lang value
<html lang="english">
<head>
<title>My Page</title>
</head>
<body>
<p>Welcome to my website.</p>
</body>
</html>
The value "english" is not a valid BCP 47 language tag. The correct code for English is "en".
Correct: Valid lang attribute
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Welcome to my website.</p>
</body>
</html>
Correct: Regional subtag
<html lang="fr-CA">
<head>
<title>Ma page</title>
</head>
<body>
<p>Bienvenue sur mon site web.</p>
</body>
</html>
Correct: Language change within the page
<html lang="en">
<head>
<title>Multilingual Page</title>
</head>
<body>
<p>This paragraph is in English.</p>
<p lang="es">Este párrafo está en español.</p>
</body>
</html>
Correct: Right-to-left language
<html lang="ar" dir="rtl">
<head>
<title>صفحتي</title>
</head>
<body>
<p>مرحبا بكم في موقعي.</p>
</body>
</html>
The dir="rtl" attribute ensures the text direction is correctly rendered for right-to-left languages like Arabic and Hebrew.
The lang attribute tells browsers and assistive technologies what language the content is written in. The xml:lang attribute serves the same purpose but comes from the XML/XHTML specification. When both attributes are present on the same element—most commonly the <html> element—they must agree on the base language. The “base language” is the primary language subtag (e.g., en in en-US or fr in fr-CA). If one attribute says en and the other says fr, assistive technologies receive conflicting information and cannot reliably determine how to process the content.
Who is affected
This issue primarily impacts screen reader users, including people who are blind, deafblind, or have cognitive disabilities. Screen readers maintain separate pronunciation libraries for each language. When they encounter a language declaration, they switch to the appropriate library so words are spoken with correct pronunciation and cadence. A mismatch between lang and xml:lang can cause the screen reader to select the wrong library or behave unpredictably, making the content difficult or impossible to understand.
Users who speak multiple languages and browse sites in different languages are especially vulnerable, as they rely on accurate language declarations to switch between language contexts.
Related WCAG success criteria
This rule relates to WCAG Success Criterion 3.1.1: Language of Page (Level A), which requires that the default human language of each web page can be programmatically determined. This criterion applies across WCAG 2.0, 2.1, and 2.2, as well as EN 301 549 and RGAA. When lang and xml:lang conflict, the programmatically determined language becomes ambiguous, violating this requirement.
How to fix it
-
Check the
<html>element (and any other elements) for the presence of bothlangandxml:langattributes. -
Ensure both attributes use the same base language. For example, if
lang="en", thenxml:langmust also start withen(e.g.,en,en-US, oren-GB). -
If you don’t need
xml:lang, the simplest fix is to remove it entirely. Thelangattribute alone is sufficient for HTML5 documents. - If you’re serving XHTML, both attributes may be required. In that case, keep them in sync.
You can find valid language codes on the ISO 639 language codes reference page. Common codes include en for English, fr for French, es for Spanish, de for German, and ar for Arabic.
Examples
Incorrect: mismatched base languages
The lang attribute specifies English, but xml:lang specifies French. This creates a conflict that confuses assistive technologies.
<html lang="en" xml:lang="fr">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
Correct: matching base languages
Both attributes specify English. The dialect subtag may differ, but the base language (en) is the same.
<html lang="en-US" xml:lang="en-GB">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
Correct: identical values
The simplest and most reliable approach—use the exact same value for both attributes.
<html lang="en" xml:lang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
Correct: only lang attribute (HTML5)
In HTML5 documents, the xml:lang attribute is not required. Using lang alone avoids any possibility of a mismatch.
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
Correct: specifying language on inline elements
When content in a different language appears within the page, use the lang attribute on the appropriate element. If you also use xml:lang, make sure they match.
<p>Welcome to our site. <span lang="es" xml:lang="es">Bienvenidos a nuestro sitio.</span></p>
For right-to-left languages, also include the dir attribute:
<p lang="ar" xml:lang="ar" dir="rtl">مرحبا بكم في موقعنا</p>
Images are a fundamental part of web content, but they are inherently visual. Screen readers cannot interpret the pixels of an image — they rely entirely on a text alternative to describe the image’s content and purpose to the user. Without alternative text, a screen reader may read out the image’s file name (e.g., “IMG_20230415_093012.jpg”), which is meaningless and confusing. In the worst case, the image is silently skipped, and the user misses critical information.
This rule primarily affects people who are blind or deafblind, who depend on screen readers or braille displays to access content. Users with low vision who rely on text magnification or high-contrast modes can also benefit from well-written alternative text.
Why This Matters
This rule maps to WCAG 2.2 Success Criterion 1.1.1: Non-text Content (Level A), which requires that all non-text content presented to the user has a text alternative that serves an equivalent purpose. It is also required by Section 508 and EN 301 549. Because it is a Level A requirement, it represents the absolute minimum baseline for accessibility — failing it means your content is fundamentally inaccessible to many users.
How to Provide Alternative Text
There are three main techniques for giving an image accessible alternative text:
-
The
altattribute — the most common and preferred method for<img>elements. -
The
aria-labelattribute — provides an accessible name directly on the element. -
The
aria-labelledbyattribute — points to theidof another element that contains the descriptive text.
Writing Good Alt Text
When writing alternative text, consider these questions:
- Why is this image here? What role does it play in the content?
- What information does it convey? What would a sighted user take away from it?
- If you removed the image, what words would replace it to preserve the same meaning?
Keep alt text concise and descriptive. Avoid phrases like “image of” or “picture of” — screen readers already announce the element as an image. Don’t use the file name as alt text. Focus on the image’s purpose and meaning, not a pixel-by-pixel description.
Handling Decorative Images
Not every image carries meaning. Decorative images — such as visual separators, background flourishes, or images whose content is fully described in surrounding text — should be hidden from assistive technology. To do this, use an empty alt attribute: alt="".
Do not omit the alt attribute entirely. If alt is missing, screen readers will often fall back to reading the file name, which creates a worse experience than having no announcement at all. An empty alt="" explicitly tells the screen reader to skip the image.
You can also use role="presentation" or role="none" to mark an image as decorative, but an empty alt attribute is simpler and more widely supported.
Examples
Incorrect: Missing Alt Text
The alt attribute is completely absent. A screen reader may announce the file name instead.
<img src="team-photo.jpg">
Incorrect: Unhelpful Alt Text
The alt text contains the file name rather than a meaningful description.
<img src="chart-q3.png" alt="chart-q3.png">
Correct: Descriptive Alt Text Using the alt Attribute
<img src="team-photo.jpg" alt="The engineering team gathered around a whiteboard during a planning session">
Correct: Alt Text Using aria-label
<img src="revenue-chart.png" aria-label="Bar chart showing quarterly revenue increasing from $2M in Q1 to $3.5M in Q4">
Correct: Alt Text Using aria-labelledby
<p id="chart-desc">Bar chart showing quarterly revenue growth from Q1 to Q4 2024.</p>
<img src="revenue-chart.png" aria-labelledby="chart-desc">
Correct: Decorative Image with Empty Alt
This separator line serves no informational purpose and should be hidden from screen readers.
<img src="decorative-line.svg" alt="">
Correct: Decorative Image Using role="presentation"
<img src="decorative-swirl.png" role="presentation">
What This Rule Checks
This axe-core rule (image-alt) inspects every <img> element on the page and verifies that it has an accessible text alternative. Specifically, it checks that each <img> has at least one of the following:
-
A non-empty
altattribute -
An
aria-labelattribute -
An
aria-labelledbyattribute that references a valid element -
An empty
alt=""(orrole="presentation"/role="none") to indicate the image is decorative
If none of these conditions are met, the rule flags the image as a critical accessibility violation.
Every interactive control on a page needs an accessible name so that assistive technologies can announce it to users. For <input> elements with type="button", type="submit", or type="reset", the accessible name typically comes from the value attribute. While <input type="submit"> and <input type="reset"> have default browser-provided labels (“Submit” and “Reset”), <input type="button"> has no default — it renders as an empty, unlabeled button if no name is provided.
This issue critically affects blind and deafblind users who rely on screen readers. When a screen reader encounters an input button without discernible text, it may announce something like “button” with no indication of what the button does. This makes it impossible to navigate forms, submit data, or perform actions confidently. Sighted users can often infer a button’s purpose from visual context, but screen reader users depend entirely on the programmatic accessible name.
Related WCAG Success Criteria
This rule maps to WCAG 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that all user interface components have a name that can be programmatically determined. It also relates to Section 508 requirements that every non-text element must have a text equivalent, and EN 301 549 Section 9.4.1.2.
How to Fix It
There are several ways to give an input button a discernible accessible name:
-
valueattribute — The most straightforward approach. Setvalueto descriptive text that communicates the button’s purpose. -
aria-labelattribute — Provides an accessible name directly. Useful when you need the accessible name to differ from the visible text, or whenvaluecannot be used. -
aria-labelledbyattribute — References another element’sidwhose text content becomes the accessible name. The referenced element must exist and contain text. -
titleattribute — Acts as a fallback accessible name. However,titleis less reliable because it is not consistently exposed by all assistive technologies and only appears as a tooltip on hover, making it inaccessible to keyboard and touch users. Prefervalueoraria-labelinstead.
For <input type="submit"> and <input type="reset">, browsers provide default labels, so they pass without an explicit value. However, if you set value="" (an empty string), you override the default and create an empty accessible name, which fails.
Examples
Incorrect: Input button with no accessible name
<form action="/search">
<input type="button" id="go" />
</form>
This button has no value, aria-label, aria-labelledby, or title. A screen reader will announce it as just “button” with no context.
Incorrect: Empty aria-label
<form action="/search">
<input type="button" aria-label="" />
</form>
An empty aria-label does not provide a discernible name.
Incorrect: aria-labelledby referencing a nonexistent or empty element
<form action="/search">
<input type="button" aria-labelledby="nonexistent" />
</form>
<form action="/search">
<input type="button" aria-labelledby="empty-label" />
<div id="empty-label"></div>
</form>
If the referenced element doesn’t exist or has no text content, the button has no accessible name.
Incorrect: Submit or reset with an empty value
<form action="/search">
<input type="submit" value="" />
<input type="reset" value="" />
</form>
Setting value to an empty string overrides the browser’s default label, leaving the button unnamed.
Correct: Using the value attribute
<form action="/search">
<input type="button" value="Search" />
</form>
Correct: Using aria-label
<form action="/search">
<input type="button" aria-label="Search" />
</form>
Correct: Using aria-labelledby
<form action="/search">
<input type="button" aria-labelledby="btn-label" />
<span id="btn-label">Search</span>
</form>
Correct: Submit and reset with default or explicit values
<form action="/search">
<input type="submit" />
<input type="reset" />
<input type="submit" value="Send Form" />
<input type="reset" value="Clear All Fields" />
</form>
The first two rely on the browser’s built-in default labels (“Submit” and “Reset”). The last two provide custom, more descriptive labels through the value attribute. Both approaches are valid, though explicit descriptive labels are generally preferred for clarity.
An <input type="image"> element renders an image that functions as a submit button. Unlike standard <input type="submit"> buttons that display their value attribute as visible text, image buttons rely entirely on their visual appearance to communicate purpose. This means assistive technologies have no way to determine the button’s function unless you explicitly provide alternative text.
This is a critical accessibility barrier for several groups of users. Blind and deafblind users who rely on screen readers will hear something generic like “button” or, worse, the image file name (e.g., “btn_submit_v2.png”), which provides no useful information. Users with low vision who use screen magnifiers in combination with screen readers are similarly affected. Without a clear accessible name, these users cannot confidently interact with forms.
This rule relates to two WCAG success criteria at Level A — the minimum conformance level:
- WCAG 1.1.1 (Non-text Content): All non-text content must have a text alternative that serves the equivalent purpose. An image button is non-text content, so it needs alternative text.
- WCAG 4.1.2 (Name, Role, Value): All user interface components must have a programmatically determinable name. Buttons must expose their name to assistive technologies.
This requirement is also mandated by Section 508, EN 301 549, and the Trusted Tester guidelines.
How to fix it
Provide a non-empty accessible name for every <input type="image"> element using one of these methods:
-
altattribute (preferred): Add analtattribute that describes the button’s action. This is the most straightforward and widely supported approach. -
aria-labelattribute: Set anaria-labelwith a concise description of the button’s action. -
aria-labelledbyattribute: Reference theidof another visible element that describes the button’s action.
Writing effective alternative text
The alternative text for an image button should describe the action the button performs, not the image itself. Ask yourself: “What happens when the user clicks this button?” The answer is your alternative text.
- ✅ “Submit”, “Search”, “Add to cart”, “Log in”
- ❌ “Green arrow icon”, “button.png”, “click here”
Avoid filler words like “image of” or “button” — assistive technologies already announce the element’s role as a button.
Important: Simply placing text next to the image button in the HTML is not sufficient. Screen readers need a programmatically associated label, not visually adjacent text. Nearby text cannot be reliably determined as the button’s label by assistive technologies.
Examples
Incorrect: missing alternative text
The image button has no alt, aria-label, or aria-labelledby attribute. A screen reader might announce this as “submit.png button” or just “button.”
<form action="/search">
<label for="query">Search</label>
<input type="text" id="query" name="q">
<input type="image" src="search-icon.png">
</form>
Incorrect: empty alt attribute
An empty alt attribute tells assistive technologies to ignore the element, effectively hiding the button entirely.
<input type="image" src="search-icon.png" alt="">
Correct: using the alt attribute
<form action="/search">
<label for="query">Search</label>
<input type="text" id="query" name="q">
<input type="image" src="search-icon.png" alt="Search">
</form>
Correct: using aria-label
<input type="image" src="submit-arrow.png" aria-label="Submit order">
Correct: using aria-labelledby
The aria-labelledby attribute points to the id of a visible element. Make sure the referenced element exists and is not hidden with display: none or aria-hidden="true".
<h2 id="checkout-heading">Complete your purchase</h2>
<form action="/checkout">
<!-- form fields -->
<input type="image" src="checkout-btn.png" aria-labelledby="checkout-heading">
</form>
What the rule checks
This rule verifies that every <input type="image"> element has a discoverable accessible name. It fails if the element has no alt, aria-label, or aria-labelledby attribute, or if those attributes are empty or resolve to an empty string.
Every form element — such as text inputs, checkboxes, radio buttons, and select menus — must have a programmatically associated label so that assistive technologies can identify and announce the purpose of each field. Without these labels, screen reader users cannot determine what information is expected, and users with motor impairments lose the benefit of a larger clickable target area that a properly associated <label> provides.
Why This Matters
When a form element lacks a programmatic label, screen readers either announce it generically (e.g., “edit text” or “checkbox”) or skip meaningful context entirely. This leaves blind, low-vision, and deafblind users unable to understand what data a field expects or what option a checkbox represents. They must guess based on surrounding content, which is unreliable and error-prone.
Labels also benefit users with motor impairments. When a <label> element is properly associated with an input, clicking the label text activates or focuses the associated control. This creates a larger click target, which is especially helpful for people with limited dexterity.
Sighted users often rely on visual proximity to infer a field’s purpose, but assistive technologies need an explicit programmatic relationship between the label text and the form control to convey the same information.
Relevant Standards
This rule maps to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that all user interface components have an accessible name that can be programmatically determined. It applies across WCAG 2.0, 2.1, and 2.2, as well as Section 508 (§1194.22(n)), EN 301 549 (9.4.1.2), and Trusted Tester guidelines. The user impact is considered critical.
How to Fix It
There are several ways to associate a label with a form element. Use the approach that best fits your situation, listed here from most recommended to least recommended.
1. Explicit <label> with for and id (Recommended)
The most common and reliable method is to use a <label> element whose for attribute matches the id of the form control. This creates an unambiguous programmatic association.
2. Implicit <label> (Wrapping)
Wrap the form control inside a <label> element. The association is implied by the parent-child relationship.
3. aria-label
Use aria-label when the field’s purpose is conveyed visually through an icon or layout rather than visible text. This creates an invisible label that only screen readers announce.
4. aria-labelledby
Use aria-labelledby when the label text already exists elsewhere on the page, or when you need to combine multiple pieces of text into a single label. Reference one or more element id values.
5. placeholder (Not Recommended)
A placeholder attribute can technically provide an accessible name, but it disappears once the user begins typing, removing the visible label. This creates usability problems for everyone and is not a recommended approach.
General Tips
-
Ensure all
idvalues are unique on the page. - Make label text descriptive and meaningful when read aloud in isolation.
-
Remember that buttons (
<button>,<input type="submit">, etc.) are self-labeling through their text content orvalueattribute and do not need a separate<label>. -
Hidden inputs (
<input type="hidden">) do not need labels since they are not presented to users.
Examples
Incorrect: Input without a label
<div>First name:</div>
<input type="text" id="firstname">
The <div> text is visually near the input, but there is no programmatic relationship. A screen reader will announce only “edit text” with no context.
Correct: Explicit label with for and id
<label for="firstname">First name:</label>
<input type="text" id="firstname">
Correct: Implicit label by wrapping
<label>
First name:
<input type="text">
</label>
Correct: aria-label for visually implied fields
<input type="text" aria-label="Search">
<button type="submit">🔍</button>
Correct: aria-labelledby referencing existing text
<div id="temp-label">Temperature</div>
<div id="high-label">High:</div>
<div id="low-label">Low:</div>
<input type="text" aria-labelledby="temp-label high-label">
<input type="text" aria-labelledby="temp-label low-label">
The first input is announced as “Temperature High:” and the second as “Temperature Low:”, combining the referenced text in order.
Incorrect: Relying only on placeholder
<input type="text" placeholder="Enter your email">
While this technically provides an accessible name, the visible hint disappears when the user starts typing, making it difficult to verify the field’s purpose. Always prefer a persistent visible label.
Correct: Visible label with supplementary placeholder
<label for="email">Email address</label>
<input type="text" id="email" placeholder="name@example.com">
Incorrect: Checkbox without a label
<input type="checkbox" id="terms">
I agree to the terms and conditions
The text is adjacent but not associated with the checkbox.
Correct: Labeled checkbox
<input type="checkbox" id="terms">
<label for="terms">I agree to the terms and conditions</label>
Why This Matters
Many users cannot perceive color differences reliably. Approximately 8% of men and 0.4% of women have some form of color vision deficiency, and there are nearly three times more people with low vision than those who are totally blind. For these users, if a link within a paragraph is styled only with a different color — say, blue text in a block of black text — it can be completely invisible as a link.
People with low vision often experience reduced contrast sensitivity, meaning everything appears roughly the same brightness. Without a non-color cue like an underline or a sufficient luminance difference, these users cannot detect that certain text is interactive. This can cause them to miss important navigation, actions, or information entirely.
This rule relates to WCAG 2.0/2.1/2.2 Success Criterion 1.4.1: Use of Color (Level A), which requires that color is not used as the only visual means of conveying information, indicating an action, or distinguishing a visual element. Links embedded in text are one of the most common places where this requirement is violated.
How the Rule Works
The axe rule link-in-text-block checks links that appear inside blocks of text (such as paragraphs) and evaluates them in three steps:
-
Non-color distinction present → Pass. If the link has a visual style that doesn’t depend on color — such as an underline, a border, a background color, or a distinct font weight/style — the rule passes automatically.
-
No non-color distinction and contrast below 3:1 → Fail. If the link relies only on color and the contrast ratio between the link text color and the surrounding text color is less than 3:1, the rule fails.
-
No non-color distinction but contrast is 3:1 or higher → Needs Review. If the link relies only on color but meets the 3:1 contrast threshold, the rule flags it for manual testing. You must verify that the link receives a distinct visual style (such as an underline) on
:focusand:hoverstates.
How to Fix It
The simplest and most reliable fix is to give links a non-color visual indicator. Here are your options, in order of recommendation:
- Underline the link — This is the most universally understood link indicator.
-
Add a border — A
border-bottomcan work as an alternative totext-decoration. - Use a distinct font style — Bold or italic can help, though underline is more conventional for links.
- Add a background or outline — A subtle background color difference can work if it’s clearly visible.
If you choose to rely on color contrast alone (3:1 minimum between link text and surrounding text), you must also ensure the link gains a distinct non-color style on :hover and :focus. This two-part requirement exists because static contrast alone may not be sufficient for all users, but a visual change on interaction confirms the element is interactive.
Examples
Incorrect: Link distinguished only by color with insufficient contrast
<style>
p { color: #333333; }
a { color: #555555; text-decoration: none; }
</style>
<p>
Learn more about our
<a href="/services">consulting services</a>
and how we can help.
</p>
In this example, the link has no underline and the color contrast between #555555 and #333333 is well below 3:1. Users with low vision or color blindness cannot identify the link.
Correct: Link has an underline (recommended)
<style>
p { color: #333333; }
a { color: #0056b3; text-decoration: underline; }
</style>
<p>
Learn more about our
<a href="/services">consulting services</a>
and how we can help.
</p>
The underline provides a clear non-color visual cue, making the link identifiable regardless of color perception.
Correct: Link uses a bottom border instead of underline
<style>
p { color: #333333; }
a {
color: #0056b3;
text-decoration: none;
border-bottom: 2px solid #0056b3;
}
</style>
<p>
Read our
<a href="/guide">accessibility guide</a>
for detailed instructions.
</p>
Correct: Color-only link with sufficient contrast plus hover/focus styles
<style>
p { color: #333333; }
a {
color: #0000ee;
text-decoration: none;
}
a:hover,
a:focus {
text-decoration: underline;
}
</style>
<p>
Visit our
<a href="/help">help center</a>
for answers to common questions.
</p>
Here the contrast between #0000ee and #333333 exceeds 3:1, and the link gains an underline on hover and focus. This satisfies the requirement, though note that axe will still flag this for manual review since it cannot automatically verify the hover/focus styles in all cases.
Incorrect: Underline removed with no replacement
<style>
a { color: #1a73e8; text-decoration: none; }
p { color: #000000; }
</style>
<p>
Check out our
<a href="/blog">latest blog posts</a>
for updates.
</p>
Even though the blue color may seem obvious to sighted users with full color vision, removing the underline without providing another non-color indicator makes this link invisible to users with color blindness or low contrast sensitivity.
Links are one of the most fundamental interactive elements on the web. When a link lacks an accessible name, screen reader users hear something like “link” with no further context, making it impossible to understand the link’s purpose or destination. This affects people who are blind, deafblind, or have low vision and rely on screen readers, as well as keyboard-only users who navigate through links sequentially.
A link’s accessible name can come from several sources: its visible text content, an aria-label attribute, an aria-labelledby reference, a title attribute, or the alt text of an image contained within it. If none of these provide a non-empty string, the link has no discernible text, and this rule will flag it.
Beyond just having text, links must also be programmatically focusable. Avoid relying on device-specific JavaScript events like onmouseover or mouseout(), which are inaccessible to keyboard users. Use device-independent alternatives like onfocus, onblur, focus(), and blur(). Additionally, don’t hide link text from assistive technologies using display: none, visibility: hidden, or aria-hidden="true" on the link or its text content.
It’s also important to use semantic <a> elements with a valid href attribute rather than simulating links with <span> or <div> elements and click handlers. Real links are focusable by default and convey the correct role to assistive technologies.
Related WCAG Success Criteria
This rule relates to two Level A success criteria that apply across WCAG 2.0, 2.1, and 2.2:
- WCAG 2.4.4 (Link Purpose in Context): Users must be able to determine the purpose of each link from the link text alone, or from the link text combined with its programmatically determined context.
- WCAG 4.1.2 (Name, Role, Value): All user interface components, including links, must have an accessible name and role that can be programmatically determined.
This rule is also required under Section 508, EN 301 549, and Trusted Tester guidelines.
How to Fix It
-
Add visible text content inside the
<a>element. -
For image links, provide meaningful
alttext on the<img>element. -
Use
aria-labelwhen you need to provide an accessible name that differs from or supplements the visible text. -
Use
aria-labelledbyto reference another element’s text as the link’s accessible name. -
Don’t hide link text from screen readers with
aria-hidden="true"or CSS that removes elements from the accessibility tree. -
Ensure links are focusable by using proper
<a>elements withhrefattributes and avoiding mouse-only event handlers.
Examples
Empty link (incorrect)
A link with no text or accessible name:
<a href="/settings"></a>
A link that only contains an image with no alt text:
<a href="/home">
<img src="logo.png" alt="">
</a>
A link with its text hidden from assistive technologies:
<a href="/profile">
<span aria-hidden="true">Profile</span>
</a>
Link with visible text (correct)
<a href="/settings">Settings</a>
Image link with descriptive alt text (correct)
<a href="/home">
<img src="logo.png" alt="Homepage">
</a>
Link with aria-label (correct)
This is useful when multiple links share the same visible text, such as repeated “Read more” links:
<h3>Accessibility Updates</h3>
<p>New WCAG 2.2 guidelines have been published.
<a href="/wcag22" aria-label="Read more about WCAG 2.2 updates">Read more</a>
</p>
<h3>Screen Reader Tips</h3>
<p>Learn how to navigate tables with a screen reader.
<a href="/sr-tips" aria-label="Read more about screen reader tips">Read more</a>
</p>
Link with aria-labelledby (correct)
<h3 id="report-title">Annual Report 2024</h3>
<a href="/report-2024.pdf" aria-labelledby="report-title">Download PDF</a>
Icon link with visually hidden text (correct)
For links that use icons without visible text, use a visually hidden <span> to provide an accessible name:
<a href="/search">
<svg aria-hidden="true" focusable="false">
<use href="#icon-search"></use>
</svg>
<span class="visually-hidden">Search</span>
</a>
The visually-hidden class hides the text visually but keeps it available to screen readers:
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
According to the HTML specification, the only permitted content directly inside <ul> and <ol> elements are <li> elements, plus non-content elements like <script> and <template>. When you place other content elements — such as <div>, <span>, <p>, <a>, or plain text nodes — directly inside a list container, you create an invalid structure that breaks the semantic relationship between the list and its items.
Why This Matters
Screen readers announce lists in a specific way to help users navigate and comprehend grouped content. When a user encounters a properly structured list, their screen reader will typically announce something like “list, 5 items” and then allow the user to move through each item individually. This behavior depends entirely on the list being structured correctly.
When non-<li> content elements appear as direct children of <ul> or <ol>, screen readers may:
- Fail to announce the total number of items in the list
- Skip over improperly nested content entirely
- Present list items and non-list content in a confusing, disjointed manner
This primarily affects blind and deafblind users who rely on screen readers, but it also impacts anyone using assistive technology that parses the DOM structure to present content.
Related WCAG Success Criteria
This rule maps to WCAG 2.0, 2.1, and 2.2 Success Criterion 1.3.1: Info and Relationships (Level A). This criterion requires that information, structure, and relationships conveyed through visual presentation can be programmatically determined. When a list is visually presented as a group of related items but its underlying HTML structure is invalid, the relationship between the list container and its items cannot be reliably communicated to assistive technology.
How to Fix It
-
Ensure every content element inside a
<ul>or<ol>is wrapped in an<li>. If you have<div>,<span>,<p>,<a>, or any other content element as a direct child of the list, move it inside an<li>. - Move non-list content outside the list. If you have headings, paragraphs, or other content that isn’t a list item, place it before or after the list element rather than inside it.
-
Nest sub-lists inside
<li>elements. If you need a nested<ul>or<ol>, it must be placed inside an<li>of the parent list, not directly as a child of the parent list.
Examples
Incorrect: <div> elements directly inside a <ul>
<ul>
<div>Apples</div>
<div>Bananas</div>
<div>Cherries</div>
</ul>
Screen readers cannot identify these <div> elements as list items.
Correct: <li> elements as direct children
<ul>
<li>Apples</li>
<li>Bananas</li>
<li>Cherries</li>
</ul>
Incorrect: Heading placed directly inside a list
<ul>
<h3>Fruits</h3>
<li>Apples</li>
<li>Bananas</li>
<li>Cherries</li>
</ul>
Correct: Heading moved outside the list
<h3>Fruits</h3>
<ul>
<li>Apples</li>
<li>Bananas</li>
<li>Cherries</li>
</ul>
Incorrect: Nested list placed directly inside a <ul>
<ul>
<li>Fruits</li>
<ul>
<li>Apples</li>
<li>Bananas</li>
</ul>
<li>Vegetables</li>
</ul>
Correct: Nested list placed inside an <li>
<ul>
<li>
Fruits
<ul>
<li>Apples</li>
<li>Bananas</li>
</ul>
</li>
<li>Vegetables</li>
</ul>
Incorrect: Link directly inside an <ol>
<ol>
<a href="/step-1"><li>Step one</li></a>
<a href="/step-2"><li>Step two</li></a>
</ol>
Correct: Link placed inside the <li>
<ol>
<li><a href="/step-1">Step one</a></li>
<li><a href="/step-2">Step two</a></li>
</ol>
HTML lists rely on a parent-child relationship between the list container (<ul> or <ol>) and its items (<li>). When an <li> element appears outside of a valid list parent, the browser and assistive technologies lose the semantic meaning of that element. The markup is technically invalid HTML, and the content is no longer recognized as part of a list structure.
This issue primarily affects screen reader users. When a screen reader encounters a properly structured list, it announces the list type and the total number of items — for example, “list, 3 items.” As the user navigates through the list, the screen reader announces each item’s position, such as “1 of 3.” This context is essential for understanding the structure of the content, anticipating its length, and navigating efficiently. Without a <ul> or <ol> parent, none of this information is communicated, leaving the user with no way to know the items are related or how many there are.
This rule relates to WCAG 2.2 Success Criterion 1.3.1: Info and Relationships (Level A), which requires that information, structure, and relationships conveyed through visual presentation are also available programmatically. A visual list of items implies a relationship between those items. Using correct semantic markup ensures that relationship is exposed to assistive technologies, not just conveyed visually through bullet points or numbering.
How to fix it
-
Identify any
<li>elements that are not wrapped in a<ul>or<ol>. - Determine whether the list is unordered (no meaningful sequence) or ordered (sequence matters).
-
Wrap the
<li>elements in the appropriate parent:<ul>for unordered lists or<ol>for ordered lists.
Choose <ul> when the order of items doesn’t matter (e.g., a list of ingredients). Choose <ol> when the order is meaningful (e.g., step-by-step instructions).
Examples
Incorrect: <li> elements without a list parent
These list items have no <ul> or <ol> container, so they are not recognized as a list by assistive technologies.
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
Correct: <li> elements inside a <ul>
Wrapping the items in a <ul> creates a valid unordered list that screen readers can announce properly.
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
Correct: <li> elements inside an <ol>
When the order of the items is meaningful, use an <ol> instead.
<ol>
<li>Preheat the oven to 350°F.</li>
<li>Mix the dry ingredients.</li>
<li>Bake for 25 minutes.</li>
</ol>
Incorrect: <li> inside a <div> instead of a list parent
A <div> is not a valid parent for <li> elements, even if it looks correct visually.
<div>
<li>Item one</li>
<li>Item two</li>
</div>
Correct: Replace the <div> with a <ul>
<ul>
<li>Item one</li>
<li>Item two</li>
</ul>
The <marquee> element was never part of any official HTML standard and has been deprecated by all major browsers. It produces text that continuously scrolls across the screen, making it extremely difficult to read, interact with, or comprehend. Even though browsers may still render it, the element should never appear in modern web content.
Why This Is an Accessibility Problem
Scrolling marquee text creates barriers for several groups of users:
- Users with low vision may not be able to read text that is constantly in motion. The movement makes it nearly impossible to track and process the words.
- Users with cognitive disabilities or attention deficits can be distracted or overwhelmed by content that moves without their control. Automatic motion competes for attention and can make it difficult to focus on other parts of the page.
- Users with limited motor skills may be unable to accurately click on links or interactive elements embedded within scrolling content, since the targets are constantly shifting position.
-
Screen reader users may encounter inconsistent or confusing output, since the
<marquee>element is non-standard and assistive technologies are not required to support it.
This rule relates to WCAG 2.2 Success Criterion 2.2.2: Pause, Stop, Hide (Level A), which requires that for any moving, blinking, or scrolling content that starts automatically and lasts more than five seconds, there must be a mechanism for users to pause, stop, or hide it. The <marquee> element provides no such mechanism by default, making it a failure of this criterion. This is also documented as a known failure technique: F16: Failure of Success Criterion 2.2.2 due to including scrolling content where movement is not essential to the activity without also including a mechanism to pause and restart the content.
How to Fix It
The fix is straightforward: remove all <marquee> elements from your HTML, including empty ones. Then decide how to handle the content:
-
Display the content as static text. In most cases, this is the best approach. Simply place the text in a standard element like a
<p>or<span>. -
If motion is truly needed, use CSS animations instead, and provide a visible control (such as a pause button) that allows users to stop the animation. Ensure the animation also respects the
prefers-reduced-motionmedia query.
Examples
Incorrect: Using the <marquee> Element
This code uses the deprecated <marquee> element to create scrolling text with links. Users cannot pause or stop the movement.
<marquee behavior="scroll" direction="left">
Frisbeetarianism is the
<a href="https://en.wikipedia.org/wiki/Belief">belief</a> that when you
<a href="https://en.wikipedia.org/wiki/Death">die</a>, your
<a href="https://en.wikipedia.org/wiki/Soul">soul</a> goes up on the
<a href="https://en.wikipedia.org/wiki/Roof">roof</a> and gets stuck.
</marquee>
Correct: Static Text
The simplest and most accessible fix is to display the content as regular, non-moving text.
<p>
Frisbeetarianism is the
<a href="https://en.wikipedia.org/wiki/Belief">belief</a> that when you
<a href="https://en.wikipedia.org/wiki/Death">die</a>, your
<a href="https://en.wikipedia.org/wiki/Soul">soul</a> goes up on the
<a href="https://en.wikipedia.org/wiki/Roof">roof</a> and gets stuck.
</p>
Correct: CSS Animation with Pause Control and Reduced Motion Support
If scrolling text is a design requirement, use CSS with a pause button and respect the user’s motion preferences.
<div class="scrolling-container">
<p class="scrolling-text" id="ticker">
Breaking news: Accessibility makes the web better for everyone.
</p>
<button type="button" onclick="document.getElementById('ticker').classList.toggle('paused')">
Pause / Resume
</button>
</div>
<style>
.scrolling-text {
animation: scroll-left 10s linear infinite;
}
.scrolling-text.paused {
animation-play-state: paused;
}
@keyframes scroll-left {
from { transform: translateX(100%); }
to { transform: translateX(-100%); }
}
@media (prefers-reduced-motion: reduce) {
.scrolling-text {
animation: none;
}
}
</style>
This approach gives users control over the motion and automatically disables the animation for users who have indicated they prefer reduced motion in their operating system settings.
When a page includes a <meta http-equiv="refresh"> tag with a time value under 20 hours, the browser will automatically reload or redirect the page after that delay. This happens without any user interaction or consent, which creates significant barriers for people with disabilities.
Why This Is a Problem
Automatic page refreshes are disorienting for many users. When the page reloads, the browser moves focus back to the top of the document. A user who was partway through reading content or filling out a form loses their place entirely. This is especially harmful for:
- Screen reader users (blind and deafblind users): A screen reader announces content linearly from the top. An unexpected refresh forces the user to start over, losing context and progress.
- Users with mobility impairments: People who navigate slowly using switch devices, mouth sticks, or other assistive technology may not finish interacting with the page before it refreshes, causing them to lose their work.
- Users with cognitive disabilities: Unexpected changes to the page can be confusing and make it difficult to complete tasks.
This rule relates to WCAG 2.2 Success Criterion 2.2.1: Timing Adjustable (Level A), which requires that for each time limit set by the content, users can turn off, adjust, or extend that time limit. A <meta> refresh gives users no such control — the page simply reloads or redirects with no warning or option to prevent it.
How to Fix It
You have several options depending on your use case:
-
Remove the
metarefresh entirely. If the refresh is unnecessary, simply delete the tag. - Set the delay to 20 hours or more. If a refresh is truly needed, a delay of 20 hours or greater passes the rule, as it’s unlikely to interrupt a user’s session.
-
Use a server-side redirect. If the goal is to redirect users to a new URL, configure a
301or302redirect on the server instead. - Use JavaScript with user controls. If you need periodic content updates, use JavaScript and provide users with the ability to pause, extend, or disable the auto-refresh.
Examples
Failing: Auto-refresh after 30 seconds
This refreshes the page every 30 seconds with no way for the user to stop it.
<head>
<meta http-equiv="refresh" content="30">
</head>
Failing: Delayed redirect after 10 seconds
This redirects the user to another page after 10 seconds, giving them no control over the timing.
<head>
<meta http-equiv="refresh" content="10; url=https://example.com/new-page">
</head>
Passing: Meta refresh removed
The simplest fix is to remove the meta refresh tag entirely.
<head>
<title>My Page</title>
</head>
Passing: Server-side redirect instead of client-side
For redirects, use a server-side response. For example, an HTTP 301 header:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/new-page
Passing: JavaScript refresh with user control
If you need periodic updates, use JavaScript and give users a way to stop or adjust the refresh.
<head>
<title>Live Dashboard</title>
</head>
<body>
<button id="toggle-refresh">Pause Auto-Refresh</button>
<script>
let refreshInterval = setInterval(function () {
location.reload();
}, 300000); // 5 minutes
document.getElementById("toggle-refresh").addEventListener("click", function () {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
this.textContent = "Resume Auto-Refresh";
} else {
refreshInterval = setInterval(function () {
location.reload();
}, 300000);
this.textContent = "Pause Auto-Refresh";
}
});
</script>
</body>
Passing: Delay set to 20 hours or more
If a refresh is absolutely necessary and no interactive alternative is feasible, setting the delay to at least 72,000 seconds (20 hours) will pass the rule.
<head>
<meta http-equiv="refresh" content="72000">
</head>
HTML has a clear content model: interactive elements like <button>, <a>, <select>, and <input> are not allowed to contain other interactive or focusable elements. When you nest one interactive control inside another — for example, placing a link inside a button — browsers and assistive technologies handle the situation inconsistently and unpredictably.
Screen readers are especially affected. When a user tabs to a nested interactive element, the screen reader may produce an empty or silent tab stop. The inner control’s name, role, and state are not announced, meaning the user has no idea what the control does or that it even exists. This creates a serious barrier for blind users who rely on screen readers and keyboard-only users who navigate by tabbing through focusable elements.
This issue also affects users with mobility impairments who use switch devices or other assistive input methods that depend on an accurate understanding of the interactive elements on the page.
Related WCAG Success Criteria
This rule maps to WCAG 2.0, 2.1, and 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A). This criterion requires that for all user interface components, the name and role can be programmatically determined, and states, properties, and values that can be set by the user can be programmatically set. Nested interactive controls violate this because the inner control’s name and role become inaccessible to assistive technology.
How to Fix It
The fix is straightforward: do not place focusable or interactive elements inside other interactive elements. Here are common strategies:
- Separate the controls. Place interactive elements side by side rather than nesting them.
- Restructure the layout. If a design requires a clickable card with inner links or buttons, use CSS positioning or JavaScript event delegation rather than literal nesting.
-
Check elements with ARIA roles. A
<div>withrole="button"is treated as an interactive control. It must not contain links, buttons, inputs, or any other focusable elements.
Examples
Incorrect: Link Nested Inside a Button
The <a> element inside the <button> creates a nested interactive control that screen readers will not announce properly.
<button>
Save
<a href="/options">More options</a>
</button>
Incorrect: Link Nested Inside an Element with role="button"
Even though the outer element is a <div>, the role="button" makes it an interactive control. The nested <a> is inaccessible to screen readers.
<div role="button" tabindex="0">
Search
<a href="/settings">Settings</a>
</div>
Incorrect: Button Nested Inside a Link
Placing a <button> inside an <a> element is equally problematic.
<a href="/dashboard">
Go to dashboard
<button>Settings</button>
</a>
Correct: Separate Interactive Controls
Place each interactive element independently so both are fully announced and operable.
<button>Save</button>
<a href="/options">More options</a>
Correct: Clickable Card with Separate Controls
If you need a card-like pattern where the entire card is clickable but also contains separate actions, avoid literal nesting. Use a flat structure with CSS for layout.
<div class="card">
<h3><a href="/article/1">Article Title</a></h3>
<p>A short description of the article.</p>
<button>Bookmark</button>
</div>
Correct: Single Button with No Nested Interactive Content
A simple, properly structured button with only non-interactive content inside it.
<button>
<svg aria-hidden="true" focusable="false">
<use href="#icon-save"></use>
</svg>
Save
</button>
Note that in the example above, the <svg> element has focusable="false" to prevent it from being a tab stop in some browsers (notably older versions of Internet Explorer and Edge), and aria-hidden="true" because the button text already provides the accessible name.
When a <video> or <audio> element plays sound automatically, it competes with assistive technology output. Screen reader users rely on hearing their screen reader’s spoken output to navigate and interact with web content. If background audio starts playing as soon as they land on a page, it can drown out the screen reader, making it difficult or impossible to find and operate the very control needed to stop the unwanted sound. This creates a frustrating catch-22: the user needs to hear their screen reader to find the stop button, but the auto-playing audio prevents them from hearing the screen reader.
People with cognitive disabilities can also be significantly affected, as unexpected audio can be disorienting, distracting, and make it harder to process page content.
Related WCAG Success Criteria
This rule maps to WCAG Success Criterion 1.4.2: Audio Control (Level A), which requires that if any audio on a web page plays automatically for more than three seconds, either a mechanism is available to pause or stop the audio, or a mechanism is available to control audio volume independently from the overall system volume level. This is a Level A requirement, meaning it is considered a minimum baseline for accessibility across WCAG 2.0, 2.1, and 2.2.
How to Fix the Problem
Use one of the following approaches:
-
Don’t autoplay audio (strongly preferred). Let the user initiate playback themselves. This is the best approach because it gives users full control and avoids any interference with assistive technology.
-
Keep auto-playing audio under three seconds. If the audio stops within three seconds and does not loop, it is unlikely to significantly disrupt screen reader usage.
-
Provide an accessible control mechanism. If audio must autoplay for more than three seconds, include a clearly visible, keyboard-accessible control near the top of the page that allows users to pause, stop, or mute the audio. The control must be reachable before the user needs to navigate through a lot of content.
Important Considerations
-
The
controlsattribute on<audio>and<video>elements satisfies the requirement for a control mechanism, as it provides built-in browser controls for play, pause, and volume. -
A short audio clip that uses the
loopattribute will effectively play indefinitely, so it fails this rule even if the source file is under three seconds. - Custom controls must be keyboard accessible and properly labeled for screen readers.
Examples
Failing: Autoplay with no controls
This <audio> element autoplays and provides no way for the user to stop or mute it:
<audio autoplay src="background-music.mp3">
</audio>
Failing: Short audio that loops indefinitely
Even though the audio file is under three seconds, the loop attribute causes it to repeat forever:
<audio autoplay loop src="chime.mp3">
</audio>
Failing: Video with autoplay and no controls
<video autoplay src="promo.mp4">
</video>
Passing: Autoplay with built-in controls
Adding the controls attribute gives users the ability to pause, stop, and adjust volume:
<audio autoplay controls src="background-music.mp3">
</audio>
<video autoplay controls src="promo.mp4">
</video>
Passing: No autoplay — user initiates playback
The best approach is to let the user decide when to start playback:
<audio controls src="background-music.mp3">
</audio>
Passing: Autoplay with short duration and no loop
If the audio is under three seconds and does not loop, it is acceptable:
<video autoplay src="short-intro.mp4">
</video>
Passing: Autoplay with a custom accessible mute button
If you cannot use the native controls attribute, provide a custom control that is keyboard accessible and labeled:
<audio id="bg-audio" autoplay src="background-music.mp3">
</audio>
<button type="button" onclick="document.getElementById('bg-audio').pause()">
Pause background audio
</button>
How the Rule Works
The axe-core rule no-autoplay-audio evaluates <audio> and <video> elements and checks whether they autoplay audio for more than three seconds without a control mechanism. Specifically:
- If the element has no source, the result is incomplete (duration cannot be determined).
- If the element can autoplay and has no controls mechanism, it fails.
- If the element plays under three seconds but loops, it fails (because the effective duration is infinite).
- If the element can autoplay but has a controls mechanism, it passes.
- If the element can autoplay and the duration is under three seconds (without looping), it passes.
The <object> element embeds external content into a web page — this could be a video, an audio player, an interactive applet, a PDF, or even another HTML page. Unlike images, which have a straightforward alt attribute, <object> elements rely on other mechanisms to provide text alternatives. When no alternative text is present, screen readers encounter the embedded object but have nothing meaningful to announce, leaving blind and deafblind users unable to understand what the content is or what purpose it serves.
Why this matters
Screen readers cannot interpret non-text content on their own. They depend entirely on text alternatives provided in the markup. When an <object> element lacks alternative text, users of assistive technology are left with no information about the embedded content. This is a serious barrier that can cause users to miss critical functionality or information on a page.
This rule relates to WCAG 2.0/2.1/2.2 Success Criterion 1.1.1: Non-text Content (Level A), which requires that all non-text content presented to the user has a text alternative that serves an equivalent purpose. It is also required by Section 508 and EN 301 549.
How to fix it
Add alternative text to every <object> element using one of these approaches:
-
titleattribute — Add atitleattribute directly on the<object>element with a concise description. -
aria-labelattribute — Provide an accessible name usingaria-label. -
aria-labelledbyattribute — Reference theidof another element that contains the descriptive text. -
role="presentation"orrole="none"— If the embedded object is purely decorative and conveys no information, you can remove it from the accessibility tree entirely.
Writing effective alternative text
When crafting alternative text, consider these questions:
- Why is this embedded content on the page?
- What information does it present to sighted users?
- What purpose or function does it serve?
- If you removed the object, what words would you use to convey the same information?
Keep the text concise and meaningful. Avoid generic terms like “object,” “video,” or file names — instead, describe the content’s actual purpose. For example, “Quarterly revenue chart for 2024” is far more useful than “chart.swf.”
Examples
Incorrect: no alternative text
The <object> element has no accessible name, so screen readers cannot describe it.
<object data="quarterly-report.pdf"></object>
Incorrect: inner content used as fallback text
Text placed inside the <object> element is intended as fallback content for browsers that cannot render the object — it is not reliably exposed as an accessible name by screen readers.
<object data="quarterly-report.pdf">
This object shows the quarterly report.
</object>
Incorrect: only whitespace inside the element
<object data="quarterly-report.pdf">
<div> </div>
</object>
Correct: using title
<object data="quarterly-report.pdf" title="2024 Q3 quarterly revenue report"></object>
Correct: using aria-label
<object data="quarterly-report.pdf" aria-label="2024 Q3 quarterly revenue report"></object>
Correct: using aria-labelledby
<h2 id="report-heading">2024 Q3 Quarterly Revenue Report</h2>
<object data="quarterly-report.pdf" aria-labelledby="report-heading"></object>
Correct: decorative object with role="none"
If the embedded object is purely decorative and adds no informational value, remove it from the accessibility tree:
<object data="decorative-animation.swf" role="none"></object>
When you apply role="img" to an element, you’re telling assistive technologies to treat that element as a single image. This is commonly used to group decorative SVG elements, icon fonts, CSS background images, or multiple visual elements into one cohesive image. However, applying the role alone isn’t enough — assistive technologies also need a text alternative that describes the image’s content or purpose.
Without an accessible name, screen readers will either skip the element entirely or announce it as an unlabeled image, leaving users who rely on assistive technology without critical information. This affects screen reader users (who hear content read aloud), braille display users (who read content through tactile output), and to some extent users with low vision who may use screen readers alongside magnification.
This rule relates to WCAG 2.1 Success Criterion 1.1.1 (Non-text Content), which requires that all non-text content presented to the user has a text alternative that serves the equivalent purpose. It is a Level A requirement — the most fundamental level of accessibility compliance.
How to Fix It
Add an accessible name to any element with role="img" using one of these methods:
-
aria-label: Provide the text alternative directly as an attribute value. -
aria-labelledby: Reference theidof another element that contains the descriptive text. -
title: Use thetitleattribute as a fallback (thougharia-labelandaria-labelledbyare preferred, astitlehas inconsistent support across assistive technologies).
The text alternative should be concise and describe the content or purpose of the image. If the image is purely decorative and conveys no information, use aria-hidden="true" instead of role="img" to hide it from assistive technologies entirely.
Examples
Incorrect: role="img" with No Text Alternative
<div role="img">
<!-- SVG icon with no accessible name -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12 2L2 22h20L12 2z"/>
</svg>
</div>
A screen reader encounters this as an image but has no name to announce, so the user gets no useful information.
Correct: Using aria-label
<div role="img" aria-label="Warning: hazardous area ahead">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12 2L2 22h20L12 2z"/>
</svg>
</div>
Correct: Using aria-labelledby
<span id="chart-desc">Quarterly revenue growth from Q1 to Q4 2024</span>
<div role="img" aria-labelledby="chart-desc">
<!-- Complex chart composed of multiple elements -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100">
<rect x="10" y="60" width="30" height="40" fill="#4a90d9"/>
<rect x="50" y="40" width="30" height="60" fill="#4a90d9"/>
<rect x="90" y="25" width="30" height="75" fill="#4a90d9"/>
<rect x="130" y="10" width="30" height="90" fill="#4a90d9"/>
</svg>
</div>
Correct: Using title
<div role="img" title="Company logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" fill="#333"/>
</svg>
</div>
Correct: Decorative Image Hidden from Assistive Technology
If the image is purely decorative and adds no meaningful information, hide it instead of labeling it:
<div aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 10">
<line x1="0" y1="5" x2="100" y2="5" stroke="#ccc" stroke-width="1"/>
</svg>
</div>
Note that role="img" is removed here because the element is not intended to be perceived as an image at all.
Pronto para validar os seus sites?
Comece o seu teste gratuito hoje.