What Screen Readers Actually See

Suppose you were designing an interface exclusively for screen reader users. Visual presentation would be irrelevant; instead, you would need to expose a structured description of the page, something akin to the DOM but leaner, containing only the semantic nodes a screen reader needs to interpret.

That simplified structure is effectively what the browser delivers to assistive technology. It takes the DOM tree and transforms it into a modified tree built for accessibility purposes: the Accessibility Tree. You can think of it as a stripped-down version of the page, much like a 1990s-era website with a few images, some links, a field, and a button, enough information for a clear but unadorned interaction.

The accessibility tree is the layer most assistive technologies rely on. The flow generally works like this:

  1. The browser exposes a semantic model of its UI to assistive technology through an accessibility API.
  2. Assistive technology uses that information to construct an alternative UI for the user. A screen reader, for example, produces a spoken representation of the app.
  3. The assistive technology can also offer alternative ways to interact. Most screen readers provide hooks that let users simulate a click or tap.
  4. When the user acts, the assistive technology sends that intent back to the app through the same API, and the app interprets the action within the context of the original UI.

For browsers, there is an extra translation step in each direction—they host web applications, so they must convert the web app into the accessibility tree while also translating incoming user actions into JavaScript events. That mechanics is the browser's job. The responsibility for web developers is to be mindful of this pipeline and build pages that benefit from it, by correctly expressing page semantics: the appropriate roles, states, properties, names, and descriptions for important elements. When done right, assistive technology can create a fully customized experience for the user.

Implicit Semantics in Native HTML

The browser can translate the DOM into the accessibility tree because much of the DOM carries implicit semantic meaning. Native HTML elements are recognized by browsers and behave predictably across platforms, so accessibility for native controls like links and buttons is handled automatically. Sticking to these elements lets you leverage that built-in behavior while you write the structure of your page.

The trouble begins when an element merely looks like a native element. For example, a "button" constructed as a styled div isn't a button at all to a screen reader. Aside from lacking a discoverable role, it would also require manual tabindex management for keyboard users, since a plain div is not focusable by default and would only work with a mouse.

The fix is straightforward: replace the div with an actual button element. This not only restores the element's role and state to the accessibility tree but also brings back native keyboard interaction. Styling native elements is fully supported, so there's no need to sacrifice visual design to retain semantics and behavior.

Naming Elements for Accessibility

Screen readers announce an element's role, name, state, and value. Choosing the right semantic element covers role, state, and value, but the name must still be made discoverable. There are two general kinds of names:

  • Visible labels, which convey meaning for all users, and
  • Text alternatives, which serve when a visual label isn't needed.

Text-level elements need no extra work; their content serves as the name. Input elements, controls, and visual content like images require an explicit name. Providing text alternatives for non-text content is among the first items on the WebAIM checklist.

For form elements, the recommendation is that inputs have associated text labels. Two methods accomplish this for a checkbox, and both also make the label text a click target for the control, aiding mouse and touchscreen users:

  • Wrap the input element inside a label element.

Or:

  • Use the label's for attribute, pointing to the input's id.

Once a checkbox is properly labeled, the screen reader can report its role, its checked state, and its name, e.g., "Receive promotional offers?"