Emoji

An emoji is a character, and browsers pick the font for it inconsistently — sometimes a colour font, sometimes a monochrome fallback, sometimes the body font's own glyph. .emoji names the colour stack explicitly and fixes the size and baseline, so the character sits with its text rather than above or below it.

When to use it

Wrap an emoji in a <span class="emoji"> whenever it appears beside text you care about. Without it the character inherits the body font, which on some platforms produces a black-and-white outline and on others shifts the line height of the whole paragraph.

The character sits on the text baseline

Deployed 🚀 in 42 seconds

Nightly export finished ✅ with 2,481 rows

<p>Deployed <span class="emoji">🚀</span> in 42 seconds</p> <p>Nightly export finished <span class="emoji">✅</span> with 2,481 rows</p>
<p>Deployed <span class="emoji">🚀</span> in 42 seconds</p>
<p>Nightly export finished <span class="emoji">✅</span> with 2,481 rows</p>

font-style: normal and font-weight: 400 are set as well, so an emoji inside a <strong> or an <em> is not asked to synthesise a bold or italic it does not have.

Sizes

Three steps, each pairing a size with the vertical-align that keeps it on the baseline — the alignment is the part that has to change with the size, and getting it wrong is what makes a large emoji look like it is falling off the line.

Default, lg, xl, hero
🎉 🎉 🎉 🎉
<span class="emoji">🎉</span> <span class="emoji emoji-lg">🎉</span> <span class="emoji emoji-xl">🎉</span> <span class="emoji emoji-hero">🎉</span>
<span class="emoji">🎉</span>
<span class="emoji emoji-lg">🎉</span>
<span class="emoji emoji-xl">🎉</span>
<span class="emoji emoji-hero">🎉</span>

.emoji-hero is the odd one: it is sized in rem rather than em, because at that scale it is a graphic in its own right rather than something sitting beside text.

Tiles

.emoji-tile puts the character in a tinted rounded square — the emoji counterpart of .icon-tile, for an empty state or a feature row. .emoji-tile-round makes it a circle.

📦 🔔 🎯
<span class="emoji-tile"><span class="emoji emoji-lg">📦</span></span> <span class="emoji-tile"><span class="emoji emoji-lg">🔔</span></span> <span class="emoji-tile emoji-tile-round"><span class="emoji emoji-lg">🎯</span></span>
<span class="emoji-tile"><span class="emoji emoji-lg">📦</span></span>
<span class="emoji-tile"><span class="emoji emoji-lg">🔔</span></span>
<span class="emoji-tile emoji-tile-round"><span class="emoji emoji-lg">🎯</span></span>

Picker grid

.emoji-grid is an auto-fitting grid of buttons that scale slightly on hover — a reaction picker or a quick-select. Deck styles the grid and supplies no picker: the characters, the categories and the search are yours.

Every button needs a label — see Accessibility
<div class="emoji-grid" style="max-inline-size:20rem"> <button type="button" aria-label="Thumbs up"><span class="emoji emoji-lg">👍</span></button> <button type="button" aria-label="Celebrate"><span class="emoji emoji-lg">🎉</span></button> <button type="button" aria-label="Eyes"><span class="emoji emoji-lg">👀</span></button> <button type="button" aria-label="Rocket"><span class="emoji emoji-lg">🚀</span></button> <button type="button" aria-label="Heart"><span class="emoji emoji-lg">❤️</span></button> <button type="button" aria-label="Thinking"><span class="emoji emoji-lg">🤔</span></button> </div>
<div class="emoji-grid" style="max-inline-size:20rem">
  <button type="button" aria-label="Thumbs up"><span class="emoji emoji-lg">👍</span></button>
  <button type="button" aria-label="Celebrate"><span class="emoji emoji-lg">🎉</span></button>
  <button type="button" aria-label="Eyes"><span class="emoji emoji-lg">👀</span></button>
  <button type="button" aria-label="Rocket"><span class="emoji emoji-lg">🚀</span></button>
  <button type="button" aria-label="Heart"><span class="emoji emoji-lg">❤️</span></button>
  <button type="button" aria-label="Thinking"><span class="emoji emoji-lg">🤔</span></button>
</div>

Classes

Generated from src/08-mobile.css by tools/docs/extract.mjs.

Classes, generated from the stylesheet source
Class Layer What it does Source
.emoji deck.mobile Pins the emoji font stack and metrics so an emoji renders at the same size and baseline on Windows, iOS and Android instead of jumping between them. 08-mobile.css:183
.emoji-grid deck.mobile Emoji picker grid — drop your own set in 08-mobile.css:214
.emoji-hero deck.mobile Sets font-size, line-height. 08-mobile.css:197
.emoji-lg deck.mobile Sets font-size, vertical-align. 08-mobile.css:195
.emoji-tile deck.mobile Emoji as an avatar or a status marker 08-mobile.css:200
.emoji-tile-round deck.mobile Sets border-radius. 08-mobile.css:211
.emoji-xl deck.mobile Sets font-size, vertical-align. 08-mobile.css:196

Tokens

Design tokens this component reads
Token Live Declared value
--surface-2 light-dark(var(--ink-25), var(--ink-900))
--surface-hover light-dark(var(--ink-50), var(--ink-900))
--r-md 12px
--r-full 999px
--space-2 .5rem

Accessibility

  • An emoji is read aloud, and the reading is often long. "🎉" is announced as "party popper". In a sentence that is usually fine; in a row of six it is noise.
  • Decorative emoji should be hidden. aria-hidden="true" on the span when the character is ornament beside text that already says everything.
  • An emoji carrying meaning needs words too. A ✅ standing alone in a table cell is announced as "check mark button", which is a description of a picture rather than a status. Put the status in text and hide the emoji.
  • Picker buttons need real labels. A grid of unlabelled emoji buttons is announced as a list of glyph names with no indication of what pressing one does. Every button in the example above carries aria-label.
  • Emoji rendering is not universal. The font stack lists five families and falls back to sans-serif; on a system with none of them a character may render as a box. Never let an emoji be the only carrier of meaning.
  • Skin-tone and ZWJ sequences vary by platform. A family emoji may render as one glyph or as four separate ones depending on the font, which changes both the width and what is announced.

Right to left

Emoji are neutral characters in the bidirectional algorithm, so they take the direction of the surrounding text and need nothing from Deck. The one thing worth knowing is that a directional emoji — an arrow, a pointing hand — does not mirror, unlike Deck's directional icons. If direction matters, use an icon.

تم النشر 🚀 خلال ٤٢ ثانية

<p dir="rtl">تم النشر <span class="emoji">🚀</span> خلال ٤٢ ثانية</p>
<p dir="rtl">تم النشر <span class="emoji">🚀</span> خلال ٤٢ ثانية</p>

Reduced motion

Only the picker grid moves — its buttons scale to 1.16 on hover — and the global reset collapses that transition to .01ms under prefers-reduced-motion: reduce. Nothing else animates.

Printing

Emoji print as characters, in colour, on a colour printer. There is no print rule and none is needed — though a colour emoji printed in greyscale can become an indistinguishable dark blob, which is the same argument for not letting one carry meaning alone.

Overriding it

@layer app.components {
  /* Prefer a bundled emoji font you ship yourself */
  .emoji { font-family: "Noto Color Emoji", sans-serif; }

  /* A tighter picker */
  .emoji-grid { grid-template-columns: repeat(auto-fit, minmax(2rem, 1fr)); }
}

Shipping your own emoji font is the only way to make rendering identical everywhere. It is also several megabytes, which is why Deck names a stack instead.

When not to use it

  • Not instead of an icon. An icon takes currentColor, mirrors under RTL, prints as line art and looks the same on every platform. An emoji does none of those.
  • Not for status. ✅ and ❌ are pictures whose announced names describe the picture, not the state.
  • Not in a heading you will translate. Emoji meaning is cultural, and a gesture that is friendly in one locale is not in another.
  • Not as a bullet. A list marked with emoji is announced with the glyph name before every item.
  • Not in a value you store and compare. The same emoji has multiple byte sequences depending on variation selectors, so equality checks surprise people.