Most developers know HTML bold and italic tags. But what happens when your text lands in an environment that strips all HTML â WhatsApp, Discord DMs, plain text emails, or Instagram captions?
The answer: Unicode mathematical characters.
Why Unicode Styling Works Everywhere
Unicode includes dedicated ranges for bold, italic, bold-italic, and styled variants of Latin letters. These are not formatting instructions â they are different characters that happen to look styled.
Regular: Hello World
Bold: ððēðđðđðž ðŠðžðŋðđðą
Italic: ððĶððð° ðð°ðģððĨ
Strike: HĖķeĖķlĖķlĖķoĖķ ĖķWĖķoĖķrĖķlĖķdĖķ
Because they are actual characters, they survive:
- Copy-paste into any app
- Plain text environments
- WhatsApp, Discord, Instagram, Telegram
- SMS on modern phones
- LinkedIn posts (which strip HTML completely)
The Unicode Ranges
Mathematical Bold â starts at U+1D400
// Offset for bold lowercase 'a' = U+1D41A
const BOLD_OFFSET_LOWER = 0x1D41A - 'a'.charCodeAt(0);
const BOLD_OFFSET_UPPER = 0x1D400 - 'A'.charCodeAt(0);
function toBold(char) {
const code = char.charCodeAt(0);
if (code >= 65 && code <= 90) // A-Z
return String.fromCodePoint(code + BOLD_OFFSET_UPPER);
if (code >= 97 && code <= 122) // a-z
return String.fromCodePoint(code + BOLD_OFFSET_LOWER);
return char;
}
const boldText = (str) => [...str].map(toBold).join('');
console.log(boldText("Hello World")); // ððēðđðđðž ðŠðžðŋðđðą
Mathematical Italic â starts at U+1D434
const ITALIC_OFFSET_LOWER = 0x1D44E - 'a'.charCodeAt(0);
const ITALIC_OFFSET_UPPER = 0x1D434 - 'A'.charCodeAt(0);
function toItalic(char) {
const code = char.charCodeAt(0);
if (code >= 65 && code <= 90)
return String.fromCodePoint(code + ITALIC_OFFSET_UPPER);
if (code >= 97 && code <= 122)
return String.fromCodePoint(code + ITALIC_OFFSET_LOWER);
return char;
}
*Strikethrough *â Unicode combining character U+0336
// Add combining strikethrough after each character
const toStrikethrough = (str) =>
[...str].map(c => c + '\u0336').join('');
console.log(toStrikethrough("Hello")); // HĖķeĖķlĖķlĖķoĖķ
*Underline *â Unicode combining character U+0332
const toUnderline = (str) =>
[...str].map(c => c + '\u0332').join('');
TypeScript Implementation with Char Maps
For production use, a char map approach is more reliable than offset math â handles edge cases like special chars that don't exist in bold Unicode ranges:
const BOLD_MAP: Record<string, string> = {
a:'ðŪ', b:'ðŊ', c:'ð°', d:'ðą', e:'ðē', f:'ðģ', g:'ðī',
h:'ðĩ', i:'ðķ', j:'ð·', k:'ðļ', l:'ðđ', m:'ðš', n:'ðŧ',
o:'ðž', p:'ð―', q:'ðū', r:'ðŋ', s:'ð', t:'ð', u:'ð',
v:'ð', w:'ð', x:'ð
', y:'ð', z:'ð',
A:'ð', B:'ð', C:'ð', // ... etc
};
function applyMap(text: string, map: Record<string, string>): string {
return [...text].map(c => map[c] ?? c).join('');
}
export const toBold = (text: string) => applyMap(text, BOLD_MAP);
Top comments (0)