Skip to content
Allin

The Fake Fonts in Your Bio Are Not Fonts: What Those Characters Really Are

Published 9/26/2025 · 13 min read · Text & language tools

Daniel Okonkwo

Daniel OkonkwoFront-end developer and tech writer at Allin

Web performance · File formats

Checked against 5 sources

View profile
In short

The generators that promise a hundred fonts for your profile do not change any font. They swap every letter for a different character that happens to be drawn in a different style. Paste "Allin" through a bold generator and you get the code points U+1D400 U+1D425 U+1D425 U+1D422 U+1D427 — not one of them is the letter A, l, l, i or n. They are MATHEMATICAL BOLD CAPITAL A and its neighbours, characters the Unicode Standard added so that a physics paper can distinguish a bold vector from an italic scalar. Because they are different characters, everything that compares text stops working: "Allin".includes(bold) returns false, toLowerCase() leaves them untouched, /\w/ and /[a-zA-Z]/ do not match them, and Number("𝟏𝟐𝟑") is NaN. A 33-character sentence grows from 33 to 117 UTF-8 bytes. Assistive technology reads the buffer it is given, and these characters carry no pronunciation, so a bio written in them is announced wrongly or skipped. The block also has 28 holes where letters were already encoded elsewhere, which is why every generator ships a patch table. NFKC normalisation folds all of it straight back to plain ASCII, so any platform that normalises deletes the effect silently.

A bold-looking 𝐀 is not the letter A in a bold font — it is U+1D400, a separate character invented for mathematics. We printed the code points, measured what it costs in bytes, broke search, sorting and word counts with it, and scanned the block to find its 28 holes.

What the generator actually does

A font is a file. It contains outlines, and it maps the letter A to a shape. Choosing a bold font does not change the letter A; it changes how the letter A is drawn. Every one of the styles a bio generator offers works the other way round: the letter is replaced and the drawing is whatever the platform's font already had for the replacement.

Print the code points and the substitution is obvious. The word Allin in the bold style is U+1D400 U+1D425 U+1D425 U+1D422 U+1D427; in double-struck it is U+1D538 U+1D55D U+1D55D U+1D55A U+1D55F; in monospace U+1D670 U+1D695 U+1D695 U+1D692 U+1D697. Five visible letters, five code points, none of them in the ASCII range. In JavaScript the string reports .length 10, because each of these characters needs two UTF-16 code units.

They cost more to store, too. The sentence "Building tools for six languages." is 33 characters and 33 UTF-8 bytes because it is pure ASCII. The same sentence in the bold style is still 33 characters, but 61 UTF-16 code units and 117 UTF-8 bytes — three and a half times the payload for the same words. On a platform that measures a bio in bytes rather than characters, that is most of your budget spent on nothing.

Everything that compares text stops matching

Substring search fails first. "Allin".includes(bold) returns false, and so does the reverse, because not a single code point is shared. A site search, a browser's find-on-page, a database LIKE clause and a hashtag index all reduce to the same comparison, and all of them miss. If your display name is written in mathematical bold, someone typing your actual name into the search box will not find you.

Case conversion is a no-op. bold.toLowerCase() returns the identical string, because Unicode's case mappings for the mathematical alphanumerics map each character to itself. So a case-insensitive search that lowercases both sides before comparing still fails. The one thing that does work is collation: Intl.Collator("en", { sensitivity: "base" }).compare("𝐀", "A") returns 0, because ICU's collation tables fold the styled letters onto their base letters. That is worth knowing, because it explains why the same string is findable in one product and invisible in another — it depends entirely on whether the search path went through a collator or through string equality.

Character classes disagree with each other. /[a-zA-Z]/.test("𝐀") is false, /\w/u.test("𝐀") is false, but /\p{L}/u.test("𝐀") is true — the character is a letter by Unicode's own general category (Lu for the capital, Ll for the small), it simply is not an ASCII letter. Digits behave the same way: /\d/u.test("𝟏") is false while /\p{Nd}/u.test("𝟏") is true, and Number("𝟏𝟐𝟑") is NaN even though the same string after NFKC parses as 123. Two validators written by two teams will therefore reach opposite verdicts on the same bio.

Sorting splits the difference. A plain Array.prototype.sort() compares UTF-16 code units, so ["𝐁rown", "Adams", "Clark"] comes out as Adams, Clark, 𝐁rown — the styled name is exiled to the end of the list because U+1D400 is numerically larger than every ASCII letter. Switch to localeCompare and the same three sort as Adams, 𝐁rown, Clark, which is what a reader expects. Neither is wrong; they answer different questions, and a directory that uses the fast one will file you under nothing.

The accessibility cost, stated plainly

A screen reader does not see your bio. It receives the sequence of characters the platform stored and hands it to a speech synthesiser, which looks each character up in a pronunciation dictionary. That dictionary is built for the letters people write with. U+1D400 is not one of them. The synthesiser has three options: read the character's Unicode name aloud, read a generic placeholder, or skip it. All three are what happens in practice, and which one you get depends on the reader, the voice and the verbosity setting — not on anything the author controls.

The cost is easy to underestimate because the failure is invisible to the person who caused it. You see a stylish name. A blind reader may hear thirty character names in a row, or hear the name of your account disappear entirely, leaving a card with a picture and no title. There is no setting a reader can flip to make this work, because as far as the machine is concerned the text really does say something else. That is the whole argument; it does not need any moral framing on top of it.

One family is worse than the rest. The circled and negative-circled sets — Ⓐ U+24B6, 🅐 U+1F150 — are not letters at all: their general category is So, Symbol/other. Software that filters a field to letters and spaces will drop them, and software that counts letters will count zero.

The 28 holes in the block, and why every generator has a patch table

The Mathematical Alphanumeric Symbols block runs from U+1D400 to U+1D7FF, which is 1,024 code points. Scanning it for assigned characters gives 996, so 28 slots are empty: 652 Latin letters (thirteen styles of 52, minus the missing ones), 294 Greek and related characters, and 50 digits in five styles.

Four of the 28 are plain reserved padding: U+1D6A6 and U+1D6A7 sit between the italic dotless j and the bold capital alpha, and U+1D7CC and U+1D7CD sit between the bold small digamma and the bold digit zero. The other 24 are letter slots that were deliberately left empty because that exact character had already been encoded years earlier in the Letterlike Symbols block at U+2100–U+214F.

The list is short enough to give in full. Italic h is missing because it was already there as U+210E PLANCK CONSTANT. Script is missing B, E, F, H, I, L, M, R (they live at U+212C, U+2130, U+2131, U+210B, U+2110, U+2112, U+2133, U+211B) and the small letters e, g, o (U+212F, U+210A, U+2134). Fraktur is missing C, H, I, R, Z (U+212D, U+210C, U+2111, U+211C, U+2128). Double-struck is missing C, H, N, P, Q, R, Z (U+2102, U+210D, U+2115, U+2119, U+211A, U+211D, U+2124) — the set-theory letters every mathematician already had.

This is why the block is a giveaway. A generator that maps letters by simple arithmetic — take the code point of A, add the offset for the style — produces unassigned code points for those 24 letters, and unassigned code points render as an empty box. A well-built generator carries the patch table. A quick one does not, which is why script text so often has a hole exactly where an E or an R should be.

Where these characters are the right answer

None of this makes the characters a mistake. They exist because in mathematics the style is the meaning. A bold R and an italic R are not the same symbol read two ways; they are two symbols. ℝ is the real numbers, R may be a ring, and a bold vector is not the scalar written with the same letter. Encoding the styles as distinct characters is what lets a formula survive being copied out of a paper into an email without losing its content.

That is also the test for whether you are using them well. If the style carries information that would be lost without it, the character is the right tool. If the style is decoration on ordinary prose, plain letters plus real formatting — the bold your editor produces, which leaves the letter A as the letter A — is the right tool, and it is the only one a screen reader, a search index and a sorting routine can all handle.

NFKC, and the platforms that delete the effect without telling you

Unicode defines a compatibility decomposition for each of these characters: the standard itself records that U+1D400 is a formatting variant of A. Normalisation form NFKC applies those decompositions and then recomposes, so bold.normalize("NFKC") on our test sentence returns the plain ASCII original, character for character — we compared the two strings and they are equal. The same happens to fullwidth text (U+FF21 becomes A), to circled letters (U+24B6 becomes A) and to the superscript modifier letters (U+1D2C becomes A).

Many services normalise on the way in, usually to stop two accounts having names that look identical. When they do, the styling you pasted is gone by the time it is saved, and nothing tells you: you submit a stylish bio and get a plain one back. Nothing is broken; the platform simply asked the character database what your text really said and stored that answer.

Two families survive NFKC, and knowing which is useful. The small-capitals letters — ᴀ is U+1D00 LATIN LETTER SMALL CAPITAL A — are phonetic letters from the IPA extensions with no compatibility decomposition at all, so they pass through normalisation untouched. So do the negative circled letters, 🅐 U+1F150 and friends. They are not stylistic variants of A in the standard's eyes; they are their own characters. That makes them more durable and no more readable, which is the trade the whole category runs on.

A practical rule for a profile

Keep the fields people search on in ordinary letters: your name, your handle, the words someone would type to find you. Those are the fields where substitution costs you something measurable. If you want one flourish, put it somewhere nobody queries — a separator, a single decorative character — and keep the surrounding words plain.

And keep a copy of the plain version. Because the transformation is a lookup table, going back is a lookup table too: normalise with NFKC and you recover the original for the mathematical, fullwidth, circled and superscript families exactly. For the small-capitals and negative-circled families you need a reverse map, because normalisation will not do it for you.

One visible letter A, six ways of encoding it — measured with Node 26 and Python's unicodedata 15.1
CharacterCode pointUnicode nameCategoryAfter NFKC
AU+0041LATIN CAPITAL LETTER ALu (letter)A (unchanged)
𝐀U+1D400MATHEMATICAL BOLD CAPITAL ALu (letter)A
𝔸U+1D538MATHEMATICAL DOUBLE-STRUCK CAPITAL ALu (letter)A
U+FF21FULLWIDTH LATIN CAPITAL LETTER ALu (letter)A
U+24B6CIRCLED LATIN CAPITAL LETTER ASo (symbol, not a letter)A
U+1D00LATIN LETTER SMALL CAPITAL ALl (letter)ᴀ (survives)
Fancy font generatorTurn text into bold, italic or monospace Unicode for bios and posts (𝗕𝗼𝗹𝗱, 𝘐𝘵𝘢𝘭𝘪𝘤).Try the tool

Frequently asked questions

Will a fake-font bio hurt my reach?
It removes you from text search on those words, which is measurable and independent of any ranking algorithm. "Allin".includes(bold) is false, so a query for your plain name cannot match a styled one by substring. Whether a specific platform compensates depends on whether it normalises or collates before indexing, and platforms do not publish that. The safe reading is that any word you want to be found by should be written in ordinary letters.
Why do some letters show as empty boxes?
Two different reasons, and they look the same. Either the generator emitted one of the 28 unassigned code points in the block — most often a script or fraktur or double-struck letter from the list of 24 holes — or the character exists but the font on the reader's device has no glyph for it. The first is the generator's bug and shows for everyone; the second depends on the device, which is why the same message looks fine on your phone and broken on someone else's.
How do I convert styled text back to plain letters?
For the mathematical, fullwidth, circled and superscript families, one call does it: text.normalize("NFKC"). We checked it round-trips a full sentence exactly — the normalised bold sentence compares equal to the ASCII original. Small capitals and negative circled letters have no compatibility decomposition and survive normalisation, so those need an explicit reverse table. A converter tool bundles both paths; a one-line normalize handles the majority.
Is there any style that is safe to use?
Not in the sense of being invisible to software — every one of them changes the code points, and that is the whole mechanism. What varies is how much you lose. Substituting one decorative character between two plain words costs almost nothing. Substituting an entire display name costs search, sorting, and the ability of assistive technology to read it. The scale of the harm tracks the number of substituted letters, so use them by the character rather than by the sentence.
Why does my styled text sometimes come back plain after I save it?
The service normalised it. NFKC is the standard way to decide whether two strings are "the same text", and platforms apply it to display names and handles to stop lookalike impersonation. Once applied, U+1D400 becomes A and there is nothing left to undo. Nothing is broken and no error is shown, because from the platform's point of view it stored exactly what you typed — under the definition of "what you typed" that the character database provides.
Do these characters count against a character limit?
It depends entirely on what the limit counts, which is the same problem character limits always have. By code points, our test sentence is 33 either way. By UTF-16 code units — what JavaScript's .length reports and what many form validators use — the plain sentence is 33 and the bold one 61. By UTF-8 bytes it is 33 against 117. A form that lets you type 33 plain characters may accept only 16 styled ones, and the counter beside the box will not explain why.

Articles you may find interesting

All guides
GuideMarkdown Task Lists and What Actually Renders WhereTask lists are not in CommonMark. They are a GitHub Flavored Markdown extension, which is why the same file shows checkboxes in one place and literal brackets in another. The exact marker rule, what nesting does, and a table of what is CommonMark, what is GFM and what is neither — checked against both specs and four renderers.ExplainerCounting Characters Against a Limit Someone Else SetOne emoji is 1 character, or 7, or 11, or 25, depending on who is counting. Which one your form, your database and your SMS gateway mean — and a one-paste test that tells you which you are facing.ExplainerHow Instagram Fonts Actually Work (They Are Not Fonts)Nothing is styled. Each letter is swapped for a different Unicode character that happens to look bold or cursive — which is why a screen reader reads the result as gibberish and some devices show empty boxes.ExplainerDetecting the Language of a Text, and Why Short Texts FailMeasured, not asserted: 90 short real phrases across six languages, none declined and 68 right — 76%, falling to 64% under sixteen letters. Four of the wrong answers came back at 100% confidence.How-toNumbering the Lines of a Text for a Review with Several PeopleNumbering starts at 1 and cannot be set to 0, the alignment is spaces rather than zeros, and the remover undoes eight of the eleven separators without touching the indentation. What it still cannot do is tell your numbers from its own.ExplainerSentence Case and Title Case: The Rules Differ by LanguageEnglish title case has three different cutoffs depending on the style guide. French, Spanish, Portuguese and Italian have none at all. German capitalises every noun. The tool knows about none of this — here is exactly what it does.

Related tools

Sources

Spotted a mistake in this article?