What a Screen Reader Hears

A screen reader announces each part of a page by its role, its name and its state, all read from your HTML, and the developer tools show you exactly what it will say.

Journeyman · HTML · Web Development · Updated

After this lesson you can

  • Describe how a screen reader announces an element by its role, its name and its state
  • Work out an element's accessible name from its text, its label or its alt text
  • Explain why a div styled to look like a button doesn't work as one

Key takeaways

  • The browser builds an accessibility tree from your HTML, and a screen reader reads the page from it.
  • Each part of the page is announced by its role (what it is), its name (what it's called) and its state (what it's doing now).
  • An element's accessible name comes from its text, its label or its alt text.
  • A div styled as a button has no button role, can't be reached with Tab and ignores Enter, so for many people it isn't a button at all.
  • The Accessibility pane in the developer tools shows each element's role, name and state.

A bug report comes in: "On the checkout page, my screen reader says button, button, button and nothing else." Or a teammate asks why the screen reader skips the "Buy tickets" button completely. To fix either one, you need to know what a screen reader actually hears when it meets your page. This lesson shows you, and once you've heard it, you'll never look at HTML the same way again.

The page, read aloud

Think of a museum's audio guide. You don't see the paintings through it: you hear "Room 3. Painting. Sunflowers, by Vincent van Gogh." It tells you what kind of thing is in front of you, what it's called, and anything you need to know about it right now.

A screen reader does that for a web page. While the browser draws the page on the screen, it also builds a second version of it, a list of everything a person can read or use, called the accessibility tree. The screen reader reads from that tree, not from the pixels. For each item it says up to three things:

  • its role: what kind of thing it is, such as a heading, a link or a button;
  • its name: what it's called, such as "Buy tickets";
  • its state: what it's doing right now, such as checked or expanded.

Here's a piece of the Starlight Cinema's page, and roughly what a screen reader says for it (each screen reader words it a little differently, and some say the role first):

<h2>This Week's Movies</h2>
<a href="tickets.html">Ticket prices</a>
<button type="submit">Sign up</button>
<input type="checkbox" id="reminders" checked>
<label for="reminders">Send me a reminder</label>
This Week's Movies, heading, level 2
Ticket prices, link
Sign up, button
Send me a reminder, checkbox, checked

None of the words in that output were written by hand. The browser worked them all out from the HTML.

Role: what kind of thing it is

The role tells a listener what they can do with something. "Link" means pressing Enter goes somewhere. "Button" means pressing it does something here. "Heading, level 2" means a new part of the page starts. Every HTML element that means something has a role built in:

Element Role a screen reader announces
<a href="..."> link
<button> button
<h1> to <h6> heading, with its level
<input type="checkbox"> checkbox
<nav>, <main> navigation, main (landmarks)
<ul> with three <li> list, 3 items
<div>, <span> no role: read as plain text

That last row matters. A <div> and a <span> are boxes with no meaning, so the accessibility tree gives them a role of "generic", and the screen reader says nothing about what they are.

Name: what it's called

The accessible name is the name a screen reader says. The browser takes it from different places for different elements:

<!-- From its text: the link's name is "Ticket prices" -->
<a href="tickets.html">Ticket prices</a>

<!-- From its label: the box's name is "Your email" -->
<label for="email">Your email</label>
<input type="email" id="email" name="email">

<!-- From its alt text: the link's name is "Starlight Cinema home" -->
<a href="index.html"><img src="logo.svg" alt="Starlight Cinema home"></a>

Notice the last one: a link that holds only a picture gets its name from the picture's alt text. Without the alt, that link would have no name, and many screen readers would read out its address or the picture's file name instead.

And notice what doesn't count. A placeholder isn't a name you can count on: some screen readers read it, others don't, and it disappears the moment someone types. A title attribute is a last resort the browser falls back on, and it doesn't show up on phones.

Try it. For each element in the Starlight Cinema's header, say its name out loud before you check: the logo link, "Showtimes", "Tickets", "Newsletter". Where does each name come from?

State: what it's doing right now

Some things change while the page is open. A checkbox gets ticked. A section opens. The state tells the listener which way it is right now:

<details>
  <summary>Snack bar menu</summary>
  <p>Popcorn, nachos, pretzels and fresh lemonade.</p>
</details>

<input type="checkbox" id="text-alerts" checked>
<label for="text-alerts">Text me when tickets go on sale</label>
Snack bar menu, button, collapsed
Text me when tickets go on sale, checkbox, checked

Press Enter on the summary and the screen reader says "expanded". You wrote no extra code for any of that: <details> and <input type="checkbox"> keep their state up to date in the accessibility tree by themselves.

Seeing the tree in the developer tools

You don't need a screen reader to find out what one will say. The browser's developer tools show the accessibility tree:

  1. Open the Starlight Cinema's page in Chrome or Edge, right-click "Ticket prices" and choose Inspect.
  2. In the Elements panel, find the Accessibility tab beside Styles and Computed. (If you can't see it, open the » menu there.)
  3. Under Computed Properties you'll see the element's Name and Role, and where the name came from.

In Firefox, the same information is in its own Accessibility panel. In Safari, it's in the Elements tab's Node sidebar, under Accessibility.

The Accessibility tab in Chrome's developer tools, showing the "Ticket prices" link with Name "Ticket prices" and Role "link": the same words a screen reader will say

Try it. Inspect any button on a site you use every day. What's its name? Is it the same as the words you see on it?

Why a div styled as a button doesn't work as one

Here's the Starlight Cinema's "Buy tickets" control, the way someone built it. With CSS it looks exactly like a button, and clicking it with a mouse works:

<div class="button" onclick="location.href='tickets.html'">Buy tickets</div>

Now meet it the other ways:

  • With a screen reader, it's read as the plain text "Buy tickets", like a sentence in a paragraph, with no "button" and no "link". Nothing tells the listener they can press it.
  • With the keyboard, Tab skips it. A <div> can't take focus, so a keyboard user can never reach it.
  • Pressing Enter or Space wouldn't do anything anyway: a <div> doesn't listen for them.

For everyone who doesn't use a mouse, the page has no way to buy tickets. The fix is the element that already does all of this:

<a class="button" href="tickets.html">Buy tickets</a>

It goes to another page, so it's a link: announced as "Buy tickets, link", reached with Tab, followed with Enter. (Something that does an action on this page, such as sending a form, is a <button>.) The same CSS class keeps the look.

Try it. Open any page, press Tab a few times and watch where the focus ring goes. If something looks clickable but the ring never lands on it, you may have found a <div> pretending to be a button.

Check yourself

2 questions to try on the lesson page.

So what

A screen reader can only say what your HTML tells it, so every element you choose is a sentence someone will hear: choose the one that says what the thing is.

Key terms

Cheat sheets