SymbolFYI

How Screen Readers Handle Unicode Symbols and Emoji

When you type a checkmark ✓ into a heading or paste a row of emoji into a product description, you are making a decision that affects a significant portion of your users. Screen readers — the software that converts on-screen content into speech or Braille output — have to decide what to say when they encounter those characters. The answer varies more than most developers expect, and the gap between "visually clear" and "audibly clear" can make the difference between an inclusive experience and a frustrating one.

How Screen Readers Process Text

A screen reader sits between the operating system accessibility API (such as Microsoft UI Automation on Windows or NSAccessibility on macOS) and a text-to-speech (TTS) engine or refreshable Braille display. When it encounters a text node, it does not simply pass raw bytes to the TTS engine. Instead it runs the text through a processing pipeline that:

  1. Queries the DOM accessibility tree to determine the element's role and computed accessible name
  2. Applies character substitution rules from an internal symbol dictionary
  3. Strips or expands characters based on the current verbosity setting
  4. Passes the processed string to the TTS engine

The symbol dictionary is where Unicode characters get their spoken names. It is maintained by each screen reader vendor independently, which is why the same character can produce different speech output across tools. The verbosity setting determines how aggressively the reader expands or suppresses symbols — more on this shortly.

The Role of Unicode Character Names

The Unicode Consortium assigns an official name to every code point. U+2713 is officially "CHECK MARK" and U+2714 is "HEAVY CHECK MARK". Screen readers typically use these official names, or shortened versions of them, as the default speech output. This is both a strength and a weakness: it is consistent and predictable, but the names are often technical and verbose in ways that break the flow of natural reading.

How Symbols Are Announced

Simple Punctuation and Math Symbols

Common ASCII punctuation — periods, commas, question marks — is usually not announced in mid-sentence. Screen readers treat them as prosodic cues that affect pause and intonation rather than generating spoken words. Once you move into the Unicode range, behavior becomes less predictable.

Consider a simple comparison table showing what each major screen reader says:

Character Name NVDA (default) JAWS (default) VoiceOver (macOS)
✓ U+2713 CHECK MARK "check" "check mark" "check mark"
✔ U+2714 HEAVY CHECK MARK "tick" "heavy check mark" "heavy check mark"
→ U+2192 RIGHTWARDS ARROW "right arrow" "right arrow" "right arrow"
© U+00A9 COPYRIGHT SIGN "copyright sign" "copyright sign" "copyright sign"
™ U+2122 TRADE MARK SIGN "trade mark" "trademark sign" "trademark"
• U+2022 BULLET "bullet" silent "bullet"
… U+2026 HORIZONTAL ELLIPSIS silent "dot dot dot" silent
★ U+2605 BLACK STAR "black star" "star" "black star"
° U+00B0 DEGREE SIGN "degrees" "degree sign" "degrees"

Notice that even for simple characters, behavior differs. JAWS silences the bullet while NVDA announces it. NVDA says "tick" for ✔ while VoiceOver says "heavy check mark". A developer who tests with only one screen reader will draw the wrong conclusions.

Emoji Verbosity

Emoji present a more dramatic version of the same problem. Screen readers use the CLDR (Common Locale Data Repository) short names for emoji, which are generally descriptive but can be surprisingly long.

😊  → "smiling face with smiling eyes"
🚀  → "rocket"
❤️  → "red heart" (or "heart" depending on the reader)
🎉  → "party popper"
👍  → "thumbs up"

Reading a sentence like "Great work! 🎉🎊🥳" aloud produces: "Great work! party popper confetti ball partying face" — which is three times longer than the visual text. In a blog post introduction with five decorative emoji, the screen reader user hears a five-item symbol inventory before the actual content begins.

This is the verbosity problem: emoji that feel lightweight and expressive visually become verbose and disruptive aurally.

Cross-Reader Behavioral Differences

NVDA (Windows)

NVDA is open source and uses the eSpeak NG TTS engine by default, though many users switch to higher-quality voices. Its symbol processing is highly configurable through Settings → Speech → Symbol/Punctuation. The verbosity levels are:

  • Never: Symbol is never announced
  • Some punctuation: Only at low verbosity settings (user-controlled threshold)
  • Most punctuation: Announced unless verbosity is set low
  • All: Always announced regardless of verbosity

NVDA ships with a comprehensive symbols.dic file that can be customized. It handles emoji via the Unicode CLDR database. One important behavior: NVDA reads emoji names for every single emoji it encounters, which means repeated emoji like "🌟🌟🌟" becomes "star star star" — each one individually announced.

JAWS (Windows)

JAWS (Job Access With Speech) from Freedom Scientific is the dominant commercial screen reader. It has its own symbol database and its default verbosity settings differ from NVDA. JAWS is more conservative — it tends to suppress more punctuation at default settings, which can make it feel cleaner for prose reading but can miss meaningful symbols in technical content.

JAWS handles emoji by reading the Unicode name but may skip some characters that are not in its internal database, resulting in silence rather than an error. Newer versions have improved emoji support substantially.

VoiceOver (macOS/iOS)

Apple's VoiceOver is built into macOS and iOS. It uses Apple's own TTS engines (including the Neural TTS voices) and Apple's own character description database. VoiceOver tends to have strong emoji support because Apple designed and shipped many emoji characters. Verbosity is controlled in System Settings → Accessibility → VoiceOver → Verbosity.

On iOS, VoiceOver behavior for emoji can differ from macOS — the same 😊 might produce slightly different spoken output between the two platforms.

TalkBack (Android)

Google's TalkBack is the primary screen reader on Android. It uses Google's TTS engines and, like VoiceOver on iOS, generally has good emoji coverage. It reads emoji names from the CLDR database localized to the device language, meaning a Korean-language Android device will announce emoji in Korean.

Narrator (Windows)

Windows Narrator is Microsoft's built-in screen reader. It is less commonly used than NVDA or JAWS but comes pre-installed. Its symbol handling is more limited than NVDA and JAWS, and its default verbosity settings are conservative.

Configuring Verbosity

Most screen readers allow users to configure how aggressively symbols are announced. This is a double-edged tool for developers to understand.

The key insight: your users may have very different verbosity settings than whatever default you test with. A developer testing at "most punctuation" verbosity might see a completely different experience than a user who has set their reader to "some punctuation" because they find full verbosity too overwhelming.

This means you cannot rely on verbosity settings to fix accessibility problems. If a symbol is meaningful — if it conveys information that is not also present in text — you must provide a text alternative. User verbosity settings are personal preferences, not accessibility mechanisms.

When Symbols Are Silent

Several situations cause a screen reader to produce no output for a symbol:

1. CSS content property with no accessible name

.icon::before {
  content: "★";
}

Some screen readers do announce CSS-generated content, but behavior is inconsistent. NVDA and JAWS often announce it; older versions of both tools did not. You cannot rely on this for meaningful content.

2. The aria-hidden="true" attribute

<span aria-hidden="true"></span>

This explicitly removes the element from the accessibility tree. The symbol will not be announced — which is exactly what you want for decorative symbols, but disastrous for meaningful ones.

3. Characters outside the screen reader's dictionary

Obscure Unicode characters — combining diacritics, rare mathematical operators, or newer additions to the standard — may not be in the screen reader's symbol dictionary. The TTS engine may attempt to pronounce them phonetically (often producing garbled output) or may silently skip them.

4. Images without alt text

If you use a symbol character as an image (SVG or <img>) and omit the alt attribute, VoiceOver may announce the filename; NVDA and JAWS may announce "graphic" with no further description.

Practical Recommendations

Test with at least two screen readers. The difference between NVDA and JAWS on Windows alone is significant. If you can also test with VoiceOver on macOS, you will have a much clearer picture.

Do not use symbols as the sole carrier of information. If ✓ means "available" and ✗ means "unavailable", provide that meaning in text too. Either include the text visually ("✓ In stock") or provide it via ARIA ("✓ <span class="sr-only">In stock</span>").

Limit decorative emoji. Three or more consecutive emoji in body text is almost always a verbosity problem. If emoji are decorative, hide them. If they carry meaning, label them.

Check character coverage. Test character accessibility with our Character Counter tool, which can show you the Unicode properties of characters you are working with.

Respect the distinction between punctuation and symbols. Periods and commas are handled by prosody. The bullet U+2022, the em dash U+2014, and the ellipsis U+2026 are symbols that get announced according to verbosity settings. Know which category your characters fall into.

Never assume silence. A character you expect to be decorative may be loudly announced at certain verbosity levels. Test before deploying.


Next in Series: ARIA and Unicode: Making Decorative Symbols Accessible — how to use aria-hidden, aria-label, and role="img" to take precise control over how symbols appear to assistive technologies.

Símbolos relacionados

Glossário relacionado

Ferramentas relacionadas

Mais guias