Common Mistakes
Real-world HTML patterns that break screen reader experiences — and how to fix them. Each example shows the bad markup, what screen readers actually announce, and the corrected version.
Button with no accessible name
Icon-only buttons without text or aria-label are announced as just "button" — users have no idea what it does.
<button> <svg viewBox="0 0 24 24"><!-- icon --></svg> </button>
"button"
"button"
Screen readers announce "button" with no context. The user has to guess what this button does. This is one of the most common accessibility failures.
<button aria-label="Close dialog"> <svg viewBox="0 0 24 24" aria-hidden="true"><!-- icon --></svg> </button>
"Close dialog, button"
"Close dialog, button"
Skipped heading levels
Jumping from h1 to h3 breaks the document outline. Screen reader users navigate by heading level and expect a logical hierarchy.
<h1>Dashboard</h1> <h3>Recent Activity</h3> <h3>Settings</h3>
"Dashboard, heading level 1 Recent Activity, heading level 3 Settings, heading level 3"
"heading level 1, Dashboard heading level 3, Recent Activity heading level 3, Settings"
Users navigating by heading level will look for h2 and find nothing. The jump from h1 to h3 suggests missing content. Speakable's audit report flags this as an error.
<h1>Dashboard</h1> <h2>Recent Activity</h2> <h2>Settings</h2>
"Dashboard, heading level 1 Recent Activity, heading level 2 Settings, heading level 2"
"heading level 1, Dashboard heading level 2, Recent Activity heading level 2, Settings"
Duplicate landmarks without labels
Multiple nav elements without aria-label are indistinguishable to screen reader users.
<nav> <a href="/">Home</a> <a href="/about">About</a> </nav> <!-- ... page content ... --> <nav> <a href="/privacy">Privacy</a> <a href="/terms">Terms</a> </nav>
"navigation landmark Home, link About, link navigation landmark Privacy, link Terms, link"
"navigation Home, link About, link navigation Privacy, link Terms, link"
Both navs are announced identically. When a user opens the landmarks list, they see two "navigation" entries with no way to tell them apart.
<nav aria-label="Main navigation"> <a href="/">Home</a> <a href="/about">About</a> </nav> <!-- ... page content ... --> <nav aria-label="Footer links"> <a href="/privacy">Privacy</a> <a href="/terms">Terms</a> </nav>
"Main navigation, navigation landmark Home, link About, link Footer links, navigation landmark Privacy, link Terms, link"
"navigation, Main navigation Home, link About, link navigation, Footer links Privacy, link Terms, link"
Image without alt text
Images without alt attributes are announced with their filename or as just "image" — meaningless to screen reader users.
<img src="/images/team-photo.jpg" />
"graphic"
"image"
The user knows there's an image but has no idea what it shows. For informative images, always provide descriptive alt text. For decorative images, use alt="" to hide them from screen readers.
<!-- Informative image --> <img src="/images/team-photo.jpg" alt="The Speakable team at a conference booth" /> <!-- Decorative image --> <img src="/images/divider.svg" alt="" />
"The Speakable team at a conference booth, graphic"
"The Speakable team at a conference booth, image"
Form input without label
Inputs without associated labels are announced as just "edit" — users don't know what to type.
<input type="email" placeholder="Enter your email" />
"edit"
"edit text"
Placeholder text is not a substitute for a label. Most screen readers don't announce placeholder text as the accessible name. The input is effectively unlabeled.
<label for="email">Email address</label> <input type="email" id="email" placeholder="name@company.com" />
"Email address, edit"
"Email address, edit text"
Div used as a button
Using a div with an onClick handler instead of a button element loses keyboard support, focus management, and role announcement.
<div class="btn" onclick="handleClick()"> Submit </div>
"Submit"
"Submit"
No "button" role is announced. The element isn't focusable via Tab. Keyboard users can't activate it with Enter or Space. Screen reader users don't know it's interactive.
<button type="submit"> Submit </button>
"Submit, button"
"Submit, button"
Link with no text
Links wrapping only an icon or image with no accessible name are announced as just "link" — users can't tell where it goes.
<a href="/settings"> <svg viewBox="0 0 24 24"><!-- gear icon --></svg> </a>
"link"
"link"
The link has no accessible name. Screen reader users hear "link" with no destination or purpose. This is especially common in icon-based navigation.
<a href="/settings" aria-label="Settings"> <svg viewBox="0 0 24 24" aria-hidden="true"><!-- gear icon --></svg> </a>
"Settings, link"
"Settings, link"
Redundant aria-label on a link
Adding aria-label that duplicates the visible text creates a confusing double announcement or hides the visible text from screen readers.
<a href="/pricing" aria-label="Click here to see pricing"> See Pricing </a>
"Click here to see pricing, link"
"Click here to see pricing, link"
The aria-label overrides the visible text. Sighted users see "See Pricing" but screen reader users hear "Click here to see pricing" — a mismatch that breaks the shared experience. Also, "click here" is not descriptive.
<a href="/pricing"> See Pricing </a>
"See Pricing, link"
"See Pricing, link"
Multiple h1 elements
Having more than one h1 on a page confuses the document structure. Screen reader users expect a single h1 as the page title.
<h1>Speakable</h1> <main> <h1>Documentation</h1> <p>Welcome to the docs.</p> </main>
"Speakable, heading level 1 Documentation, heading level 1"
"heading level 1, Speakable heading level 1, Documentation"
Two h1 elements suggest two separate pages or documents. Users navigating by heading can't determine which is the actual page title.
<header> <span class="logo">Speakable</span> </header> <main> <h1>Documentation</h1> <p>Welcome to the docs.</p> </main>
"Documentation, heading level 1"
"heading level 1, Documentation"
Disabled button without accessible state
Using CSS to visually disable a button without the disabled attribute or aria-disabled means screen readers don't know it's inactive.
<button class="opacity-50 cursor-not-allowed"> Submit </button>
"Submit, button"
"Submit, button"
The button looks disabled visually but screen readers announce it as a normal, active button. Users will try to activate it and nothing will happen — with no explanation why.
<button disabled> Submit </button>
"Submit, button, unavailable"
"Submit, button, dimmed"
Catch these automatically
Run speakable page.html -f audit to detect many of these issues automatically. The audit report flags missing names, heading hierarchy violations, unnamed landmarks, and more — with specific remediation suggestions for each.