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)