Technology

unicode-range

CSS descriptor in @font-face that specifies which Unicode characters to download from a font file, enabling automatic subsetting by the browser.

The unicode-range descriptor in @font-face tells browsers which Unicode characters a font file contains, enabling conditional loading: the font file is only downloaded if the page contains at least one character from the specified range. This is a powerful performance tool that makes it practical to declare many font files without paying the download cost for each.

/* Latin Extended subset */
@font-face {
  font-family: 'Source Serif';
  src: url('/fonts/source-serif-latin-ext.woff2') format('woff2');
  font-weight: 400;
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020,
                 U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F,
                 U+A720-A7FF;
}

/* Basic Latin — always loads if Latin text is present */
@font-face {
  font-family: 'Source Serif';
  src: url('/fonts/source-serif-latin.woff2') format('woff2');
  font-weight: 400;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC,
                 U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
                 U+FEFF, U+FFFD;
}

Syntax for unicode-range values:

  • Single codepoint: U+26 (the ampersand &)
  • Codepoint range: U+0025-00FF
  • Wildcard range: U+00?? (equivalent to U+0000-00FF)

How browsers use it:

Before downloading any font file in a font stack, the browser scans the text content of the page and checks which unicode-range declarations include characters that are actually present. Files whose ranges have no matching characters are never requested. This happens before rendering, so it doesn't cause layout shifts — the browser just skips the fetch entirely.

This is exactly how Google Fonts serves multilingual font families. Noto Sans has separate @font-face declarations for Latin, Greek, Cyrillic, Devanagari, Japanese, Chinese, and dozens of other scripts — but a page with only Latin content downloads only the Latin subset file.

Practical use cases:

/* Only load currency symbols font if page has non-Latin currencies */
@font-face {
  font-family: 'CurrencySymbols';
  src: url('/fonts/currencies.woff2') format('woff2');
  unicode-range: U+20A0-20CF; /* Currency Symbols block */
}

/* Load math symbols only when needed */
@font-face {
  font-family: 'MathFont';
  src: url('/fonts/math.woff2') format('woff2');
  unicode-range: U+2200-22FF; /* Mathematical Operators */
}

One important nuance: unicode-range checks characters in the DOM, not what's actually visible. Content in display: none elements still triggers downloads if those elements contain characters in the range. This rarely causes practical issues but is worth knowing when debugging unexpected font requests.

คำศัพท์ที่เกี่ยวข้อง

ฟอนต์ที่แสดงแนวคิดนี้

เรียนรู้เพิ่มเติม