usability.cat
Issue Wiki

Screen Reader Issues

Someone is reading your site aloud — badly. Here's how to make sure screen readers tell the right story.

What is this?

Imagine someone is reading your website aloud to a friend over the phone, but they are terrible at it. They read the decorative dividers ("dash dash dash dash"). They announce every icon ("image, image, image"). They skip important context ("button... button... button..."). They read things in the wrong order. That is what your site sounds like to screen reader users when the HTML is not structured properly.

Screen readers are software that converts web content to speech or braille. They depend entirely on your HTML to know what to say. Bad HTML means a bad experience.

Why it matters

  • For your visitors: Approximately 7.6 million people in the US alone use screen readers. They are not just people who are blind — many have low vision, learning disabilities, or cognitive differences. When your site confuses a screen reader, these visitors get a garbled, frustrating experience.
  • For your business: Screen reader users are potential customers, subscribers, and advocates. If they cannot use your site, they will use your competitor's. Word travels fast in accessibility communities.
  • The standard: Multiple WCAG guidelines apply: 1.3.1 (Info and Relationships), 1.3.2 (Meaningful Sequence), 2.4.6 (Headings and Labels), and 4.1.2 (Name, Role, Value). Screen reader issues usually indicate several WCAG failures at once.
Screen reader friendly
<nav aria-label="Main navigation">
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>
<main>
  <h1>Welcome</h1>
  <p>Content here.</p>
  <img src="/chart.png" alt="Revenue grew 40% in Q4" />
  <button aria-label="Close" onclick="closeModal()">
    <svg aria-hidden="true"><!-- X icon --></svg>
  </button>
</main>
Screen reader nightmare
<div class="nav">
  <div onclick="goto('/')">Home</div>
  <div onclick="goto('/about')">About</div>
</div>
<div class="content">
  <div class="big-text">Welcome</div>
  <div>Content here.</div>
  <img src="/chart.png" />
  <div onclick="closeModal()">
    <svg><!-- X icon --></svg>
  </div>
</div>

How to fix it

React / Next.js

The biggest wins come from using the right HTML elements and adding context where needed.

// src/components/dashboard.tsx

export function Dashboard() {
  return (
    <>
      {/* Use landmark elements so screen readers can jump between sections */}
      <nav aria-label="Dashboard navigation">
        <ul>
          <li>
            <a href="/dashboard">Overview</a>
          </li>
          <li>
            <a href="/dashboard/analytics">Analytics</a>
          </li>
        </ul>
      </nav>

      <main>
        <h1>Dashboard Overview</h1>

        {/* Use semantic elements, not divs for everything */}
        <section aria-labelledby="stats-heading">
          <h2 id="stats-heading">Your Stats</h2>

          {/* Hide decorative content from screen readers */}
          <div aria-hidden="true" className="decorative-line" />

          {/* Announce dynamic content changes */}
          <div aria-live="polite" aria-atomic="true">
            {/* When this content updates, screen readers announce it */}
            <p>You have 5 new notifications</p>
          </div>

          {/* Tables need proper structure */}
          <table>
            <caption>Monthly revenue by product</caption>
            <thead>
              <tr>
                <th scope="col">Product</th>
                <th scope="col">Revenue</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>Pro Plan</td>
                <td>$12,400</td>
              </tr>
            </tbody>
          </table>
        </section>
      </main>
    </>
  );
}

Plain HTML

<!-- Use landmark roles (or better, semantic elements) -->
<header>
  <nav aria-label="Main">
    <ul>
      <li><a href="/">Home</a></li>
    </ul>
  </nav>
</header>

<main>
  <!-- Screen readers build a page outline from headings -->
  <h1>Page Title</h1>

  <!-- Hide decorative content -->
  <hr aria-hidden="true" />
  <img src="/squiggle.svg" alt="" aria-hidden="true" />

  <!-- Use aria-live for dynamic updates (chat messages, notifications) -->
  <div aria-live="polite">
    <!-- Updated content will be announced automatically -->
  </div>

  <!-- For custom widgets, provide context -->
  <div role="tablist" aria-label="Account settings">
    <button role="tab" aria-selected="true" aria-controls="panel-1">Profile</button>
    <button role="tab" aria-selected="false" aria-controls="panel-2">Security</button>
  </div>
  <div role="tabpanel" id="panel-1">Profile content</div>
</main>

<footer>
  <nav aria-label="Footer">
    <ul>
      <li><a href="/privacy">Privacy</a></li>
    </ul>
  </nav>
</footer>

Key principles:

  • Use semantic HTML (<nav>, <main>, <header>, <button>) instead of divs for everything
  • Hide decorative elements with aria-hidden="true"
  • Label regions with aria-label or aria-labelledby
  • Use aria-live for content that updates dynamically
High impactaccessibility~2 paws

Screen reader issues usually indicate deeper structural problems with your HTML. The cat treats these seriously because they affect the largest number of assistive technology users.

How the cat scores this

The cat simulates a screen reader's interpretation of your page. It checks for: semantic HTML landmarks (<nav>, <main>, <header>, <footer>), proper heading structure, labeled interactive elements, hidden decorative content, meaningful reading order, and announced dynamic content. Pages built entirely from <div> and <span> elements get the harshest scores because screen readers see them as undifferentiated blobs of text.

Further reading

On this page