How to de-emoji markdown for screen-reader accessibility
- Step 1Replace meaningful emoji with text first — Before running the tool, manually swap any emoji that conveys information for words — a status emoji to 'Status: passing', a star rating to 'Rating: 4 of 5'. The tool removes emoji; it does not describe them.
- Step 2Load the content — Paste the Markdown or upload the
.mdfile. One document per run. - Step 3Triage with Replace — Set Mode to Replace with [name] and run, so you can scan every former emoji as a bracketed token and confirm only decorative ones remain to delete.
- Step 4Keep code samples honest — Leave Process code blocks too unchecked so a code sample that legitimately contains an emoji (for example, documenting emoji handling) is preserved.
- Step 5Run De-Emoji in Strip mode — Switch Mode to Strip (remove) and click Run De-Emoji to delete the decorative emoji and shortcodes. Check for double spaces a reader would pause on.
- Step 6Verify with a screen reader — Copy or download the result and test the listen-through with NVDA, JAWS, or VoiceOver. Emoji removal is one WCAG-related improvement among several (headings, alt text, link text).
Every option this tool exposes (and nothing it does not)
The De-Emoji panel has exactly two controls. There is no allow-list, no per-emoji picker, no 'keep meaningful emoji' toggle, and no name dictionary — these are the real defaults from lib/markdown/markdown-tool-schemas.ts.
| Control (UI label) | Field | Values | Default | Effect |
|---|---|---|---|---|
| Mode → Strip (remove) | mode | strip | Yes (default) | Deletes each matched emoji and each :shortcode: outright. Surrounding spaces are left in place — they are not collapsed. |
| Mode → Replace with [name] | mode | replace | — | Wraps each matched emoji in square brackets (the literal glyph stays inside, e.g. the rocket becomes [ rocket-glyph ]) and turns :rocket: into [rocket]. |
| Process code blocks too | includeCodeBlocks | true / false | false (off) | Off: fenced `` `` blocks are skipped so command-line emoji or test fixtures stay intact. On: emoji inside fences are processed identically to prose. |
What counts as an emoji (matched Unicode ranges)
The de-emoji pass matches these Unicode blocks, each optionally followed by the U+FE0F variation selector. Anything outside these blocks is left untouched — verified directly against the matcher in lib/markdown/markdown-processor.ts.
| Block / range | Examples it matches | What it does NOT catch | Why it matters |
|---|---|---|---|
| U+1F600–1F64F (Emoticons) | grinning, wink, crying, neutral faces | ASCII emoticons like :) :-( ^_^ (plain text, never matched) | Face emoji are the most common decorative noise in AI-drafted prose |
| U+1F300–1FAFF (Misc Symbols & Pictographs, Supplemental, Extended-A) | weather, food, animals, hands, objects, newer 2020+ emoji | Letterlike symbols (tm) U+2122, (c) U+00A9, (R) U+00AE | Trademark and copyright marks survive — they are not emoji |
| U+2600–27BF (Misc Symbols + Dingbats) | check mark, star, heart, warning, arrows-as-dingbats | Box-drawing, geometric shapes below U+2600, math operators | A literal check (check) in a feature list is treated as decorative and removed |
| U+1F680–1F6FF (Transport & Map) | rocket, car, plane, warning-triangle transport icons | Map pin variants outside the block | Common in README hero lines and roadmap bullets |
| U+1F900–1F9FF (Supplemental Symbols) | additional hands, gestures, body parts, fantasy | Skin-tone modifier in isolation is matched as its own token | Skin-tone + base render as two bracket tokens in Replace mode |
| U+1F1E6–1F1FF (Regional Indicators) | the two-letter pieces that combine into flags | The flag as a single grapheme is matched as two code points | A flag becomes two bracket tokens in Replace mode, not one |
Before / after by mode (verified output)
Exact transformations produced by the strip and replace passes. Note the leftover space after removal and the bracket-the-glyph behaviour in Replace mode.
| Input fragment | Strip output | Replace output |
|---|---|---|
Ship it (rocket-emoji) today | Ship it today (double space remains) | Ship it [rocket-glyph] today |
Done :white_check_mark: | Done (shortcode removed) | Done [white_check_mark] |
Status: :) | Status: :) (ASCII emoticon untouched) | Status: :) (ASCII emoticon untouched) |
(c) 2026 ACME (tm) | (c) 2026 ACME (tm) (marks survive) | (c) 2026 ACME (tm) (marks survive) |
thumb-emoji + skin-tone | `` (both code points removed) | [thumb-glyph][skin-glyph] (two tokens) |
Cookbook
Accessibility-focused fragments before and after. (rocket), (check), (sparkles) stand in for literal emoji a screen reader would verbalise.
Silence a decorative feature line
A screen reader would read 'rocket Fast cold starts'. Strip removes the spoken-aloud noise.
Mode: Strip Input: - (rocket) Fast cold starts - (sparkles) Beautiful defaults Output: - Fast cold starts - Beautiful defaults (Reader now hears just the feature text; mind the double space.)
Replace meaningful emoji yourself, then strip the rest
A status emoji carries meaning and must become text BEFORE stripping; decorative emoji can then be removed.
Step 1 (manual): change Build (check) and tests (check) to Build passing and tests passing Step 2 (Strip mode) on the rest: Ship it (rocket) -> Ship it (The tool never converts (check) to 'passing' for you.)
Remove a run of repeated emoji
A rating row of five stars is read as 'star star star star star'. Replace it with text, then strip any leftover decoration.
Step 1 (manual): change a five-star row to 'Rating: 5 of 5'. Step 2 (Strip): remove any remaining decorative glyphs. Input after step 1: Rating: 5 of 5 (sparkles) Output: Rating: 5 of 5 (Decorative sparkles gone; the meaningful rating is now text.)
Clean literal shortcodes a reader spells out
Some readers announce a literal :smile: as 'colon s m i l e colon'. Strip removes the shortcode.
Mode: Strip Input: Great work everyone :smile: :clap: Output: Great work everyone (No more spelled-out shortcodes; trailing spaces remain.)
Protect an emoji that is the subject of the doc
An accessibility guide ABOUT emoji needs a code sample to keep its emoji. Keep Process code blocks off.
Mode: Strip, Process code blocks: OFF Input: Screen readers announce this glyph: ```text (rocket) -> spoken as "rocket" ``` Output: Screen readers announce this glyph: ```text (rocket) -> spoken as "rocket" ``` (The illustrative emoji in the fence survives.)
Edge cases and what actually happens
Meaningful emoji removed without a label
By designThe tool strips emoji indiscriminately — it never converts a meaningful emoji to descriptive text. A status or rating emoji that carries information will simply disappear in Strip mode. Replace such emoji with words yourself before running, or use Replace mode to find them first.
Double space left where an emoji was
By designRemoval does not collapse whitespace, so Fast (rocket) starts becomes Fast starts. A screen reader may pause on the extra gap. Tidy with md-prettifier to keep prose smooth.
ASCII emoticons are not removed
Preserved:) and :-( are plain ASCII, not emoji. Some readers verbalise them oddly, but this tool does not target them — remove typographic emoticons manually if they disrupt listen-through.
Document structure is preserved
PreservedHeadings, lists, links, and tables are not emoji, so the matcher leaves them intact. The structural landmarks assistive tech depends on are untouched — only the emoji characters inside text are removed.
Literal `:shortcode:` spelled out by readers
ExpectedA :smile: that renders as literal text in a non-GitHub reader can be announced letter-by-letter. Strip removes the shortcode, ending the spelled-out reading. Note any non-emoji :word: is also caught.
Emoji removal alone is not full WCAG compliance
By designRemoving decorative emoji helps listen-through, but WCAG also needs proper heading order, link text, alt text, and contrast. Treat this as one remediation step, not a complete accessibility pass.
Flag and skin-tone emoji split into pieces
ExpectedPer code point, a flag is two regional indicators and a skin-toned hand is base plus modifier. In Strip mode all pieces are removed (good for listen-through); in Replace mode you see multiple bracket tokens.
Content over the character limit
LimitFree caps pasted text at 500,000 characters; Pro 5,000,000; Pro-media 20,000,000; Developer unlimited. For a large remediation job, upload the file or split with md-splitter.
Replace keeps the literal emoji glyph
ExpectedReplace wraps a Unicode emoji as [ glyph ] rather than naming it, so the bracketed token is still an emoji a screen reader would read. Use Replace only to locate emoji during triage, then Strip for the accessible final.
Frequently asked questions
Does this convert a meaningful emoji to descriptive text?
No. The tool removes emoji; it does not describe them. For an emoji that carries information (status, rating, severity), replace it with words yourself first — for example change a check emoji to 'passing' — then run Strip on the decorative remainder.
Why remove decorative emoji for screen readers at all?
Screen readers announce each emoji by its name. A decorative rocket is read as 'rocket' mid-sentence, and a row of checks becomes 'check mark, check mark, check mark'. Removing purely decorative emoji smooths the listen-through experience.
Will it keep my headings, links, and lists?
Yes. Those are not emoji, so the matcher leaves all document structure intact. Only the emoji characters inside the text are removed or bracketed.
Does Strip remove literal `:shortcode:` text too?
Yes. Strip removes both Unicode emoji and :name: shortcodes, so a :smile: that a reader would spell out letter-by-letter is cleaned away.
What about ASCII emoticons like :)?
They are kept — they are plain ASCII, not emoji. If they disrupt listen-through, remove them manually.
Is removing emoji enough for WCAG compliance?
No. It is one helpful step. WCAG also requires correct heading order, descriptive link text, alt text on images, and adequate contrast. Use this alongside those checks, not instead of them.
How do I find meaningful emoji before deleting?
Run Replace mode first. Every emoji becomes a bracketed token in place, so you can scan for each one and decide whether to give it a text label or let Strip remove it.
Why is there a double space after removal?
Strip leaves the spaces that surrounded the emoji, so Fast (rocket) starts becomes Fast starts. A reader may pause on the gap — tidy with md-prettifier.
Does it touch emoji inside code samples?
Not by default. Fenced code is skipped unless you check 'Process code blocks too' — useful when a sample legitimately documents emoji handling.
Is anything uploaded during remediation?
No. The transform runs in your browser, so remediating internal or client content requires no upload.
Does Replace produce a readable name for screen readers?
No. Replace wraps a Unicode emoji as [ glyph ], keeping the original emoji inside, so a reader would still announce it. Use Replace only to locate emoji, then Strip for the accessible output.
How large a document can I remediate at once?
Pasted text caps at 500,000 characters (Free), 5,000,000 (Pro), 20,000,000 (Pro-media), or unlimited (Developer). File uploads follow the 1/10/50/500 MB size tiers. The character cap is separate from file size, and the tool processes one document per run.
Privacy first
All Markdown processing runs locally in your browser using JavaScript. No file is ever uploaded to JAD Apps servers — only metadata counters are saved for signed-in dashboard stats.