Escaping symbols in CSS, Python, JavaScript and JSON
Every language has its own escape syntax for a Unicode character, and the differences
cause real bugs. CSS needs a trailing space after a hex escape when the next character
is a hex digit, JavaScript needs braces around code points above U+FFFF, JSON cannot
express them at all without surrogate pairs, and Python has separate \u and \U
forms. The table below maps one character, ā U+2713, across all of them.
| Context | Escape | Notes |
|---|---|---|
| Python | "\u2713" |
Four hex digits only |
| Python, above BMP | "\U0001F600" |
Eight hex digits, capital U |
| JavaScript | "\u2713" |
Four hex digits only |
| JavaScript, above BMP | "\u{1F600}" |
Braces, ES6 and later |
| JSON | "\u2713" |
Four hex digits only |
| JSON, above BMP | "\ud83d\ude00" |
Surrogate pair required |
| LaTeX | \checkmark |
Math mode; command names differ per symbol |
| HTML | ✓ |
See the HTML guide |
CSS
A CSS escape is a backslash followed by the hex code point.
.badge::before {
content: "\2713";
}
The trailing space rule catches people out. A hex escape is greedy: it consumes as
many hex digits as follow it. In "\2713" the escape ends at the closing quote, so
the space is not needed. But in the following, the escape would swallow the a:
/* Wrong: \2713a is parsed as the escape for U+2713A */
content: "\2713abc";
/* Right: the space terminates the escape */
content: "\2713 abc";
The space is consumed by the parser as part of the escape terminator and does not
appear in the output. Above U+FFFF, use six hex digits, for example "\01F600 ".
Python
check = "\u2713" # code points up to U+FFFF
grin = "\U0001F600" # code points above U+FFFF, capital U, 8 digits
check = chr(0x2713) # same result, easier to read for a variable code point
A lowercase \u only accepts exactly four hex digits. Passing a longer value raises a
syntax error. Use \U with eight digits, or chr(), for anything above the Basic
Multilingual Plane.
For Python source, also confirm the file is UTF-8. Python 3 defaults to UTF-8 so
writing the character directly is usually simplest. Write "\u2713" when you cannot
guarantee the file encoding survives whatever pipeline it passes through.
JavaScript
const check = "\u2713"; // up to U+FFFF
const grin = "\u{1F600}"; // above U+FFFF, ES6 and later
const same = String.fromCodePoint(0x2713);
Inside a template literal the escape behaves identically, but a backslash still needs
escaping when you mean a literal one. Watch length calculations: a string containing
an emoji built from a surrogate pair reports a length of 2 because length counts
UTF-16 code units, not characters. Use [...str].length when you need character
counts.
JSON
JSON has no form for a code point above U+FFFF other than a surrogate pair, and
\u{...} is invalid JSON.
{
"bmp": "\u2713",
"astral": "\ud83d\ude00"
}
The practical advice is to skip escaping entirely and write the character as UTF-8 in
the file, which is valid JSON for the whole of Unicode:
{
"check": "ā"
}
That only breaks if a downstream tool re-encodes the file as ASCII. When in doubt,
surrogate pairs are always safe, and json.dumps(..., ensure_ascii=False) in Python
produces the readable UTF-8 form.
LaTeX
LaTeX commands are names, not code points, and most require math mode.
$\checkmark$ \quad $\degree$ \quad $\pm$ \quad $\times$
Wrap the command in $...$ unless the command is a text command such as
\textcopyright. The symbol pages on this site mark each LaTeX command as math mode
or text mode.
FAQ
Why does "\u2713" fail in some languages?
The escape lengths differ. Python, JavaScript and JSON all read \u as exactly four
hex digits. A five or six digit code point needs a different form in each language,
which the table above lists.
Do I need to escape symbols in an SQL string?
SQL strings are Unicode-aware in every current database when the connection and column
collation are correct. Write the character directly. If you must escape, use the
database's own function rather than a language-level Unicode escape.
Is it safe to paste the character instead of escaping it?
Yes, in files saved as UTF-8. The escape forms exist for cases where the file encoding
is not under your control, for comparing invisible characters, and for source that has
to survive a pipeline that mangles non-ASCII bytes.