Screen Readers
A screen reader is software that converts on-screen content into audio — reading text aloud, announcing interactive elements, and describing images when descriptions are provided. Users listen and navigate with the keyboard.
It's easy to picture this as a niche tool, but the population using screen readers is broader than you might expect. Blind and low-vision users are the most obvious group, but screen readers are also used by people with certain cognitive and learning disabilities, people who experience eye strain reading on screens, and people who simply prefer to consume content by listening. Accessibility, as always, serves more people than the ones you first imagined.[1]
The core shift to internalize: for a screen reader user, the web is something you hear, not something you see. Everything else follows from that.
The linear stream
Here's what a screen reader does not have access to: your layout. The two-column grid, the sidebar, the visual grouping of related elements, the way a card separates one piece of content from another — none of that exists. What the screen reader gets is the DOM, read top to bottom, in source order.
This means visual presentation and reading order can silently disagree. A sidebar that appears on the right side of the screen might sit before the main content in the HTML, meaning a screen reader user encounters it before the thing they came to read. A decorative element that looks like a separator might announce itself as content. A hero image with text overlaid might read the text before the surrounding context that gives it meaning.
The order of your HTML is the experience for screen reader users. It's worth reading your pages that way.
How users actually navigate
Screen reader users rarely listen to a page start to finish. That would be like reading a book by starting at the first word and never skimming. Instead, they use keyboard shortcuts to jump directly to what they need:
- Headings — pressing H (or a number key for a specific level) jumps to the next heading. This is the most common navigation pattern. Headings function as a table of contents for the page.
- Landmarks — regions like
<main>,<nav>,<header>, and<footer>are navigable by keyboard. A "skip to main content" link works because it targets the main landmark. - Links — users can pull up a list of all links on the page and scan them. This is why "click here" is a problem: extracted from context, it says nothing.
- Form fields — similar to links, fields can be listed and navigated directly.
What breaks this: missing headings, skipped heading levels (jumping from H2 to H4), headings used for visual styling rather than document structure, unlabeled links, unlabeled form fields. Any of these forces the user to fall back to linear reading to find what they're looking for.
What gets announced
Every interactive element needs a name — something the screen reader can announce so the user knows what they're about to activate. How that name gets determined:
- Links use their visible text. "Read more about our accessibility policy" is good. "Read more" is marginal. "Click here" is useless.
- Buttons use their visible text, or an
aria-labelif there's no visible text (like an icon button). - Images use their
altattribute. Noaltmeans the filename gets read, which is often something likeIMG_4823.jpg. A decorative image withalt=""is correctly skipped. - Form inputs use their associated
<label>. A placeholder is not a label — it disappears when the user starts typing and is not reliably announced.
The test: if you removed every visual from the page and left only what a screen reader would announce, could someone still understand and use it?
The emoji problem
Emojis on screen look expressive and fun. Through a screen reader, they become a different experience entirely.
Screen readers read the Unicode name of each emoji aloud. 🔥 is announced as "fire." 😂 is announced as "face with tears of joy." This is, in small doses, fine — even kind of charming. The problems start to compound fast.[2]
A row of decorative fire emojis — 🔥🔥🔥🔥🔥🔥🔥 — becomes "fire fire fire fire fire fire fire." An enthusiastic social post that ends with 🎉✨💫🙌 becomes "party popper sparkles dizzy folded hands." A face emoji used to convey tone — 😬 — becomes "grimacing face," which is accurate but pulls the user out of the flow completely.
Emoji in link text is especially rough: "🔗 Read more" becomes "link read more" on most screen readers, but on others the emoji name is announced first, making "link link read more" or "link link symbol read more" depending on settings. Testing reveals surprises.
The fix is intentional: use aria-label to override what gets announced for emoji-heavy elements, or aria-hidden="true" on purely decorative emoji so they're skipped entirely. The principle is the same as alt text — you're deciding what the announcement should be, rather than leaving it to chance.
Screen readers to know about
You don't need to test on all of these, but knowing they exist helps you understand why behavior can vary:
- VoiceOver — built into macOS, iOS, and iPadOS. Free. The easiest starting point for testing if you're on Apple hardware. Toggle it with Cmd + F5 on Mac, or triple-click the side button on iPhone.
- NVDA (NonVisual Desktop Access) — free and open source, Windows only. Widely used, widely tested against. The go-to for Windows testing.
- JAWS (Job Access With Speech) — paid, Windows only, with a free time-limited mode. Common in enterprise environments and government agencies, which means it's often what your users have at work even if they use something else at home.
- TalkBack — built into Android. The mobile counterpart to VoiceOver on iOS.
Screen readers don't all behave identically — they can announce the same markup differently, support different shortcut keys, and handle edge cases in different ways. Testing on more than one is ideal; testing on at least one is necessary.