🍱 Lunchbox Hands

unicode

Fancy Fonts Aren't Fonts: Unicode and Screen Readers

Fancy fonts aren't fonts — they're substitute Unicode characters from the math blocks. What screen readers announce, why search misses them, when to use them.

You paste a bold bio into Instagram and it works. You paste the same bio somewhere else and half of it turns into empty rectangles, nobody can find your account by searching your own name, and someone using a screen reader tells you your profile reads as gibberish. Nothing about your text is “bold” — the generator replaced every letter with a different character that happens to look bold, and those characters have their own names, their own blocks, and their own consequences. There is no font involved anywhere in the process.

Once you see the mechanism, every behaviour on that list becomes predictable — and so does the narrow set of places this trick still works. Here is what our Fancy Text Generator is actually doing to your string.

The substitution, in one line

Plain H is U+0048. The “bold” 𝐇 you copied is U+1D407, MATHEMATICAL BOLD CAPITAL H — a different character in a different plane, related to H only by appearance. No font file was consulted, no styling was applied; the letters were swapped. The conversion is arithmetic: take the code point, subtract the offset for A, add the offset for the target block’s A. That is the whole transform, and you can watch it happen a character at a time in the Unicode Inspector, which lists every code point in a pasted string with its UTF-8 and UTF-16 encodings.

The Unicode Standard is blunt about this use. Chapter 22 of the core specification says “Characters from the Mathematical Alphanumeric symbols block should not be used to represent styling of nonmathematical text.” They exist so a physicist can write a bold vector and an italic scalar as distinct symbols in plain text, where there is no markup to carry the distinction. Social bios borrowed them.

That block also has holes. Twenty-four italic, script, Fraktur, and double-struck letters already existed in Letterlike Symbols (U+2100–U+214F), so Unicode left gaps rather than encode them twice. That is why the italic lowercase h in our output is U+210E, PLANCK CONSTANT — there is no MATHEMATICAL ITALIC SMALL H to use. Our tool patches eleven such gaps for script and seven for double-struck.

Where each of the 14 styles comes from

Fourteen styles, five mechanisms. The last column is NFKC — Unicode’s compatibility normalization, the fold most search and moderation pipelines apply before comparing anything:

StyleCharacters come fromNFKC folds to
BoldMath Alphanumeric Symbols, U+1D400 (digits U+1D7CE)plain ASCII
ItalicU+1D434, with h from U+210Eplain ASCII
Bold ItalicU+1D468plain ASCII
Cursive (Script)U+1D49C, 11 letters from Letterlike Symbolsplain ASCII
Bold CursiveU+1D4D0plain ASCII
Double-StruckU+1D538 (digits U+1D7D8), 7 from Letterlike Symbolsplain ASCII
MonospaceU+1D670 (digits U+1D7F6)plain ASCII
Sans BoldU+1D5D4 (digits U+1D7EC)plain ASCII
Small CapsIPA/Phonetic Extensions — ᴀ U+1D00, ʙ U+0299, ꜰ U+A730unchanged
Tiny / Superscriptmodifier letters — ʰ U+02B0, ⁿ U+207F, ⁱ U+2071lowercase ASCII
BubbleEnclosed Alphanumerics — Ⓐ U+24B6, ⓐ U+24D0, ① U+2460plain ASCII
SquaredEnclosed Alphanumeric Supplement — 🄰 U+1F130uppercase ASCII
Strikethroughletter + combining long stroke overlay U+0336marks survive
Underlineletter + combining low line U+0332marks survive

Three rows in that table are worth staring at.

Small caps doesn’t normalize. ʜᴇʟʟᴏ survives NFKC intact, because those are real phonetic letters for transcribing speech, not compatibility variants of ASCII. It is the style most likely to stay put — and the one a screen reader has the least chance of guessing at.

Squared normalizes to uppercase. 🄷🄴🄻🄻🄾 folds to HELLO, not Hello, because Enclosed Alphanumeric Supplement only has capitals. Our tool maps lowercase input to them anyway, so case is destroyed on the way in.

Strikethrough and underline aren’t substitutions at all. They interleave a combining mark after every character, so “hello” becomes ten code points, and NFKC leaves all ten alone. Even a system that normalizes before searching will not match hello against h̶e̶l̶l̶o̶.

What a screen reader actually says

This is the part worth caring about. Assistive tech reads the characters that are there, and the characters that are there are named things like MATHEMATICAL BOLD SMALL D.

Behaviour varies by screen reader, version, and browser, but it lands in one of three places, none of them good:

  1. It announces the character names, so a sentence comes out as “bold small d, bold small o, bold small n, bold small o, bold small t” — letter by letter, at typical screen-reader speed, for the whole bio.
  2. It skips them entirely, because the character has no pronunciation in that voice’s dictionary. Your styled words are silently deleted from the sentence.
  3. It switches language heuristics, the specific failure mode of the look-alike styles below, where a string genuinely contains Cyrillic, Greek, Hangul, and Bopomofo characters.

There is no fourth outcome where it reads normally, because nothing in the string says “this is the letter D, rendered bold.” That fact existed in the generator’s offset arithmetic and was discarded the moment you hit copy.

On the web you have an alternative — <strong> or <em> plus CSS, where styling is markup and text stays text. In a bio field you don’t, which is why the workaround exists and why it should be measured in words, not paragraphs. The same pragmatism runs through contrast ratios and colour-blind-safe palettes: keep the semantics, change the presentation.

The four other things that break

Search and Ctrl-F. Whether hello finds 𝗵𝗲𝗹𝗹𝗼 depends entirely on whether that particular search box normalizes first. Some do, some don’t, and you can’t tell from the outside. Assume the worst for anything you want found: your name, your company, your city.

Handles and hashtags. Usernames are ASCII-constrained on every major platform — X allows letters, digits and underscore; Discord usernames are lowercase letters, digits, . and _; Instagram allows letters, digits, periods and underscores. Fancy characters can only ever live in a display name, bio, or post body. A hashtag built from them is a different string from the one everyone else is using — a hashtag with one participant.

Tofu. A device renders 𝐇 only if some installed font has a glyph for U+1D407. Plenty of older Android builds, embedded browsers, and stripped-down desktop font sets have no coverage for Plane 1 at all, and the fallback is ☐. This is not a bug you can see from your own phone — your phone has the font.

Length limits. Every one of these is a full code point, and the math-block ones are two UTF-16 code units each — in a field with a character cap, some counters charge you once and some twice. Combining-mark styles charge per mark, which is where the next section gets expensive.

The neighbours: aesthetic, zalgo, upside-down, mirror

The same “no font” rule explains our other text tools, each of which raids a different corner of Unicode:

  • Aesthetic Text uses Fullwidth Forms: printable ASCII U+0021–U+007E shifted by exactly 0xFEE0 to land in U+FF01–U+FF5E, with the space becoming U+3000 IDEOGRAPHIC SPACE. These are compatibility characters for mixing Latin text into CJK typesetting, which is why they are monospaced-wide. NFKC folds all of them straight back to ASCII.
  • Zalgo Text adds nothing but combining diacritical marks from U+0300–U+036F — above, through, and below each base character. On our high setting a single letter can carry up to 21 marks, so a five-letter word can exceed a hundred code points — which is why it blows through character limits, why some renderers clip it, and why moderation bots strip it on sight.
  • Upside Down Text is a lookup table plus a string reversal, and the table is a tour of the standard: ɐ U+0250 from IPA Extensions, ∀ U+2200 from Mathematical Operators, ᗺ U+15FA from Unified Canadian Aboriginal Syllabics, Ԁ U+0500 from Cyrillic, ᄅ U+1105 (a Hangul jamo) for the digit 2, and ㄣ U+3123 (Bopomofo) for the digit 4. Letters that are their own rotation — H, I, N, O, S, X, Z, 0, 8 — stay plain ASCII.
  • Mirror Text reverses the string and, in mirror mode, swaps in approximate reversed glyphs: Я U+042F is Cyrillic, Ⴑ U+10B1 is Georgian, ꟻ U+A7FB and ꟼ U+A7FC are Latin Epigraphic letters from Latin Extended-D, ⅃ U+2143 is REVERSED SANS-SERIF CAPITAL L. Most letters have no mirrored form at all and pass through untouched, which the tool says on the page.

Then there’s the deliberately empty end of the spectrum: Invisible Character emits zero-width space U+200B, word joiner U+2060, or Hangul filler U+3164 for fields that reject blank input. Same family of trick, opposite goal — and the full catalogue of invisible characters covers what they do to string comparisons when they arrive uninvited.

The ASCII Art Generator is the opposite trade: its FIGlet banners use nothing but plain printable ASCII, so they survive every normalization, but a screen reader still reads them as a run of slashes and underscores.

Why spam filters care

Character substitution is a classic filter-evasion move: a keyword written in math-bold is, byte for byte, not that keyword. This is why serious moderation and anti-abuse pipelines NFKC-normalize before matching — the fold in that table turns the styled string back into the plain one, so the filter sees what a human sees. Filters that skip that step keep missing it, which is why the technique persists, and why a classifier can reasonably treat a bio made entirely of math-block characters as something trying not to be read.

What to do

The trick is fine in small doses and bad as a default:

  • One to three words, never a whole bio. A styled name or a single emphasised word costs you very little. A styled sentence is unreadable to a screen reader and unfindable by search.
  • Never style anything functional. No phone numbers, emails, link labels, hashtags, or the name people search to find you. Those need to survive normalization and get read aloud correctly.
  • Prefer the styles that fold. Bold, italic, cursive, bubble, and fullwidth all NFKC back to plain ASCII, so systems that normalize can still recover your text. Small caps and the combining styles don’t.
  • Assume it renders as boxes somewhere. If the message breaks when a word turns into ☐☐☐, don’t style that word.
  • Inspect before you trust. Paste anything suspicious into the Unicode Inspector and read the code points — a name that looks like yours but is built from Cyrillic look-alikes shows up instantly. That is the same substitution that makes homograph domains work, minus the domain.
  • Emoji are the other half of this. They are also multi-code-point sequences that render differently everywhere — see why emoji look different or broken.

Then type into the Fancy Text Generator, copy the one style you need, and leave the rest of the sentence alone. It runs entirely in your browser, shows all fourteen styles at once, and now you know exactly which characters it is handing you.