Guias de acessibilidade para mobilidade
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.
Why This Matters
The autocomplete attribute does more than enable browser autofill — it programmatically communicates the purpose of a form field to assistive technologies. This information is critical for several groups of users:
-
Screen reader users rely on the announced field purpose to understand what information is being requested. Without a valid
autocompletevalue, the screen reader may not convey this context clearly. - Users with cognitive disabilities benefit from browsers and assistive tools that can auto-populate fields or display familiar icons based on the field’s purpose, reducing the mental effort required to complete forms.
- Users with mobility impairments benefit from autofill functionality that minimizes the amount of manual input required.
- Users with low vision may use personalized stylesheets or browser extensions that adapt the presentation of form fields based on their declared purpose (e.g., showing a phone icon next to a telephone field).
This rule maps to WCAG 2.1 Success Criterion 1.3.5: Identify Input Purpose (Level AA), which requires that the purpose of input fields collecting user information can be programmatically determined. The autocomplete attribute is the standard mechanism for satisfying this requirement in HTML.
How the Rule Works
The axe rule autocomplete-valid checks that:
-
The
autocompleteattribute value is a valid token (or combination of tokens) as defined in the HTML specification for autofill. -
The value is appropriate for the type of form control it is applied to (e.g.,
emailis used on an email-type input, not on a checkbox). - The tokens are correctly ordered when multiple tokens are used (e.g., a section name followed by a hint token followed by the field name).
The rule flags fields where the autocomplete value is misspelled, uses a non-existent token, or is applied in an invalid way.
How to Fix It
- Identify all form fields that collect personal user information (name, email, address, phone number, etc.).
- Check if the data type matches one of the 53 input purposes defined in WCAG 2.1 Section 7.
-
Add the correct
autocompletevalue to each matching field. Make sure:- The value is spelled correctly.
- It is appropriate for the input type.
-
If using multiple tokens, they follow the correct order: optional section name (
section-*), optionalshippingorbilling, optionalhome,work,mobile,fax, orpager, and then the autofill field name.
-
Set
autocomplete="off"only when you have a legitimate reason to disable autofill — and note that this does not exempt you from the rule if the field still collects identifiable user data.
Common autocomplete Values
Here are some of the most frequently used values:
| Purpose |
autocomplete Value |
|---|---|
| Full name |
name |
| Given (first) name |
given-name |
| Family (last) name |
family-name |
| Email address |
email |
| Telephone number |
tel |
| Street address |
street-address |
| Postal code |
postal-code |
| Country |
country |
| Credit card number |
cc-number |
| Username |
username |
| New password |
new-password |
| Current password |
current-password |
Examples
Incorrect: Missing or Invalid autocomplete Values
<!-- Missing autocomplete attribute entirely -->
<label for="name">Full Name</label>
<input type="text" id="name" name="name">
<!-- Misspelled autocomplete value -->
<label for="email">Email</label>
<input type="email" id="email" name="email" autocomplete="emal">
<!-- Invalid autocomplete value -->
<label for="phone">Phone</label>
<input type="tel" id="phone" name="phone" autocomplete="phone-number">
In the examples above, the first field has no autocomplete attribute, the second has a typo (emal instead of email), and the third uses a non-standard value (phone-number instead of tel).
Correct: Valid autocomplete Values
<label for="name">Full Name</label>
<input type="text" id="name" name="name" autocomplete="name">
<label for="email">Email</label>
<input type="email" id="email" name="email" autocomplete="email">
<label for="phone">Phone</label>
<input type="tel" id="phone" name="phone" autocomplete="tel">
Correct: Using Multiple Tokens
When a form has separate shipping and billing sections, you can use additional tokens to distinguish them:
<fieldset>
<legend>Shipping Address</legend>
<label for="ship-street">Street Address</label>
<input type="text" id="ship-street" name="ship-street"
autocomplete="shipping street-address">
<label for="ship-zip">Postal Code</label>
<input type="text" id="ship-zip" name="ship-zip"
autocomplete="shipping postal-code">
</fieldset>
<fieldset>
<legend>Billing Address</legend>
<label for="bill-street">Street Address</label>
<input type="text" id="bill-street" name="bill-street"
autocomplete="billing street-address">
<label for="bill-zip">Postal Code</label>
<input type="text" id="bill-zip" name="bill-zip"
autocomplete="billing postal-code">
</fieldset>
Correct: Named Sections with section-*
You can use custom section names to group related fields when the same type of data appears multiple times:
<label for="home-tel">Home Phone</label>
<input type="tel" id="home-tel" name="home-tel"
autocomplete="section-home tel">
<label for="work-tel">Work Phone</label>
<input type="tel" id="work-tel" name="work-tel"
autocomplete="section-work tel">
By using valid, correctly applied autocomplete values, you ensure that assistive technologies can communicate the purpose of each field to users, browsers can offer reliable autofill, and your forms meet the requirements of WCAG 2.1 Success Criterion 1.3.5.
Some users need to adjust text spacing to make content readable. People with low vision may increase letter or word spacing to reduce visual crowding. People with cognitive disabilities, dyslexia, or attention deficit disorders often struggle to track lines of text that are tightly spaced — increasing line-height, letter-spacing, or word-spacing can make reading significantly easier.
When text-spacing properties are set inline with !important, they gain the highest specificity in the CSS cascade. This means user stylesheets, browser extensions, and assistive technology tools cannot override those values. The text becomes locked into a fixed spacing that may be difficult or impossible for some users to read.
This rule relates to WCAG 2.1 Success Criterion 1.4.12: Text Spacing (Level AA), which requires that no loss of content or functionality occurs when users adjust:
- Line height to at least 1.5 times the font size
- Spacing following paragraphs to at least 2 times the font size
- Letter spacing to at least 0.12 times the font size
- Word spacing to at least 0.16 times the font size
If inline !important declarations prevent these adjustments, the content fails this criterion.
How to Fix It
The fix is straightforward: do not use !important on inline style attributes for line-height, letter-spacing, or word-spacing. You have a few options:
-
Remove
!importantfrom the inline style declaration. Without!important, users can override the value with a custom stylesheet. - Move styles to an external or embedded stylesheet. This is generally the best approach because it separates content from presentation and gives users more control through the cascade.
-
If
!importantis truly necessary, apply it in a stylesheet rather than inline. Inline!importantstyles are virtually impossible for users to override, while stylesheet-level!importantcan still be overridden by user!importantrules.
Note that other inline style properties like font-size are not flagged by this rule — only the three text-spacing properties (line-height, letter-spacing, word-spacing) are checked.
Examples
Incorrect: Inline styles with !important
These examples fail because !important on inline text-spacing properties prevents user overrides.
<!-- line-height with !important — cannot be overridden -->
<p style="line-height: 1.5 !important;">
This text is locked to a specific line height.
</p>
<!-- letter-spacing with !important — cannot be overridden -->
<p style="letter-spacing: 2px !important;">
This text has fixed letter spacing.
</p>
<!-- word-spacing with !important — cannot be overridden -->
<p style="word-spacing: 4px !important;">
This text has fixed word spacing.
</p>
<!-- Mixed: word-spacing is fine, but letter-spacing has !important -->
<p style="word-spacing: 4px; letter-spacing: 2px !important; line-height: 1.8;">
Even one !important on a spacing property causes a failure.
</p>
Correct: Inline styles without !important
These examples pass because users can override the inline values with a custom stylesheet.
<!-- line-height without !important — overridable -->
<p style="line-height: 1.5;">
Users can adjust this line height with a custom stylesheet.
</p>
<!-- letter-spacing without !important — overridable -->
<p style="letter-spacing: 2px;">
Users can adjust this letter spacing.
</p>
<!-- word-spacing without !important — overridable -->
<p style="word-spacing: 4px;">
Users can adjust this word spacing.
</p>
<!-- Multiple spacing properties, all without !important -->
<p style="word-spacing: 4px; letter-spacing: 2px; line-height: 1.8;">
All three spacing properties can be overridden by the user.
</p>
<!-- font-size with !important is fine — not a text-spacing property -->
<p style="font-size: 200%;">
This does not trigger the rule.
</p>
Best practice: Use an external stylesheet instead
<!-- HTML -->
<p class="readable-text">
Styles are defined in the stylesheet, giving users full control.
</p>
/* CSS */
.readable-text {
line-height: 1.8;
letter-spacing: 0.05em;
word-spacing: 0.1em;
}
By keeping text-spacing styles in a stylesheet, you make it easy for users to apply their own overrides while maintaining your default design.
The <blink> element was once used to draw attention to content by making it flash repeatedly. While it may have seemed like an eye-catching effect, it creates serious accessibility barriers for multiple groups of users. The element has been deprecated from the HTML specification, and although most modern browsers no longer render the blinking effect, the element should still be removed from your markup to ensure compliance and avoid issues in any environment that might still support it.
Why this is an accessibility problem
Blinking content affects several groups of users:
- Users with cognitive disabilities may find blinking text distracting or incomprehensible. The constant flashing can make it very difficult to focus on and understand the content.
- Users with low vision struggle to read text that appears and disappears rapidly. The intermittent visibility makes the content effectively unreadable.
- Users with motor disabilities may have difficulty clicking on blinking links or buttons, since the target is not consistently visible and requires precise timing to activate.
- Users with photosensitive conditions can experience discomfort or, in extreme cases, seizures from flashing content, depending on the frequency.
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, users must be able to pause, stop, or hide it. Since the <blink> element provides no mechanism for users to control the flashing, it fails this criterion outright.
This rule also applies to Section 508 §1194.22(j), which states that pages must be designed to avoid causing the screen to flicker with a frequency greater than 2 Hz and lower than 55 Hz.
How to fix it
-
Remove all
<blink>elements from your HTML. Replace them with standard elements like<span>,<strong>, or<em>. -
Remove
text-decoration: blink;from your CSS, as this is the CSS equivalent of the<blink>element. - Use alternative emphasis techniques to make content stand out — bold text, color, larger font size, borders, background highlights, or icons.
Important: Many modern browsers silently ignore the <blink> element, so the text won’t visually blink even though the element is present in the source code. Don’t rely on how the page looks in the browser to determine whether <blink> tags exist. Always check the actual HTML source.
Examples
Incorrect: using the <blink> element
<p><blink>Moving Sale Thursday!</blink></p>
This causes the text to flash on and off (in browsers that support it), making it inaccessible.
Incorrect: using text-decoration: blink in CSS
<style>
.attention {
text-decoration: blink;
}
</style>
<h2 class="attention">Limited Time Offer!</h2>
The CSS text-decoration: blink value achieves the same inaccessible effect as the <blink> element.
Correct: using static visual emphasis
<p><strong>Moving Sale Thursday!</strong></p>
Using <strong> makes the text bold and conveys emphasis to screen readers without any flashing.
Correct: using CSS for visual emphasis without blinking
<style>
.highlight {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 8px 12px;
font-weight: bold;
}
</style>
<p class="highlight">Limited Time Offer!</p>
This draws attention to the content using color, a border, and bold text — all without any flashing or blinking, keeping the content readable and accessible for everyone.
When a web page uses CSS media queries like @media (orientation: portrait) or @media (orientation: landscape) to force content into a single orientation, it prevents the page from responding to the user’s actual device position. This is checked by the axe rule css-orientation-lock.
Why this matters
Many users physically cannot rotate their devices. People with mobility impairments may have their phone or tablet mounted to a wheelchair, bed, or desk in a fixed orientation. Users with low vision may prefer landscape mode to enlarge text, while others may find portrait easier to read. Locking orientation removes their ability to choose what works best for them.
Beyond motor and vision disabilities, orientation locking also affects users with cognitive disabilities and dyslexia who may rely on a particular layout for readability. Sighted keyboard users who use external displays or stands may also be impacted.
This rule relates to WCAG 2.1 Success Criterion 1.3.4: Orientation (Level AA), which requires that content not restrict its view and operation to a single display orientation unless a specific orientation is essential. Essential use cases are rare — examples include a piano keyboard app, a bank check deposit interface, or a presentation slide display where the orientation is integral to the functionality.
How to fix it
-
Remove orientation-locking CSS. Look for
@mediaqueries that use theorientationfeature combined with styles that effectively hide or completely rearrange content for only one orientation (e.g., settingdisplay: noneorwidth: 0on the body or main content). -
Use responsive design instead. Rather than checking orientation, use
min-widthormax-widthmedia queries to adapt your layout to available space. This naturally accommodates both orientations. - Test in both orientations. Rotate your device or use browser developer tools to simulate both portrait and landscape modes. Verify that all content remains visible and functional.
- Only lock orientation when essential. If your application genuinely requires a specific orientation for core functionality (not just aesthetic preference), document the rationale. This is the only valid exception.
Examples
Incorrect: Locking content to portrait only
This CSS hides the main content area when the device is in landscape orientation, effectively forcing users to use portrait mode:
<style>
@media (orientation: landscape) {
.content {
display: none;
}
.rotate-message {
display: block;
}
}
@media (orientation: portrait) {
.rotate-message {
display: none;
}
}
</style>
<div class="content">
<h1>Welcome to our site</h1>
<p>This content is only visible in portrait mode.</p>
</div>
<div class="rotate-message">
<p>Please rotate your device to portrait mode.</p>
</div>
Incorrect: Using transform to force portrait layout in landscape
<style>
@media (orientation: landscape) {
body {
transform: rotate(-90deg);
transform-origin: top left;
width: 100vh;
height: 100vw;
overflow: hidden;
position: absolute;
}
}
</style>
This forcibly rotates the entire page, fighting against the user’s chosen orientation and creating a confusing, inaccessible experience.
Correct: Responsive layout that works in both orientations
<style>
.content {
padding: 1rem;
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 600px) {
.grid {
grid-template-columns: 1fr 1fr;
}
}
</style>
<div class="content">
<h1>Welcome to our site</h1>
<div class="grid">
<div>
<p>Column one content.</p>
</div>
<div>
<p>Column two content.</p>
</div>
</div>
</div>
This approach uses min-width instead of orientation, adapting the layout based on available space. The content remains fully accessible and readable whether the device is held in portrait or landscape.
Correct: Using orientation queries for minor style adjustments (not locking)
<style>
.hero-image {
width: 100%;
height: 200px;
object-fit: cover;
}
@media (orientation: landscape) {
.hero-image {
height: 300px;
}
}
</style>
<img class="hero-image" src="hero.jpg" alt="A scenic mountain landscape">
Using orientation media queries is acceptable when you’re making minor visual adjustments without hiding or restricting access to content. The key is that all content and functionality remain available in both orientations.
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.
Screen reader users frequently navigate web pages by jumping between headings to get an overview of the content, much like sighted users visually scan a page. When a heading element is empty or its content is hidden from assistive technologies, the screen reader announces something like “heading level 2” with nothing after it. This is disorienting and can make users think content is missing or that the page is broken.
This rule is flagged as a Deque best practice and primarily affects users who are blind or deafblind and rely on screen readers, though it also impacts users with mobility impairments who use heading-based navigation. Well-structured, meaningful headings are foundational to an accessible page — they communicate the document’s outline and help all users find the information they need quickly.
A heading can be effectively “empty” in several ways:
-
The element contains no text at all (
<h2></h2>) - The element contains only whitespace or non-text elements with no accessible name
-
The text is hidden from assistive technologies using
aria-hidden="true"or CSS likedisplay: none
How to fix it
- Add meaningful text to every heading element. Headings should be brief, clear, and descriptive of the section they introduce.
-
Remove heading tags from elements that aren’t actually headings. Don’t use
<h1>through<h6>just for visual styling — use CSS instead. -
Don’t hide heading text from screen readers using
aria-hidden="true"ordisplay: none. If a heading shouldn’t be visible on screen but is needed for accessibility, use a visually-hidden CSS technique instead. -
Maintain a logical heading hierarchy. Headings should be ordered by level (
<h1>, then<h2>, then<h3>, etc.) to accurately convey the structure of the page.
As a quick check, read through only the headings on your page. If they don’t give you a clear sense of the page’s content and structure, rewrite them.
Examples
Empty heading (incorrect)
<h2></h2>
<p>This section has no heading text.</p>
The <h2> is announced by a screen reader, but there’s no content to read.
Heading with only whitespace (incorrect)
<h3> </h3>
Whitespace alone doesn’t provide an accessible name, so this is treated as empty.
Heading hidden from assistive technologies (incorrect)
<h2 aria-hidden="true">About Our Team</h2>
The aria-hidden="true" attribute hides the heading from screen readers entirely, even though sighted users can see it. This creates a gap in the page structure for assistive technology users.
Heading hidden with CSS (incorrect)
<h2 style="display: none;">Contact Us</h2>
Using display: none removes the heading from both the visual layout and the accessibility tree, making it inaccessible to everyone.
Heading with visible text (correct)
<h2>About Our Team</h2>
<p>We are a small company dedicated to accessible design.</p>
The heading clearly describes the section that follows.
Visually hidden heading for screen readers only (correct)
When a heading is needed for document structure but shouldn’t appear visually, use a visually-hidden class rather than display: none or aria-hidden:
<style>
.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;
}
</style>
<h2 class="visually-hidden">Main Navigation</h2>
<nav>
<a href="/home">Home</a>
<a href="/about">About</a>
</nav>
This keeps the heading accessible to screen readers while hiding it visually.
Heading with an image that has alt text (correct)
<h1>
<img src="logo.png" alt="Acme Corporation">
</h1>
The heading’s accessible name comes from the image’s alt attribute, so the heading is not considered empty.
When users navigate a web page using a keyboard or screen reader, they move through what’s called the “focus order” — the sequence of interactive elements that receive focus. Each time an element receives focus, a screen reader announces its role (e.g., “button,” “link,” “checkbox”) along with its name and state. This role is how users understand what type of control they’ve landed on and what actions they can take.
If a focusable element has no role — for example, a <div> made focusable with tabindex="0" — the screen reader may read the element’s text content but will provide no context about what the element is. The user hears text but has no idea whether to press Enter, type input, or toggle a state. Similarly, if an element has an inappropriate role like presentation or an abstract role like widget, assistive technologies cannot convey meaningful interaction patterns.
This issue primarily affects blind and deafblind users who rely on screen readers, and users with motor disabilities who navigate exclusively with a keyboard. Without proper role information, these users cannot efficiently or accurately interact with page controls.
Why Roles Matter
This rule aligns with accessibility best practices recommended by Deque and RGAA. While it doesn’t map to a single WCAG success criterion, it supports several principles:
- WCAG 4.1.2 (Name, Role, Value): All user interface components must expose their role, name, and state to assistive technologies.
- WCAG 2.1.1 (Keyboard): All functionality must be operable through a keyboard. Meaningful role information is essential for keyboard users to understand what they can do with a focused element.
When an element in the focus order has a valid, appropriate role, screen readers can:
- Announce the type of control (e.g., “button,” “textbox,” “menu item”).
- Inform users of available interactions (e.g., “press Enter to activate,” “use arrow keys to navigate”).
- Communicate state changes (e.g., “checked,” “expanded”).
How to Fix the Problem
Use Native HTML Elements First
The simplest and most reliable fix is to use the correct native HTML element. Native elements like <button>, <a>, <input>, and <select> come with built-in roles, keyboard behavior, and accessibility support — no extra attributes needed.
Add ARIA Roles to Custom Widgets
If you must use a non-semantic element (like <div> or <span>) as an interactive control, you need to:
-
Add an appropriate
roleattribute (e.g.,role="button",role="checkbox",role="tab"). - Ensure all required keyboard interactions are implemented.
-
Manage ARIA states and properties (e.g.,
aria-checked,aria-expanded).
Avoid Abstract Roles
ARIA defines abstract roles like command, input, landmark, range, section, sectionhead, select, structure, and widget. These exist only as part of the role taxonomy and must never be used directly on elements. Always use a concrete role instead.
Remove tabindex from Non-Interactive Elements When Possible
If an element doesn’t need to be interactive, consider removing its tabindex attribute entirely so it doesn’t appear in the focus order.
Appropriate Roles for Interactive Content
Here are some common categories of valid roles for focusable elements:
-
Widget roles:
button,checkbox,combobox,link,listbox,menu,menuitem,menuitemcheckbox,menuitemradio,option,radio,scrollbar,slider,spinbutton,switch,tab,textbox,treeitem -
Composite widget roles:
grid,menubar,radiogroup,tablist,toolbar,tree,treegrid -
Landmark roles:
banner,complementary,contentinfo,main,navigation,region,search -
Document structure roles:
dialog,alertdialog,application,document,log,status,timer,tooltip
Examples
Incorrect: Focusable Element With No Role
This <div> can receive focus, but a screen reader won’t announce what it is:
<div tabindex="0" onclick="submitForm()">
Submit
</div>
A screen reader user will hear “Submit” but won’t know it’s meant to be a button.
Correct: Using a Native HTML Button
<button type="button" onclick="submitForm()">
Submit
</button>
The <button> element has a built-in button role. The screen reader announces “Submit, button.”
Correct: Adding an ARIA Role to a Custom Widget
If you cannot use a native <button>, add the appropriate role and keyboard support:
<div role="button" tabindex="0" onclick="submitForm()" onkeydown="handleKeydown(event)">
Submit
</div>
Now the screen reader announces “Submit, button.” You must also implement onkeydown to handle Enter and Space key presses, since a <div> doesn’t natively support those interactions.
Incorrect: Using an Abstract Role
<div role="command" tabindex="0">
Save
</div>
The command role is abstract and must not be used on elements. Assistive technologies will not recognize it as a valid role.
Correct: Replacing the Abstract Role
<div role="button" tabindex="0" onkeydown="handleKeydown(event)">
Save
</div>
Incorrect: Non-Interactive Role on a Focusable Element
<span role="paragraph" tabindex="0" onclick="openMenu()">
Options
</span>
The paragraph role is not interactive. The element will not behave as expected for assistive technology users, and may not even receive focus in some screen readers.
Correct: Using an Appropriate Widget Role
<span role="button" tabindex="0" onclick="openMenu()" onkeydown="handleKeydown(event)">
Options
</span>
Incorrect: Focusable Custom Checkbox Without a Role
<div tabindex="0" class="custom-checkbox" onclick="toggleCheck(this)">
Accept terms
</div>
Correct: Custom Checkbox With Proper Role and State
<div role="checkbox" tabindex="0" aria-checked="false" onclick="toggleCheck(this)" onkeydown="handleKeydown(event)">
Accept terms
</div>
The role="checkbox" tells the screen reader this is a checkbox, and aria-checked communicates its current state. Remember to update aria-checked in your JavaScript when the state changes.
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>
Headings (h1 through h6) serve as a structural outline of your page. Sighted users can visually scan a page and understand its organization through font sizes and visual weight, but screen reader users depend entirely on properly marked-up heading levels to achieve the same understanding. When heading levels are skipped — for example, jumping from an h2 to an h4 — it creates confusion because users can’t tell whether they missed a subsection or if the structure is simply incorrect.
This rule primarily affects users who are blind, deafblind, or have mobility impairments and rely on assistive technology to navigate. Screen readers provide keyboard shortcuts that let users jump between headings, effectively creating a table of contents for the page. If the heading hierarchy has gaps, users may waste time trying to find content they assume exists in the missing level, or they may misunderstand the relationship between sections.
This is a Deque best practice rule. While it aligns with the intent of WCAG Success Criterion 1.3.1 (Info and Relationships) and Success Criterion 2.4.6 (Headings and Labels), the sequential ordering of heading levels is not an explicit WCAG requirement. However, maintaining a logical heading order is widely considered essential for usable, accessible content.
How to Fix It
-
Audit your heading structure. Review all headings on the page and verify they follow a sequential order. An
h1should be followed by anh2, anh2by anh3, and so on. You can go back up to a higher level at any time (e.g., fromh3back toh2), but you should never skip levels going down. -
Start main content with an
h1. Screen reader users can jump directly to the firsth1on a page, so placing it at the beginning of your main content lets them skip navigation and other preamble. - Don’t use headings for visual styling. If you need text to look bigger or bolder, use CSS instead of heading elements. Heading markup should only be used for actual headings that describe the content that follows.
- Read only the headings. A quick test: read through just the headings on your page. Do they give you a clear sense of the page’s structure and content? If not, revise them.
Examples
Incorrect: Skipped Heading Levels
This example jumps from h1 to h3, skipping the h2 level entirely.
<h1>Photography Basics</h1>
<p>Learn the fundamentals of photography.</p>
<h3>Understanding ISO</h3>
<p>ISO controls the sensor's sensitivity to light.</p>
<h3>Choosing an Aperture</h3>
<p>Aperture affects depth of field and light intake.</p>
Correct: Sequential Heading Levels
The headings follow a logical order without skipping any levels.
<h1>Photography Basics</h1>
<p>Learn the fundamentals of photography.</p>
<h2>Understanding ISO</h2>
<p>ISO controls the sensor's sensitivity to light.</p>
<h2>Choosing an Aperture</h2>
<p>Aperture affects depth of field and light intake.</p>
Correct: Deeper Nesting with Proper Hierarchy
When content has subsections, each level increments by one. You can return to a higher level when starting a new major section.
<h1>Setting Exposure Manually on a Camera</h1>
<p>Manual exposure involves three key settings.</p>
<h2>Set the ISO</h2>
<p>Start by choosing an ISO value.</p>
<h3>Low Light Conditions</h3>
<p>Use a higher ISO in dim environments.</p>
<h3>Bright Light Conditions</h3>
<p>Use a lower ISO outdoors in sunlight.</p>
<h2>Choose an Aperture</h2>
<p>Aperture is measured in f-stops.</p>
<h2>Set a Shutter Speed</h2>
<p>Shutter speed controls motion blur.</p>
Incorrect: Using Headings for Visual Styling
Here, an h4 is used not because it fits the document hierarchy but because the developer wanted smaller text.
<h1>Our Services</h1>
<p>We offer a range of professional services.</p>
<h4>Contact us today for a free quote!</h4>
Correct: Using CSS Instead of Heading Markup for Styling
<h1>Our Services</h1>
<p>We offer a range of professional services.</p>
<p class="callout">Contact us today for a free quote!</p>
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.
When an interactive element displays visible text — like a button saying “Submit” — users naturally expect that text to be the element’s name. However, developers sometimes use aria-label or aria-labelledby to set an accessible name that differs from the visible text. This creates a disconnect: what sighted users see and what assistive technologies announce become two different things.
This is a serious accessibility problem that primarily affects speech input users. These users interact with web pages by speaking the names of controls they see on screen. If someone sees a link labeled “Next Page” and says “click Next Page,” but the accessible name is actually “OK,” the speech command fails. The user has no way to know the correct programmatic name, leading to frustration and an inability to use the interface.
This issue also affects screen reader users and users with cognitive disabilities. When a screen reader announces a name that doesn’t match the visible label, it creates confusion — the user may not be sure they’re focused on the right element. For users with cognitive disabilities who rely on consistent, predictable interfaces, this mismatch adds unnecessary complexity.
This rule checks compliance with WCAG 2.1 Success Criterion 2.5.3: Label in Name (Level A). This criterion requires that when a user interface component has a visible text label, the accessible name must contain that visible text. The intent is to ensure that the words users see on screen can be used to interact with the component, regardless of input method.
The rule applies to elements that meet all three conditions:
-
The element has a widget role that supports name from content — specifically:
button,checkbox,gridcell,link,menuitem,menuitemcheckbox,menuitemradio,option,radio,searchbox,switch,tab, ortreeitem. - The element has visible text content.
-
The element has an
aria-labeloraria-labelledbyattribute that overrides the default accessible name.
When evaluating the match, leading and trailing whitespace is ignored, and the comparison is case-insensitive. The complete visible text must either match the accessible name exactly or be fully contained within it.
How to Fix
To resolve this issue:
-
Make the accessible name include the full visible text. If the element’s visible text is “Next Page,” the
aria-labelmust contain “Next Page” — not just part of it, and not something completely different. - Start the accessible name with the visible text. While not strictly required, it’s best practice. This helps speech input users who may only speak the beginning of a label.
-
Consider removing the
aria-labelentirely. If the visible text alone is a sufficient accessible name, you may not need an override at all. The simplest fix is often to let the element derive its name from its content naturally.
Examples
Failing: Accessible name doesn’t match visible text
The visible text says “Next” but the accessible name is “OK”:
<div role="link" aria-label="OK">Next</div>
Failing: Accessible name only contains part of the visible text
The visible text is “The full label” but the accessible name is “the full,” which doesn’t include the complete visible text:
<button aria-label="the full">The full label</button>
Passing: Accessible name matches visible text
The aria-label matches the visible text (trailing whitespace and case differences are ignored):
<div role="link" aria-label="Next Page">next page</div>
Passing: Accessible name contains the full visible text
The visible text “Next Page” is fully contained within the accessible name:
<button aria-label="Next Page in the list">Next Page</button>
Passing: No aria-label override needed
When the visible text is already a good accessible name, simply omit the aria-label:
<button>Next Page</button>
Passing: aria-labelledby references text that includes the visible content
<span id="btn-label">Search the full catalog</span>
<button aria-labelledby="btn-label">Search</button>
Here, the visible text “Search” is contained within the accessible name “Search the full catalog,” so the rule passes. The accessible name begins with the visible label, which is ideal for speech input users.
The title and aria-describedby attributes serve a supplementary role in accessibility — they convey hints, tooltips, or additional descriptions. However, they are not treated as primary labels by accessibility APIs. When a screen reader encounters a form field that has only a title or aria-describedby attribute and no proper label, it may present the text as advisory information rather than as the field’s name. This makes it difficult or impossible for users to understand what input is expected.
This issue primarily affects users who are blind, deafblind, or who rely on assistive technologies such as screen readers. It can also impact users with mobility impairments who use voice control software, since voice control often relies on visible labels to target form elements. A missing visible label also hurts sighted users who may not see a tooltip until they hover over the element.
This rule is classified as a Deque Best Practice and aligns with the broader intent of WCAG Success Criterion 1.3.1 (Info and Relationships), which requires that information and relationships conveyed visually are also available programmatically, and SC 2.4.6 (Labels or Instructions), which requires that labels describe the purpose of form controls. While title and aria-describedby do expose some text, they do not fulfill the requirement for a reliable, programmatically determinable label.
How to Fix It
Every form control needs a proper accessible name. You can provide one using any of these methods:
-
Explicit
<label>element — Associate a<label>with the form control using matchingforandidattributes. This is the most reliable and widely supported approach. -
Implicit
<label>element — Wrap the form control inside a<label>element. This works in most assistive technologies but has inconsistent support in some combinations (e.g., JAWS with<select>menus). -
aria-labelledby— Reference visible text elsewhere on the page by itsid. Useful for complex labeling scenarios. -
aria-label— Apply an invisible label directly to the element. Use this only when a visible label is truly not feasible.
In most cases, an explicit <label> is the best choice. It creates a clear association, provides a larger click/tap target, and is universally supported.
Examples
Incorrect: Using Only title
The title attribute provides a tooltip on hover but does not create a proper accessible label.
<input type="text" title="Enter your email address">
Incorrect: Using Only aria-describedby
The aria-describedby attribute provides a description, not a label. Screen readers may announce this text differently or skip it in certain contexts.
<p id="email-hint">Enter your email address</p>
<input type="text" aria-describedby="email-hint">
Correct: Explicit <label> Element
This is the recommended approach. The for attribute on the <label> matches the id on the input, creating an unambiguous association.
<label for="email">Email Address</label>
<input type="text" id="email" name="email">
You can still use title or aria-describedby for supplementary hints alongside a proper label:
<label for="email">Email Address</label>
<input type="text" id="email" name="email" title="e.g., user@example.com">
Correct: Implicit <label> Element
Wrapping the input inside the <label> creates an implicit association. This works for most assistive technologies but is less reliable than explicit labels.
<label>
Email Address
<input type="text" name="email">
</label>
Correct: Using aria-labelledby
Reference visible text on the page by its id. This is useful when the labeling structure is complex, such as data tables with form controls, or when multiple elements share a label.
<p id="email-label">Email Address</p>
<input type="text" aria-labelledby="email-label">
Correct: Using aria-label
This provides a label that is invisible on screen. Use it only when a visible label cannot be provided, such as a search field with a clearly identifiable search icon and button.
<input type="text" aria-label="Search">
<button type="submit">Search</button>
Note: Because aria-label is not visible, sighted users get no label text. Prefer a visible <label> whenever possible.
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>
Landmark regions are structural areas of a page — like <main>, <nav>, <header>, <footer>, and <aside> — that help assistive technology users understand the layout and quickly navigate between sections. Screen readers provide shortcut keys that let users jump directly to these landmarks, making it easy to skip to the content they need.
The <aside> element (or role="complementary") represents content that supplements the main content, such as sidebars, related links, or additional context. When an <aside> is nested inside another landmark like <main> or <nav>, screen readers may not expose it as a top-level landmark. This means users who rely on landmark navigation may not be able to discover or jump to that complementary content at all, effectively hiding it from their navigation flow.
This issue primarily affects screen reader users, who depend on landmarks as a primary way to orient themselves and move through a page. When landmarks are improperly nested, the page structure becomes confusing or incomplete, reducing the efficiency and usability of assistive technology.
This rule relates to WCAG 2.1 Success Criterion 1.3.1 (Info and Relationships), which requires that information, structure, and relationships conveyed visually are also available programmatically. It also supports WCAG 2.1 Success Criterion 4.1.2 (Name, Role, Value) and the general best practice of using landmarks correctly so that assistive technologies can present content structure accurately.
How to Fix It
-
Move
<aside>elements to the top level of the document body, outside of<main>,<nav>,<header>,<footer>, or any other landmark. -
Check for
role="complementary"on any elements and ensure they are also placed at the top level, not nested inside another landmark. -
If the content truly belongs inside another landmark and is not meant to be independently navigable, consider whether it actually needs to be an
<aside>at all. A simple<div>or<section>without a landmark role may be more appropriate.
Examples
Incorrect: <aside> nested inside <main>
<main>
<h1>Article Title</h1>
<p>Main article content goes here.</p>
<aside>
<h2>Related Links</h2>
<ul>
<li><a href="/topic-a">Topic A</a></li>
<li><a href="/topic-b">Topic B</a></li>
</ul>
</aside>
</main>
In this example, the <aside> is inside <main>, so screen readers may not list it as a top-level landmark. Users navigating by landmarks could miss the related links entirely.
Correct: <aside> at the top level alongside <main>
<main>
<h1>Article Title</h1>
<p>Main article content goes here.</p>
</main>
<aside>
<h2>Related Links</h2>
<ul>
<li><a href="/topic-a">Topic A</a></li>
<li><a href="/topic-b">Topic B</a></li>
</ul>
</aside>
Now the <aside> is a sibling of <main>, making it a top-level landmark that screen reader users can easily discover and navigate to.
Incorrect: role="complementary" nested inside <nav>
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
<div role="complementary">
<p>Navigation tip: Use keyboard shortcuts to browse faster.</p>
</div>
</nav>
Correct: role="complementary" moved to the top level
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<div role="complementary" aria-label="Navigation tips">
<p>Navigation tip: Use keyboard shortcuts to browse faster.</p>
</div>
By keeping complementary landmarks at the top level of the page, you ensure all users — including those using assistive technology — can discover and navigate to every section of your content.
The <main> element (or role="main") represents the dominant content of a page — the content that is directly related to the central topic or functionality. Screen reader users rely on landmark navigation to jump quickly between major sections of a page, such as the banner, navigation, main content, and footer. When the <main> landmark is nested inside another landmark, this hierarchy becomes confusing. A screen reader might present it as a subsection of, say, the navigation or banner, making it harder for users to understand the page structure and locate the primary content efficiently.
This rule primarily affects users who are blind, deafblind, or have mobility impairments and depend on assistive technologies to navigate by landmarks. When landmarks are properly structured as flat, top-level regions, users can cycle through them predictably and efficiently.
Why landmark hierarchy matters
The HTML Living Standard specifies that a hierarchically correct <main> element is one whose ancestor elements are limited to <html>, <body>, <div>, <form> without an accessible name, and autonomous custom elements. This means <main> should never appear inside <header>, <nav>, <aside>, <footer>, or any element with a landmark role.
Think of landmarks as the top-level sections of your page. They should be siblings, not nested within each other. A well-structured page has a clear, flat hierarchy of landmarks:
-
Banner (
<header>orrole="banner") — site-wide header content -
Navigation (
<nav>orrole="navigation") — navigation links -
Main (
<main>orrole="main") — primary page content -
Contentinfo (
<footer>orrole="contentinfo") — footer content
This rule is classified as a Deque best practice and supports the broader principle of providing clear, navigable page structure for assistive technology users.
How to fix it
-
Move
<main>out of any enclosing landmark. If your<main>element is nested inside a<header>,<nav>,<aside>,<footer>, or any element with an ARIA landmark role, restructure your markup so<main>is a direct child of<body>(or only wrapped in non-landmark elements like<div>). - Ensure landmarks are siblings, not nested. The primary landmarks — banner, navigation, main, and contentinfo — should sit at the same level in the DOM.
-
Use both HTML5 elements and ARIA roles for maximum compatibility. While modern browsers and screen readers handle HTML5 landmark elements well, adding the corresponding ARIA role (e.g.,
<main role="main">) provides a fallback for older assistive technologies.
Examples
Incorrect: <main> nested inside another landmark
In this example, the <main> element is nested inside the <nav> landmark, which breaks the expected landmark hierarchy.
<header role="banner">
<p>Company Logo</p>
</header>
<nav role="navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
<main role="main">
<p>This is the primary content, incorrectly nested inside nav.</p>
</main>
</nav>
<footer role="contentinfo">
<p>Copyright 2024</p>
</footer>
Incorrect: <main> nested inside <aside>
<aside role="complementary">
<main role="main">
<p>Main content should not be inside a complementary landmark.</p>
</main>
</aside>
Correct: <main> as a top-level landmark
All landmarks are siblings at the top level of the document. The <main> element is not contained within any other landmark.
<header role="banner">
<p>Company Logo</p>
</header>
<nav role="navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<main role="main">
<p>This is the primary content of the page.</p>
</main>
<footer role="contentinfo">
<p>Copyright 2024</p>
</footer>
Correct: <main> wrapped in a non-landmark <div>
Wrapping <main> in a <div> is acceptable because <div> is not a landmark element.
<header role="banner">
<p>Company Logo</p>
</header>
<div class="content-wrapper">
<main role="main">
<p>This is the primary content of the page.</p>
</main>
</div>
<footer role="contentinfo">
<p>Copyright 2024</p>
</footer>
Landmarks are semantic regions that help assistive technology users understand the structure of a page and jump between sections efficiently. The main landmark specifically identifies the primary content area — the unique content that distinguishes this page from others on the site. When multiple main landmarks exist, screen readers present all of them in the landmarks list, making it unclear which one contains the actual primary content.
This issue primarily affects blind and deafblind users who rely on screen readers, as well as keyboard-only users with mobility disabilities who use landmark-based navigation. Screen readers like JAWS, NVDA, and VoiceOver allow users to press a shortcut key to jump directly to the main landmark. If there are two or more main landmarks, the user must guess which is correct, defeating the purpose of this navigational aid.
While this rule is categorized as a Deque best practice rather than a specific WCAG success criterion violation, it strongly supports WCAG 1.3.1 (Info and Relationships) and WCAG 2.4.1 (Bypass Blocks). Properly structured landmarks help users understand page organization and skip repetitive content.
How to Fix It
-
Audit your page for duplicate
mainlandmarks. Search your HTML for multiple<main>elements or multiple uses ofrole="main". Remember that a<main>element has an implicitrole="main", so a<main>and a<div role="main">on the same page creates a duplicate. -
Consolidate into a single
mainlandmark. Wrap all primary content in one<main>element and remove any others. -
Check iframes. If your page contains
<iframe>elements, ensure each iframe’s document also has at most onemainlandmark. -
Use complementary landmarks for other sections. Content that is not the primary focus — such as sidebars — should use
<aside>(orrole="complementary") instead of<main>.
It’s also a good practice to structure your entire page with semantic HTML5 landmark elements — <header>, <nav>, <main>, and <footer> — so all content is contained within a recognizable region. You can pair these with their ARIA equivalents (role="banner", role="navigation", role="main", role="contentinfo") for maximum compatibility across screen readers, though modern browsers and assistive technologies handle the HTML5 elements well on their own.
Examples
Incorrect: Multiple main Landmarks
This page has two main landmarks, which makes it ambiguous for screen reader users.
<header>
<h1>My Website</h1>
</header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<main>
<h2>Articles</h2>
<p>Article content here.</p>
</main>
<main>
<h2>Sidebar</h2>
<p>Related links and info.</p>
</main>
<footer>
<p>© 2024 My Website</p>
</footer>
Incorrect: Mixing <main> with role="main"
Even though these are different elements, both create a main landmark, resulting in duplicates.
<main>
<p>Primary content here.</p>
</main>
<div role="main">
<p>More content here.</p>
</div>
Correct: Single main Landmark with Proper Page Structure
All primary content is in one <main> element, and the sidebar uses <aside> instead.
<header>
<h1>My Website</h1>
</header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<main>
<h2>Articles</h2>
<p>Article content here.</p>
</main>
<aside>
<h2>Sidebar</h2>
<p>Related links and info.</p>
</aside>
<footer>
<p>© 2024 My Website</p>
</footer>
Correct: Page with an Iframe
The parent page and the iframe document each have at most one main landmark.
<!-- Parent page -->
<main>
<h1>Dashboard</h1>
<iframe src="widget.html" title="Statistics widget"></iframe>
</main>
<!-- widget.html -->
<main>
<p>Widget content here.</p>
</main>
Landmarks are structural regions of a page — like header, navigation, main content, and footer — that assistive technologies use to build an outline of the page. When a screen reader user opens a page, they can pull up a list of landmarks and jump directly to the section they need. Without a main landmark, there is no way for these users to skip past repeated headers and navigation to reach the content they came for. This makes the page significantly harder to use.
This rule is a Deque best practice aligned with the WAI-ARIA technique ARIA11: Using ARIA landmarks to identify regions of a page. Users who are blind, deafblind, or who have mobility impairments and rely on screen readers or alternative navigation methods are the most directly affected. Without landmarks, these users must tab or arrow through every element on the page to find the content they need.
How landmarks work
HTML5 introduced semantic elements that automatically create landmark regions:
| HTML5 Element | Implicit ARIA Role |
|---|---|
<header> |
role="banner" |
<nav> |
role="navigation" |
<main> |
role="main" |
<footer> |
role="contentinfo" |
<aside> |
role="complementary" |
Modern browsers and screen readers understand these HTML5 elements natively. For maximum compatibility, you can also add the explicit ARIA role alongside the HTML5 element (e.g., <main role="main">). This redundancy is harmless and can help with older assistive technologies.
How to fix the problem
-
Wrap your primary content in a
<main>element. Every page should have exactly one<main>landmark. -
Place all other visible content inside appropriate landmarks. Use
<header>,<nav>,<footer>, and<aside>as needed so that no content is orphaned outside a landmark region. -
Check
iframeelements. If aniframecontains landmarked content, it should have no more than one<main>landmark. -
Don’t nest
<main>elements. There should be only one<main>per page (or periframe).
Note that landmarks primarily benefit screen reader users. Sighted users and screen magnifier users don’t perceive landmarks, so they still need visible skip-navigation links to bypass repeated content.
Examples
Incorrect: no main landmark
This page has no <main> element, so screen reader users have no way to jump directly to the primary content.
<header>
<p>Company Logo</p>
</header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<div class="content">
<p>This is the primary content of the page.</p>
</div>
<footer>
<p>© 2024 Company Name</p>
</footer>
Correct: page with one main landmark
Replacing the generic <div> with a <main> element gives screen reader users a direct navigation point to the primary content.
<header>
<p>Company Logo</p>
</header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<main>
<p>This is the primary content of the page.</p>
</main>
<footer>
<p>© 2024 Company Name</p>
</footer>
Correct: using both HTML5 elements and ARIA roles for maximum compatibility
<header role="banner">
<p>Company Logo</p>
</header>
<nav role="navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<main role="main">
<p>This is the primary content of the page.</p>
</main>
<footer role="contentinfo">
<p>© 2024 Company Name</p>
</footer>
Incorrect: multiple main landmarks
Having more than one <main> element confuses assistive technologies about which section is the true primary content.
<main>
<p>Article content here.</p>
</main>
<main>
<p>Sidebar content here.</p>
</main>
Correct: single main with an aside for secondary content
<main>
<p>Article content here.</p>
</main>
<aside>
<p>Sidebar content here.</p>
</aside>
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;
}
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 refreshes automatically, the browser reloads the entire document and moves focus back to the top of the page. This means a user who was partway through reading content or filling out a form suddenly loses their place with no warning. For screen reader users, this is particularly disruptive — they must navigate from the beginning of the page again. Users with cognitive disabilities or those who read more slowly may not have finished processing the content before it disappears. People with mobility impairments who navigate slowly with alternative input devices are also affected, as their progress through the page is reset.
Delayed refreshes also constitute an interruption that the user cannot suppress, postpone, or control. Even if the delay is long (e.g., 60 seconds), the user has no way to opt out or extend the timer.
Related WCAG Success Criteria
This rule relates to two AAA-level WCAG success criteria:
- WCAG 2.2.4 (Interruptions): Interruptions must be able to be postponed or suppressed by the user, except in emergencies. An automatic page refresh is an interruption that the user cannot control.
- WCAG 3.2.5 (Change on Request): Changes of context must be initiated only by user request, or a mechanism must be available to turn off such changes. An automatic refresh or redirect is a change of context that occurs without user action.
How to Fix It
-
Remove the
http-equiv="refresh"attribute from everymetaelement that contains it. -
For redirects, use server-side HTTP redirects (e.g., a
301or302status code) instead of client-side meta refresh. This is more reliable and does not cause accessibility issues. - For periodic content updates, use JavaScript to fetch and update only the changed content without reloading the entire page. Provide users with controls to pause, extend, or disable the automatic updates.
Examples
Incorrect: Using Meta Refresh to Redirect After a Delay
This refreshes the page after 40 seconds, redirecting the user to a new URL without their consent:
<head>
<meta http-equiv="refresh" content="40; url=https://example.com/new-page">
</head>
Incorrect: Using Meta Refresh to Reload the Page
This reloads the current page every 60 seconds:
<head>
<meta http-equiv="refresh" content="60">
</head>
Correct: Server-Side Redirect
Instead of using a meta refresh for redirection, configure your server to return an HTTP redirect. For example, in an .htaccess file:
Redirect 301 /old-page https://example.com/new-page
The HTML page itself contains no meta refresh:
<head>
<meta charset="utf-8">
<title>My Page</title>
</head>
Correct: JavaScript with User Control for Content Updates
If you need to periodically update content, use JavaScript and give the user the ability to control the behavior:
<head>
<meta charset="utf-8">
<title>Live Dashboard</title>
</head>
<body>
<h1>Live Dashboard</h1>
<button id="toggle-refresh">Pause Auto-Refresh</button>
<div id="content">
<p>Dashboard content goes here.</p>
</div>
<script>
let refreshInterval = setInterval(updateContent, 60000);
let isActive = true;
document.getElementById("toggle-refresh").addEventListener("click", function () {
if (isActive) {
clearInterval(refreshInterval);
this.textContent = "Resume Auto-Refresh";
} else {
refreshInterval = setInterval(updateContent, 60000);
this.textContent = "Pause Auto-Refresh";
}
isActive = !isActive;
});
function updateContent() {
// Fetch and update only the content area
}
</script>
</body>
This approach keeps the user in control. They can pause updates when they need more time to read content, and resume when ready — satisfying both WCAG 2.2.4 and 3.2.5.
What This Rule Checks
The axe rule meta-refresh-no-exceptions checks for the presence of the http-equiv="refresh" attribute on any meta element in the document. If found, the rule flags it as a violation regardless of the delay value, since even long delays deny the user control over when the refresh occurs.
Pronto para validar os seus sites?
Comece o seu teste gratuito hoje.