# Emoji read out by their names, not their meaning

Source: https://easeweb.dev/learn/emoji
Topics: Images and media > Emoji and symbols say what they mean, or are skipped when they only decorate (WCAG 1.1.1)
The fix: Decide for every emoji whether it carries meaning, then name it or hide it. Either wrap each meaningful emoji in an element with role="img" and an aria-label that says what it means, or write the meaning as text beside it and hide the emoji with aria-hidden="true". Decorative emoji get aria-hidden="true" in both.
Test with: screen reader
References: https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA6 https://www.w3.org/WAI/WCAG22/Techniques/failures/F71 https://www.w3.org/WAI/WCAG22/Techniques/failures/F72 https://www.w3.org/WAI/WCAG22/Techniques/html/H86 https://unicode.org/emoji/charts/emoji-list.html https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/img_role

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A bakery takes orders on its website. Under the heading "✨ Fresh today ✨", each bake ends with a few small emoji instead of an allergen list, and a key under the list says what they mean: 🌾 for contains wheat, 🥛 for contains milk, 🥜 for contains peanuts and 🌱 for vegan. A sighted customer matches the marks to the key and sees that the sourdough loaf is vegan and the croissant has milk in it. A screen reader reads the heading as "sparkles Fresh today sparkles", then "Sourdough loaf, sheaf of rice, seedling", "Butter croissant, sheaf of rice, glass of milk" and "Peanut cookie, peanuts, seedling". Every emoji is read by the name of its picture, not by what the bakery means by it. A customer who must avoid wheat hears "rice" at every bake; a vegan hears "seedling". The key is read last, as "Key: sheaf of rice Contains wheat" and so on, so the meanings come only after the list, and only to someone who remembers which picture name stood where. Only the peanut happens to be read as what it means.

## Why it fails

An emoji is a character in the text, but it works like a small picture: its meaning comes from what it shows, and here from a code the bakery made up. Screen readers read each emoji by a standard name for its picture. NVDA, for example, takes these names from the Unicode CLDR list, where 🌾 is "sheaf of rice" and 🌱 is "seedling". That name is all the text the mark has in its row. 1.1.1 Non-text Content asks for a text alternative that serves the same purpose as the mark, and the purpose here is "contains wheat" and "vegan": in the rows neither is said, and the key, which says it, is read after the list. The sparkles fail it the other way round: they only decorate, and 1.1.1 asks for decoration to be implemented so assistive technology can ignore it, but they are read twice, around the heading's words, every time someone moves to the heading.

## Who is affected

The marks look clear to the person who chose them, but their meaning is a convention: a seedling for vegan, a sheaf of rice for wheat, written down only in the key. Emoji are also drawn differently on Windows, Apple and Android, so the picture the bakery chose is not exactly the picture every customer sees.

## What automation and AI miss

axe and similar tools have no rule for emoji: the heading and the list items have text, so the page passes. Even a tool that found every emoji couldn't tell a mark that carries meaning from one that decorates, or say what it means here. This audit reports emoji in headings, links, buttons, labels, list items and table cells as a lead for a person to check, and words written in look-alike mathematical letters as a finding. A quick AI fix tends to do one of two things: label each emoji with its picture ("wheat", "seedling"), which says what it shows, not what it means, or hide every emoji to silence the reading, which removes the allergen information completely. Only someone who knows the menu can say which marks mean something and what.

## Before: bare emoji as the allergen marks and around the heading

```html
<h2>✨ Fresh today ✨</h2>
<ul class="bakes">
  <li>
    Sourdough loaf
    <span class="marks">🌾 🌱</span>
  </li>
  <li>
    Butter croissant
    <span class="marks">🌾 🥛</span>
  </li>
  <li>
    Peanut cookie
    <span class="marks">🥜 🌱</span>
  </li>
</ul>
<p class="key">
  Key:
  🌾 Contains wheat
  🥛 Contains milk
  🥜 Contains peanuts
  🌱 Vegan
</p>
```

```css
.bakes li { display: flex; align-items: center; gap: 12px; }
.marks { margin-inline-start: auto; }
```

Each mark's meaning is written only in the key, after the list, and in the rows its only text is the name of its picture. The sparkles carry nothing, but they are read as part of the heading.

## After: name each mark by its meaning

```html
<h2>
  <span aria-hidden="true">✨</span>
  Fresh today
  <span aria-hidden="true">✨</span>
</h2>
<ul class="bakes">
  <li>
    Sourdough loaf
    <span class="marks">
      <span role="img" aria-label="Contains wheat">🌾</span>
      <span role="img" aria-label="Vegan">🌱</span>
    </span>
  </li>
  <li>
    Butter croissant
    <span class="marks">
      <span role="img" aria-label="Contains wheat">🌾</span>
      <span role="img" aria-label="Contains milk">🥛</span>
    </span>
  </li>
  <li>
    Peanut cookie
    <span class="marks">
      <span role="img" aria-label="Contains peanuts">🥜</span>
      <span role="img" aria-label="Vegan">🌱</span>
    </span>
  </li>
</ul>
<p class="key">
  Key:
  <span aria-hidden="true">🌾</span> Contains wheat
  <span aria-hidden="true">🥛</span> Contains milk
  <span aria-hidden="true">🥜</span> Contains peanuts
  <span aria-hidden="true">🌱</span> Vegan
</p>
```

```css
.bakes li { display: flex; align-items: center; gap: 12px; }
.marks { margin-inline-start: auto; }
```

Each mark is wrapped in a `span` with `role="img"`, so it is one image, and an `aria-label` that says what it means on this menu. The label replaces the emoji's own name: a screen reader reads "Sourdough loaf, Contains wheat, Vegan", with "image" or "graphic" after each mark, depending on the screen reader. The sparkles are wrapped in `aria-hidden="true"`, so the heading is read as "Fresh today". The emoji in the key are hidden the same way, because the words beside them already say what each means. The page looks exactly as before, and that is this fix's limit: the meaning reaches screen readers, but on screen each mark is still a picture that customers have to look up in the key, and some page translators leave `aria-label` untranslated.

## After: write the meaning beside each emoji

```html
<h2>
  <span aria-hidden="true">✨</span>
  Fresh today
  <span aria-hidden="true">✨</span>
</h2>
<ul class="bakes">
  <li>
    Sourdough loaf
    <span class="marks">
      <span class="mark"><span aria-hidden="true">🌾</span> Contains wheat</span>
      <span class="mark"><span aria-hidden="true">🌱</span> Vegan</span>
    </span>
  </li>
  <li>
    Butter croissant
    <span class="marks">
      <span class="mark"><span aria-hidden="true">🌾</span> Contains wheat</span>
      <span class="mark"><span aria-hidden="true">🥛</span> Contains milk</span>
    </span>
  </li>
  <li>
    Peanut cookie
    <span class="marks">
      <span class="mark"><span aria-hidden="true">🥜</span> Contains peanuts</span>
      <span class="mark"><span aria-hidden="true">🌱</span> Vegan</span>
    </span>
  </li>
</ul>
```

```css
.bakes li { display: flex; align-items: center; gap: 12px; }
.marks { margin-inline-start: auto; }
.mark { display: inline-block; border-radius: 999px; padding: 2px 10px; background: #f3ede4; }
```

Each mark becomes a small pill with its meaning written in it, so the emoji is now decoration beside the words and is hidden with `aria-hidden="true"`. A screen reader reads "Sourdough loaf, Contains wheat, Vegan", with no picture names and no "image". Sighted customers no longer need the key, so it goes, and the words are translated, enlarged and restyled with the rest of the page.

## Which option to use

Both options replace every picture name with its meaning and silence the sparkles, so both meet 1.1.1. Naming the marks changes only markup and keeps the row as compact as it was, but its words exist only for assistive technology, and everyone who sees the page still has to look up in the key that a seedling means vegan. Writing the meaning beside each emoji is plain text, reaches everyone who reads the page, and leaves nothing for ARIA to carry; it costs room in the row. Use it. Keep named emoji for places that truly have no room for words, such as a dense table, and then put a visible key nearby.

## Implementation decisions

**Name or hide, in one component.** Most sites add emoji through a few templates: menu marks, badges, headings. Give them one shared component that takes a label: with a label, it renders `role="img"` and `aria-label`; without one, `aria-hidden="true"`. Then nobody has to decide afresh for each emoji, and a bare one stands out in review.

**Symbols as well as emoji.** The same applies to symbol characters used as marks: ✓ and ✗ in a comparison, ★ for a rating, → as "next". Some are read by a name such as "check mark", some are skipped, and which depends on the screen reader and its punctuation setting. Name them by their meaning or write the meaning beside them.

**Emoji in what people write.** Emoji in reviews, comments and chat messages are the writer's own words, and the page can't know what each one means to them. Leave them as they are; this guide is about the marks and decoration the site itself adds.

**Fancy letters.** Generators that turn text into 𝐛𝐨𝐥𝐝, 𝘪𝘵𝘢𝘭𝘪𝘤 or 𝓈𝒸𝓇𝒾𝓅𝓉 letters swap each letter for a look-alike character from Unicode's mathematical letters. A screen reader reads them letter by letter by their names, or skips them (W3C failure F71). Write ordinary text and style it with CSS. The same goes for ASCII art and emoticons built from characters, such as `¯\_(ツ)_/¯`: wrap them in an element with `role="img"` and an `aria-label`, as W3C technique H86 shows (failure F72 is ASCII art left without one).

**The picture varies.** Each platform draws its own emoji: the same character can look like a different plant on another phone. The meaning can't depend on fine detail in the picture, which is one more reason to write it in words.

## Verify the fix

1. In the browser's accessibility tree, check that each mark is an image named by its meaning ("Contains wheat", "Vegan"), or is absent with its meaning present as text, and that the heading's name is "Fresh today", with no sparkles.
2. With a screen reader, read the list from the heading down. Each bake's marks should be read once, as what they mean, with no emoji names such as "sheaf of rice" or "seedling".
3. Ask someone who knows the menu to compare each name or word with its mark, and confirm that every mark shown is also said, in words that say what it means here, not what it pictures.
4. Search the page's source for emoji characters, and confirm that each one is inside an element named with `role="img"` and an `aria-label`, or inside one with `aria-hidden="true"` and its meaning beside it as text.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat whenever the menu's marks or the component that renders them change.

## Limits

This is a starting pattern, not a guarantee for every application. Class names, colours and the bakes are placeholders. Screen readers name emoji from different lists, and some versions or settings skip them, so test with the assistive technology your users have. An image icon or SVG used the same way follows the same rule; see [SVG icons with no name, or read when they should be silent](/learn/svg-icons). Whether an emoji decorates or means something depends on the page around it, and only someone who knows the content can judge that. Test the complete journey with your target assistive technology.

---

Get this guide in your coding agent: MCP https://easeweb.dev/mcp (setup at https://easeweb.dev/services/developers)
