DEV Community

Cover image for How Fancy Letters Really Work: A Developer's Tour of Unicode's Hidden Alphabets
Letras Bonitas
Letras Bonitas

Posted on

How Fancy Letters Really Work: A Developer's Tour of Unicode's Hidden Alphabets

You've seen it in Instagram bios, Discord names and game nicknames: ๐›๐จ๐ฅ๐, ๐˜ช๐˜ต๐˜ข๐˜ญ๐˜ช๐˜ค, ๐“ˆ๐’ธ๐“‡๐’พ๐“…๐“‰, ๐”ฃ๐”ฏ๐”ž๐”จ๐”ฑ๐”ฒ๐”ฏ, โ“’โ“˜โ“กโ“’โ“›โ“”โ““, ๏ฝ†๏ฝ•๏ฝŒ๏ฝŒ๏ฝ—๏ฝ‰๏ฝ„๏ฝ”๏ฝˆ and tฬทฬขอ™hฬถฬคอiฬตอšอ‘sฬธฬฑฬพ kind of chaos, all pasted into fields that only accept plain text.

None of that is a font. No CSS, no rich text, no <b> tag. Every one of those glyphs is its own Unicode code point, and the "fancy text generators" all over the web are mostly lookup tables. This post goes through the blocks those characters come from, why the alphabets have strange gaps, and what they cost you in accessibility and searchability.

Fonts vs. characters

Normally "bold" is presentation. The character A (U+0041) stays the same, and your font or CSS decides how heavy it looks. Copy it somewhere else and the boldness is gone.

Fancy text works the other way. ๐€ is U+1D400 MATHEMATICAL BOLD CAPITAL A, a separate character from A. Because the style is part of the character, it survives copy-paste into any plain-text field, provided the reader's device has a font that can draw it.

That one fact explains both why these characters are useful and most of the trouble they cause.

1. Mathematical Alphanumeric Symbols (U+1D400โ€“U+1D7FF)

Most fancy text comes from this block. It was added in Unicode 3.1 (2001), mainly for mathematicians, because in math notation the style of a letter carries meaning. A bold x might be a vector, a fraktur ๐”ค a Lie algebra, and a double-struck โ„ the real numbers. Those distinctions need to survive in plain text, so Unicode encoded them as separate characters.

The block contains full Latin alphabets (and some Greek letters and digits) in styles such as:

Style Example Starts at (capital A)
Bold ๐€๐๐‚ ๐š๐›๐œ U+1D400
Italic ๐ด๐ต๐ถ ๐‘Ž๐‘๐‘ U+1D434
Bold italic ๐‘จ๐‘ฉ๐‘ช U+1D468
Script ๐’œ๐’ž๐’Ÿ U+1D49C
Bold script ๐“๐“‘๐“’ U+1D4D0
Fraktur ๐”„๐”…๐”‡ U+1D504
Double-struck ๐”ธ๐”น๐”ป U+1D538
Bold fraktur ๐•ฌ๐•ญ๐•ฎ U+1D56C
Sans-serif (+ bold/italic) ๐–  ๐—” ๐˜ˆ ๐˜ผ U+1D5A0โ€ฆ
Monospace ๐™ฐ๐™ฑ๐™ฒ U+1D670

There are also bold, double-struck, sans-serif and monospace digits (๐ŸŽ๐Ÿ๐Ÿ, ๐Ÿ˜๐Ÿ™๐Ÿš, ๐Ÿถ๐Ÿท๐Ÿธ) near the end of the block, starting at U+1D7CE.

The holes, and why they exist

Try generating italic "hello" by adding a fixed offset and you'll hit a problem: U+1D455, where italic small h should be, is unassigned. The same goes for several letters in the script, fraktur and double-struck alphabets.

The reason is history. Some of these letters were already in Unicode before 2001, in the Letterlike Symbols block (U+2100โ€“U+214F), because they were common symbols:

  • โ„Ž U+210E PLANCK CONSTANT, which serves as the italic small h
  • โ„ฌ U+212C SCRIPT CAPITAL B, โ„ฐ, โ„ฑ, โ„‹, โ„, โ„’, โ„ณ, โ„›, plus small โ„ฏ, โ„Š, โ„ด
  • โ„ญ U+212D BLACK-LETTER CAPITAL C, โ„Œ, โ„‘, โ„œ, โ„จ
  • โ„‚ U+2102 DOUBLE-STRUCK CAPITAL C, โ„, โ„•, โ„™, โ„š, โ„, โ„ค

Unicode doesn't encode the same character twice, so the matching slots in the math block were left as reserved holes. Any generator that hasn't special-cased them will output missing characters for words like "Chess" in double-struck.

2. Enclosed Alphanumerics (U+2460โ€“U+24FF) and the Supplement (U+1F100โ€“U+1F1FF)

The "bubble" letters come from Enclosed Alphanumerics:

  • circled: โ’ถ U+24B6 โ€ฆ โ“ U+24D0 โ€ฆ
  • parenthesized: โ’œโ’โ’ž
  • circled and parenthesized numbers: โ‘ โ‘กโ‘ข, โ‘ดโ‘ตโ‘ถ

Many of these came into Unicode for compatibility with older East Asian character sets, where enclosed numbers and letters were common in lists and labels.

The Enclosed Alphanumeric Supplement adds heavier variants:

  • negative circled: ๐Ÿ… U+1F150 โ€ฆ
  • squared: ๐Ÿ„ฐ๐Ÿ„ฑ๐Ÿ„ฒ
  • negative squared: ๐Ÿ…ฐ U+1F170 โ€ฆ, the source of the ๐Ÿ…ฐ๐Ÿ…ฑ "blood type" glyphs. With emoji presentation these display as red emoji on many platforms.
  • regional indicator symbols (U+1F1E6โ€“U+1F1FF), which aren't decorative letters. In pairs they form flags: ๐Ÿ‡ฒ + ๐Ÿ‡ฝ = ๐Ÿ‡ฒ๐Ÿ‡ฝ.

3. Halfwidth and Fullwidth Forms (U+FF00โ€“U+FFEF)

๏ฝ–๏ฝ๏ฝ๏ฝ๏ฝ’๏ฝ—๏ฝ๏ฝ–๏ฝ… ๏ฝ๏ฝ…๏ฝ“๏ฝ”๏ฝˆ๏ฝ…๏ฝ”๏ฝ‰๏ฝƒ text uses fullwidth Latin letters such as ๏ผก U+FF21 and ๏ฝ U+FF41.

These come from CJK computing. In Chinese, Japanese and Korean text, characters sit on a square grid, and older East Asian encodings included "wide" versions of Latin letters and digits so they would fill one full cell and line up with ideographs. Halfwidth katakana (๏ฝฑ๏ฝฒ๏ฝณ) are the reverse case: narrow forms from early systems with limited display space. Unicode kept both for round-trip compatibility with those legacy encodings.

Out of context, the extra width reads as a "vaporwave" look.

4. Combining characters, Zalgo, underline and strikethrough

A combining character has no width of its own. It attaches to the character before it. The main range is Combining Diacritical Marks, U+0300โ€“U+036F: e + U+0301 โ†’ รฉ.

Unicode doesn't limit how many marks you can stack on one base character. Stack dozens above and below and you get Zalgo text:

Hฬตฬกฬ›ฬ–ฬชฬeฬธฬขอšฬ“ ฬดฬฐฬ”cฬทฬบฬˆฬoฬถฬงอmฬตฬ™ฬŠeฬธฬซอ’sฬทฬชฬˆฬ

Renderers try to stack the marks, run out of room, and draw them over neighboring lines.

The same mechanism gives you underline and strikethrough in plain text:

  • U+0332 COMBINING LOW LINE after each letter: uฬฒnฬฒdฬฒeฬฒrฬฒlฬฒiฬฒnฬฒeฬฒ
  • U+0336 COMBINING LONG STROKE OVERLAY: sฬถtฬถrฬถiฬถkฬถeฬถ
  • U+0335 (short stroke) and U+0338 (solidus overlay) give variations such as sฬธlฬธaฬธsฬธhฬธ

How cleanly these join depends entirely on the font and the text renderer.

5. Small caps, superscripts and upside-down text

Unicode has no small-caps alphabet. Generators borrow letters from linguistics instead:

  • IPA (U+0250โ€“U+02AF) has ส€ ษข ษช สœ สŸ ษด ส ส™, which phoneticians use as real symbols.
  • Phonetic Extensions (U+1D00โ€“U+1D7F) adds แด€ U+1D00, แด„ แด… แด‡ แดŠ แด‹ แด แด แด˜ แด› แดœ แด  แดก แดข, and modifier letters such as แดฌ แดฎ แดฐ.

The result, ๊œฑแดแด€สŸสŸ แด„แด€แด˜๊œฑ, is assembled from several blocks, which is why some letters look slightly off.

Superscript text (หขแต˜แต–แต‰สณหขแถœสณโฑแต–แต—) works the same way, combining modifier letters from Spacing Modifier Letters, Phonetic Extensions and Superscripts and Subscripts. A few letters have no superscript form at all; there is no dedicated superscript small q, for example. Generators either substitute something similar or leave the letter as it is.

Upside-down text (ส‡xวส‡ uสop-วpแด‰sdn) combines character reversal with a lookup table of look-alikes: ษ U+0250 LATIN SMALL LETTER TURNED A, ว, ษฅ, ษฏ, ส‡, plus punctuation such as ยฟ and ยก. These are mostly real IPA letters used for their shape, not true rotated copies.

6. A tiny generator in JavaScript

Since the bold block has no holes, a bold generator is just arithmetic on code points:

const toMathBold = (s) =>
  [...s].map((ch) => {
    const c = ch.codePointAt(0);
    if (c >= 0x41 && c <= 0x5a) return String.fromCodePoint(0x1d400 + (c - 0x41)); // Aโ€“Z
    if (c >= 0x61 && c <= 0x7a) return String.fromCodePoint(0x1d41a + (c - 0x61)); // aโ€“z
    if (c >= 0x30 && c <= 0x39) return String.fromCodePoint(0x1d7ce + (c - 0x30)); // 0โ€“9
    return ch;
  }).join("");

const out = toMathBold("Hello World 2026");
console.log(out);                     // ๐‡๐ž๐ฅ๐ฅ๐จ ๐–๐จ๐ซ๐ฅ๐ ๐Ÿ๐ŸŽ๐Ÿ๐Ÿ”
console.log(out.length, [...out].length); // 30 16
console.log(out.normalize("NFKC"));   // Hello World 2026
Enter fullscreen mode Exit fullscreen mode

Three things to notice in the output (I ran this on Node 22):

  1. out.length is 30, not 16. These characters are outside the Basic Multilingual Plane, so each one takes a UTF-16 surrogate pair. Use codePointAt, String.fromCodePoint and [...str], not charCodeAt or split(""), or you'll split characters in half. It also matters for character limits: a platform counting UTF-16 units sees a 16-character name as 30.
  2. NFKC normalization turns it back into "Hello World 2026". These characters have compatibility decompositions to plain letters. That's useful for search and moderation pipelines, and it shows that Unicode itself treats them as styled versions of ordinary letters.
  3. For italic, script, fraktur or double-struck, you need an exception map for the Letterlike Symbols holes (h โ†’ โ„Ž, C โ†’ โ„‚ and so on). Otherwise you'll output unassigned code points.

7. Caveats: when not to use fancy text

These characters are fun, but they have real costs, and you should know about them before putting them in front of users.

Accessibility. Screen readers handle math alphanumerics inconsistently. Depending on the reader and its settings, ๐‡๐ž๐ฅ๐ฅ๐จ may be read as "mathematical bold capital H, mathematical bold small eโ€ฆ", spelled out letter by letter, or skipped entirely. Zalgo and upside-down text are worse. If someone who uses a screen reader needs to understand it, don't style it this way. At most, style a word or two and keep the important information in plain text.

Search and indexing. To a naive search, ๐œ๐จ๐Ÿ๐Ÿ๐ž๐ž and coffee are different strings. Some systems apply NFKC or similar folding and some don't, so a fancy-text username or bio may not show up when people search for the plain word. Don't put your brand name, handle or keywords only in fancy letters.

Platform support. Most fields on Instagram, TikTok, WhatsApp, Discord and X accept these characters because they're just Unicode. Rendering depends on the viewer's fonts, and older Android phones in particular may show โ–ก boxes. Games are stricter. Free Fire nicknames, for example, are a popular use, but games validate names against their own character rules and length limits, which can change, so test on the platform before you commit. Display names and handles also usually follow different rules.

Copy-paste and data. The style travels with the text, so it ends up in places you didn't intend: spreadsheets, CSV exports, CRM fields, grep failures. For developers, that also means:

  • validate and normalize (NFKC) usernames to prevent look-alike impersonation (๐š๐๐ฆ๐ข๐ง vs admin)
  • count graphemes or code points, not UTF-16 units, when enforcing limits
  • strip or limit combining marks if Zalgo would break your layout

Never use them for passwords, email addresses, URLs, legal or medical text, or anything someone needs to type back.

Try it

To see all of these blocks side by side without writing your own lookup tables, try Letras Bonitasz. It's a free Spanish-language generator: type a word and it shows the math, enclosed, fullwidth, small-caps and upside-down styles, and you can copy whichever you want. It has a dedicated Zalgo text page for testing how your app handles stacked combining marks, which works well as test input for UI layouts.

A fun exercise: paste its output into your own app's username field, run it through normalize("NFKC"), and see what your validation, search and length checks do with it.


Further reading: Wikipedia's articles on Mathematical Alphanumeric Symbols, Enclosed Alphanumerics, Halfwidth and Fullwidth Forms, Combining character and Zalgo text, and the official charts at unicode.org/charts.

Top comments (0)