VOOZH about

URL: https://dev.to/techmind-click/unicode-text-styling-bold-italic-strikethrough-without-css-1h64

⇱ Unicode Text Styling: Bold, Italic & Strikethrough Without CSS - DEV Community


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);