How to set an emoji strategy for your design system
- Step 1Survey current behaviour — Audit your CSS for `font-family` declarations that include emoji fonts (Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji) in fallback chains. Run [Character Coverage Map](/font-tools/character-coverage-map) on each self-hosted font to see whether it carries emoji glyphs at all. Capture the de-facto strategy explicitly — most teams have one by accident.
- Step 2Pick a strategy — OS-native: strip emoji from custom fonts, rely on Apple/Segoe/Noto (zero bytes, platform variance). Branded chromatic: ship a colour emoji font — pixel-perfect cross-platform but the heaviest payload on the page. Hybrid: branded on a few marketing surfaces, OS-native everywhere in-product.
- Step 3Strip emoji from product fonts — For OS-native product surfaces, run each product font through the [Emoji Remover](/font-tools/emoji-remover). It removes the emoji/dingbat/PUA ranges and rebuilds a TTF. For body fonts that need kerning, use the layout-preserving build-pipeline path instead, because the browser tool drops `GPOS`/`GSUB`.
- Step 4Decide the branded-emoji delivery if you go that route — A branded chromatic font means COLR/CPAL (vector, small) or sbix/CBDT (bitmap, large). Subset it to only the emoji you actually use with the [Character Whitelist Builder](/font-tools/character-whitelist-builder), then accept that it's still your biggest font asset and load it `font-display: optional` so it never blocks text.
- Step 5Pin the strategy and enforce it in CI — Write the chosen strategy into the design-system docs. Add a CI check (the [build-pipeline script](/font-tools/guides/emoji-removal-build-pipeline-script)) that asserts product fonts have no emoji codepoints. Exempt designated marketing fonts. Without enforcement, a future font update silently re-introduces emoji glyphs.
- Step 6Verify the fallback chain ends generic — Make every `font-family` list end with a generic (`sans-serif`, `serif`). That guarantees the browser can reach the OS emoji font after your stripped product font. A chain that ends in a specific font with no emoji renders tofu on devices without that font.
Emoji strategy decision matrix
Payload figures are typical orders of magnitude for the font asset itself, not the whole page. The enforcement column lists the JAD tool that implements each path.
| Strategy | Cross-platform look | Font payload | Enforced with |
|---|---|---|---|
| OS-native | Varies by device (Apple ≠ Segoe ≠ Noto) | 0 bytes — emoji come from the OS | Emoji Remover on product fonts |
| Branded chromatic (full) | Pixel-identical everywhere | Huge — Apple Color Emoji is 60+ MB; a full set is impractical to ship | Don't ship full; subset it |
| Branded chromatic (subset) | Pixel-identical for the emoji you kept | ~200–500 KB for 50–100 emoji — still the heaviest font on the page | Character Whitelist Builder |
| Hybrid | Branded on marketing, OS-native in product | Branded subset on a few pages; 0 bytes elsewhere | Per-surface font stacks + CI exemptions |
Colour-emoji table formats and what they cost
If you do ship a branded emoji font, the table format drives the size. The Emoji Remover's fallback path and the Colour Table Remover both operate on these tables.
| Table | Type | Relative size | Notes |
|---|---|---|---|
COLR / CPAL | Layered vector | Smallest | Resolution-independent; the modern choice for branded emoji |
SVG | SVG-in-OpenType vector | Small–medium | Rich gradients; uneven browser support historically |
sbix | PNG bitmaps (Apple) | Large | Apple Color Emoji uses this — why it's 60+ MB |
CBDT / CBLC | PNG bitmaps (Google) | Large | Noto Color Emoji bitmap strikes |
Cookbook
Strategy applied to real surfaces. The point is matching the lever (which tool, which CSS) to the chosen path.
OS-native product, enforced in CI
ExampleThe default for almost every team. Strip emoji from product fonts and let the OS render them; gate it so it stays true.
Product font stack (every component):
font-family: "ProductSans", system-ui, -apple-system,
"Segoe UI", Roboto, sans-serif;
/* generic last -> OS emoji reachable */
Build step:
ProductSans.ttf -> Emoji Remover -> ProductSans.no-emoji.ttf
CI assert: output cmap has 0 codepoints in U+1F000-1FFFF etc.
Result: emoji render as Apple/Segoe/Noto. Zero emoji bytes shipped.Hybrid: branded marketing, OS-native product
ExampleMarketing hero needs the brand's exact emoji; the app does not. Two font stacks, one CI exemption.
Marketing surfaces only:
@font-face { font-family: "BrandEmoji";
src: url(/fonts/brand-emoji.subset.woff2) format("woff2");
font-display: optional; } /* never blocks text */
.hero { font-family: "BrandEmoji", "ProductSans", sans-serif; }
Product surfaces: ProductSans.no-emoji + OS fallback (as above)
CI: emoji-strip check SKIPS brand-emoji.*, ENFORCES product fonts.Subsetting a branded emoji font down to what you use
ExampleA full chromatic font is unshippable. Keep only the 60 emoji the brand actually uses with the Whitelist Builder.
Audit: brand uses 60 emoji across the site. Character Whitelist Builder, paste the 60 emoji -> brand-emoji.subset.ttf (~340 KB, COLR/CPAL preserved) -> ttf-to-woff2 -> brand-emoji.subset.woff2 (~210 KB) Still the heaviest font on the marketing page -> load it font-display: optional and only on surfaces that need it.
Removing accidental monochrome emoji from a text font
ExampleA licensed body font shipped grey fallback emoji the browser never used. Pure dead weight; strip them — but keep kerning.
Audit (Character Coverage Map): TextBody.ttf has 80 glyphs
in U+2600-27BF and U+1F300-1FAFF -> never rendered (OS wins).
Because it's a BODY font (kerning matters), do NOT use the
browser Emoji Remover (drops GPOS). Use the hb-subset path:
hb-subset TextBody.ttf --unicodes='0-25FF,2800-DFFF,...' \
--layout-features='*' -o TextBody.no-emoji.ttf
Kerning survives; ~12 KB of dead emoji glyphs gone.Document the trade-off for stakeholders
ExampleThe brand team wants identical emoji everywhere; engineering wants performance. Frame it as a number, not an opinion.
Decision doc, one row per option:
OS-native: +0 KB, emoji differ per device
Branded subset: +210 KB, emoji identical, heaviest asset,
font-display: optional so text never waits
Branded full: unshippable (60+ MB)
Recommendation: OS-native product, branded subset on the
homepage hero only. Sign-off: brand + eng.Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
Mixed strategy produces inconsistent emoji within one product
Silent regressionIf one surface ships a branded emoji font and another relies on OS emoji, the same emoji looks different page-to-page in the same product — exactly the inconsistency a design system exists to prevent. Decide product-wide: either branded everywhere in-product (expensive) or OS-native everywhere in-product (cheap). Keep branded strictly to marketing if you go hybrid.
Emoji Remover deletes your icon-font glyphs
RemovedIf your design system's icon set lives in the Private Use Area (U+E000–U+F8FF), the Emoji Remover strips it — that range is in its removal set. Never route an icon font through the Emoji Remover. Use the Character Whitelist Builder to keep the icon codepoints, and reserve emoji removal for text fonts.
Body font loses kerning after a browser-tool strip
By designThe JAD browser Emoji Remover rebuilds via opentype.js and drops GPOS/GSUB. Strip emoji from a body font that way and headings/body text reflow because kerning vanished — a visible regression at large sizes. For body fonts, use the layout-preserving hb-subset path from the build-pipeline guide.
Font-stack doesn't reach the OS emoji font
Tofu riskOS-native only works if the browser can fall through to the OS emoji font. A font-family that ends in a specific font (no generic) can render tofu (□) on a device that lacks both your font's emoji and a reachable system emoji font. Always end the stack with a generic family so the fallback chain completes.
Branded full emoji font is unshippable
ImpracticalA complete chromatic emoji font (Apple Color Emoji, Noto Color Emoji) is 60+ MB because of sbix/CBDT bitmap strikes. You cannot ship that to the web. Branded chromatic is only viable as a tight subset of the emoji you actually use — and even then it's your heaviest asset.
Colour emoji renders monochrome after table strip
ExpectedIf you run the Colour Table Remover (or hit the Emoji Remover's emoji-only fallback) on a branded chromatic font, you remove COLR/CPAL/sbix/SVG and the emoji render as flat outlines, not colour. That's the documented behaviour — keep the colour tables if you want colour, or commit fully to OS-native.
Email surfaces ignore your strategy entirely
Out of scopeHTML email clients render emoji wildly inconsistently and many (Outlook desktop especially) strip @font-face outright, so neither branded nor stripped web fonts apply. Treat email as its own track: accept OS variance and don't burn effort trying to make email emoji match the product.
A font update silently re-adds emoji glyphs
DriftWithout a CI check, the next vendor font update can quietly ship emoji glyphs again, re-bloating the font and (worse) double-rendering on some platforms. The enforcement step in the build pipeline — assert zero emoji codepoints in product fonts — is what prevents this drift.
Frequently asked questions
How big is a branded chromatic emoji font?
A full one is 60+ MB (Apple Color Emoji's scale) and unshippable to the web. A subset covering 50–100 emoji runs roughly 200–500 KB depending on whether it uses vector COLR/CPAL (smaller) or bitmap sbix/CBDT (larger). Even subset, it's the heaviest font payload on the page — only worth it for premium brands where consistency outranks performance.
Will users notice OS-emoji inconsistency?
Most won't — people are used to seeing different emoji on different devices. Designers and brand teams notice; general users don't. If your audience is brand-savvy creatives, branded emoji on key surfaces might be worth the cost; for a typical product, OS-native is the right default.
Which tool enforces the OS-native strategy?
The Emoji Remover for display/product fonts (it strips the emoji/dingbat/PUA ranges and rebuilds a TTF), backed by a CI check from the build-pipeline guide. For body fonts that need kerning, use the layout-preserving hb-subset path instead, since the browser tool drops layout.
Can I keep just my brand emoji and drop the rest?
Yes — that's the hybrid/branded-subset path. Use the Character Whitelist Builder to keep only the emoji codepoints you ship and drop everything else. Don't use the Emoji Remover for this; it removes the whole emoji range, which is the opposite of what you want.
What about icon fonts in our design system?
Keep them away from the Emoji Remover. Most icon fonts live in the Private Use Area (U+E000–U+F8FF), which the Emoji Remover deletes. Manage icon fonts with the Character Whitelist Builder or a dedicated icon-subset step.
How do I migrate from branded to OS-native without breaking pages?
Change the strategy at the font-stack level first: ensure every font-family ends with a generic so the OS emoji font is reachable, then strip emoji from the product fonts and stop shipping the branded emoji font. Because the fallback chain already pointed at OS emoji, the visual change is colour-style only, not missing glyphs.
What about email?
HTML email clients have wildly inconsistent emoji rendering and Outlook desktop strips @font-face. Accept platform variance for email and focus your consistency budget on product and marketing web surfaces where you actually control the font stack.
Does removing emoji from a font change how text renders?
Text glyphs (Latin, punctuation, etc.) are untouched — only emoji/dingbat/PUA codepoints are removed. The one real side effect of the browser Emoji Remover is that kerning/ligatures are dropped on rebuild, which matters for body text. Use the layout-preserving pipeline for kerning-critical fonts.
Which colour-emoji table format should branded fonts use?
Prefer COLR/CPAL (layered vector) — it's the smallest and resolution-independent. Avoid sbix/CBDT bitmap formats for the web; they're why Apple/Noto colour fonts are tens of megabytes. SVG -in-OpenType is rich but historically uneven in browser support.
How do I stop a font update from re-introducing emoji?
Add the CI assertion from the build-pipeline guide: after the strip step, fail the build if the product font's cmap contains any codepoint in the emoji ranges. That's the only reliable guard against silent drift on vendor updates.
Is OS-native really zero bytes?
For the emoji themselves, yes — they come from the OS font the user already has, so you ship nothing for them. You only pay the (tiny) cost of removing the glyphs from your font, which actually makes it smaller. That's why OS-native is the performance default.
Can I audit which fonts in my system carry emoji?
Yes — run Character Coverage Map on each self-hosted font. It scores coverage against the real Unicode blocks, so you can see at a glance which fonts carry Emoticons / Misc Symbols / PUA glyphs and therefore need stripping (or, for icon fonts, protecting).
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.