CSE 134B Web Client Languages
  1. Presentation
  2. Fonts
  3. Type for people

Presentation · Fonts 10

Type for people

Every earlier page chose type for an imagined reader. Real readers zoom, override fonts, ask for more contrast, read with dyslexia or low vision, and read in scripts that have no capitals, no italics, or letters that must join. This page is about type that keeps working for all of them, and it closes the section.

The first half is accessibility: size, weight and contrast together, the reader's own settings, and what the evidence says about fonts marketed to help. The second is internationalization: what the world's scripts need from type and CSS, and which Latin habits break them. Fonts in CSS, Size, spacing and measure and CSS for people laid groundwork that this page links rather than repeats.

1. Size, weight and contrast work together

WCAG measures contrast between two colors. Success criterion 1.4.3, Contrast (Minimum), at level AA, asks for a ratio of at least 4.5:1 between text and its background, and 3:1 for large-scale text, which WCAG defines as at least 18 point, or 14 point bold, "or font size that would yield equivalent size for Chinese, Japanese and Korean (CJK) fonts." In CSS pixels that is about 24 and 18.7. The ratio is computed from the relative luminance of the two colors, and CSS for people covers the thresholds and the DevTools contrast check, and Color for people shows how the ratio is computed.

The formula knows nothing about the letters. A light gray that passes at 4.5:1 is measured the same way whether it colors a sturdy text face at 18 pixels or a hairline display face at 12. Readers do not see it the same way. A thin stroke covers fewer pixels, and anti-aliasing blends its edges with the background, so the text appears lighter than its nominal color. WCAG's own explanation of the criterion says so plainly: fonts "with extraordinarily thin strokes or unusual features and characteristics" are harder to read at lower contrast, its size thresholds hold "except for very thin or unusual fonts," and when anti-aliasing makes thin text look faint, "best practice would be for authors to choose a font with stronger/thicker lines."

So treat size, weight and color as one decision. Three tendencies follow:

  • Small text needs weight and contrast most. Text at 14 pixels in weight 300 and mid-gray can pass the ratio and still fail many readers.
  • Light weights are display type. Variable fonts explains why, and how a grade axis adds weight without changing width.
  • Light on dark changes the balance. Strokes seem to spread on a dark ground, so dark mode often wants a slightly lighter weight, not the same settings with the colors swapped (knockout text).

Some newer contrast methods try to account for this. The best known, APCA (the Accessible Perceptual Contrast Algorithm), weighs the polarity of the colors and ties its thresholds to font size and weight. It is an idea to know about, not a standard. It appeared in early drafts of WCAG 3, was dropped from the working draft in July 2023, and the current editor's draft says only that "the contrast algorithm used in WCAG 3 is yet to be determined." WCAG 2.2's ratio is still what audits and laws use: meet it, then add judgment about weight and size.

2. The reader's settings win

CSS is a request. The reader's browser, operating system and assistive technology have the last word, and each setting below is a reason some reader can use the web at all.

  • Default font size. Type in rem and em grows with it; type in pixels ignores it (why).
  • Zoom. WCAG 1.4.4 asks that text can be resized to 200 percent without loss of content or function. Fixed heights are what usually break.
  • Minimum font size. Chrome, Firefox and Safari let a reader set a floor; 9-pixel captions become 12-pixel captions and must still fit.
  • Their own fonts, per script. Beyond the font settings, user style sheets and reader modes described in Fonts in CSS, Firefox keeps its font choices per script, so a reader can pick one face for Latin text and another for Arabic or Devanagari.

The habits that survive all of these, generic families, relative sizes and boxes that grow, are the ones Fonts in CSS lists.

Forced colors and high contrast

CSS for people introduces forced colors mode, set by Windows contrast themes, in which the reader's small palette replaces the page's colors. For type, the CSS Color Adjustment specification adds three effects: text-shadow computes to none; the browser may paint a solid "backplate" behind text so it stays readable over an image; and it should draw emoji in monochrome where possible. Fonts and sizes are left alone. So text legible over a photograph only because of its shadow loses the shadow, and a heading told apart only by color looks like body text, while hierarchy built from size, weight and space survives. Emulate forced-colors: active in DevTools' Rendering panel, and adjust only what truly breaks, with system colors:

@media (forced-colors: active) { @layer components { /* The badge was a colored pill with no border: give it one. */ mark[data-badge] { border: 1px solid CanvasText; } } }

The fourth reader setting is spacing. WCAG 1.4.12, Text Spacing, asks that nothing be lost when a reader raises line height, paragraph spacing, letter spacing and word spacing to set values. Size, spacing and measure quotes the criterion, explains what it does and does not require, and shows the bookmarklet that tests it. Its exception for scripts that do not use a property, such as letter spacing in Arabic, returns in section 9.

3. All caps and letter spacing

Capitals are uniform rectangles. Lowercase letters rise and fall, and readers use those outlines; long passages in capitals read slowly and feel like shouting. Capitals suit short labels and abbreviations, with a little extra tracking, because letters designed to sit beside lowercase crowd each other. Size, spacing and measure gives the values, and the British Dyslexia Association's guidance, quoted in Choosing and pairing, asks for no capitals in continuous text.

How the capitals get there matters too. Text typed in capitals is capitals in the HTML, and a screen reader meets the letters, not the look. Speech engines guess whether a word in capitals is an abbreviation, and they guess differently: a word such as "CONTACT" may be read as a word, while an unfamiliar or short one may be spelled out letter by letter, as if it were an initialism. Type the words in normal case and let CSS draw the capitals:

<h2 class="eyebrow">Contact us</h2> <!-- not CONTACT US --> <style> h2.eyebrow { text-transform: uppercase; letter-spacing: 0.08em; } </style>

text-transform changes only how the text is drawn. The DOM, the accessible name, search and translation all see "Contact us." That is the right default, not a guarantee: guidance from the US government's CMS design system notes that some screen readers occasionally spell out text even when the HTML is in normal case and CSS supplies the capitals. When a label matters, listen to it with a screen reader. And keep real abbreviations, such as NASA or HTML, in capitals in the source, because there the capitals are part of the text.

4. Text in images

A heading drawn into a picture looks exactly as designed, and that is its only advantage. WCAG 1.4.5, Images of Text, at level AA, asks that "if the technologies being used can achieve the visual presentation, text is used to convey information" rather than an image of text, with exceptions for logotypes and for text whose particular presentation is essential. Web fonts removed the old reason for images of text, a typeface the reader did not have, which A short history of type recalls.

An image of text ignores every reader setting in section 2, blurs when zoomed, cannot be selected, searched or translated, and must be remade for every language; Images for people works through an example. A logo can be an image with good alt text; a styled heading should be text; a labeled diagram can be inline SVG with real <text>.

5. Low vision, dyslexia and choice

Readers with low vision zoom, magnify, use high contrast or listen. What helps across that range is a generous x-height, open counters, letters that are hard to confuse, and the freedom to change all of it. Choosing and pairing tells the story of Atkinson Hyperlegible, designed for the Braille Institute around exactly those letter differences.

Dyslexia is a difficulty with reading and spelling, rooted mainly in how the brain processes the sounds of language, and it affects readers in very different ways. Several typefaces are sold as dyslexia fonts, with weighted bottoms or shapes that make b and d less alike. The research on them:

  • Rello and Baeza-Yates, 2013. In "Good fonts for dyslexia", at the ACM ASSETS conference, 48 readers with dyslexia read texts in twelve fonts while an eye tracker recorded their reading. Font mattered: sans serif, monospaced and roman (upright) styles did better than serif and italic ones. OpenDyslexic, one of the fonts made for dyslexia, did not improve reading time or shorten fixations.
  • Wery and Diliberto, 2017. "The effect of a specialized dyslexia font, OpenDyslexic, on reading rate and accuracy", in Annals of Dyslexia, compared OpenDyslexic with Arial and Times New Roman for twelve students in grades 3 to 6 identified with dyslexia. It found no improvement in reading rate or accuracy, for any student or for the group, and no student preferred the font.
  • Kuster and colleagues, 2018. "Dyslexie font does not benefit reading in children with or without dyslexia", also in Annals of Dyslexia, tested the Dyslexie font with 170 Dutch children with dyslexia in one experiment and 102 with and 45 without dyslexia in another. Dyslexie did not improve speed or accuracy; most children preferred Arial, and preference did not predict performance. The authors concluded the font neither helps nor hinders.

Spacing has better evidence than letter shapes. In a 2012 study in PNAS, Zorzi and colleagues found that extra-large letter spacing helped 74 Italian and French children with dyslexia read faster and more accurately, while a comparison group of younger children matched for reading level did not gain; the authors link it to crowding, the interference between nearby letters. Dyslexie itself is set with wide spacing, and a 2016 study by Marinus and colleagues, in the journal Dyslexia, tested exactly that with 39 struggling young readers of English: they read about 7 percent more words per minute in Dyslexie than in Arial, but when Arial was given the same spacing, the difference was no longer significant. The benefit came from the spacing, not the letter shapes. These are short-term results, not a prescription, and they are consistent with WCAG's text spacing criterion: let readers add space.

No typeface has been shown to fix reading for people with dyslexia, and some readers still like a particular face, which is a real reason for them to choose it. So the best accommodation is not a font you pick for the reader but a page that lets the reader pick: plain text faces, no long italic or capitalized passages, generous line spacing, unjustified text, and layouts that survive the reader's font, size and spacing.

6. Cognitive load and motion

Every typographic distinction asks the reader to decode it, and readers who are tired, reading in a second language or living with a cognitive disability have less attention to spend. The advice of Choosing and pairing, few typefaces and a clear hierarchy, is an accessibility practice as much as an aesthetic one.

Motion is the extreme case. Variable fonts make it easy to animate weight or width, and Variable fonts shows how, with the cautions: animating an axis costs layout on every frame, widening text pushes its neighbors, and moving text distracts and can cause real discomfort for people with vestibular disorders. Honor prefers-reduced-motion by removing such animation or making it instant.

7. The world's scripts

Most of the world does not read Latin letters. Each column of the table is a habit a Latin-trained designer takes for granted.

What a few scripts have, and lack, compared with Latin
Script (languages)DirectionCaseItalic traditionLetters joinSpaces between words
Latin, Greek, Cyrillicleft to rightyesyesno (except in scripts and handwriting styles)yes
Arabic (Arabic, Persian, Urdu)right to leftnonoyes: most letters change shape by positionyes
Hebrewright to leftno (five letters have final forms)nonoyes
Devanagari (Hindi, Marathi, Nepali)left to rightnonoalong a headline; consonants combine into conjunctsyes
Thaileft to rightnonono; vowels and tone marks stack above and belowno; spaces separate phrases
Chinese, Japaneseleft to right, or verticalnononono
Korean (Hangul)left to right, or verticalnonoletters group into syllable blocksyes

The next sections take the columns in turn. The sampler applies Latin habits to eight scripts at once.

Try the scripts

8. Scripts without case or italics

Case

Only a few scripts have capital and small letters: Latin, Greek, Cyrillic, Armenian and a handful of others. text-transform: uppercase on Arabic, Hebrew, Devanagari, Thai, Chinese or Japanese does nothing, because there is nothing to map to. Labels that rely on capitals need another signal in those languages: weight, size, color or space.

Even where case exists, it is language-sensitive. CSS Text requires that when the content language is known, "any appropriate language-specific rules must be applied." Turkish distinguishes a dotted and a dotless i, so in lang="tr" Chrome uppercases istanbul to İSTANBUL with a dotted capital; in English it gives ISTANBUL. Greek capitals are usually written without accents, and Chrome follows that with lang="el": Ελληνικά becomes ΕΛΛΗΝΙΚΑ, while without the language it keeps an accent on the capital, ΕΛΛΗΝΙΚΆ. Same text, same CSS, different and correct results, when the HTML declares its language.

Italics and emphasis

Italic is a Latin invention, the subject of a whole part of A short history of type. Chinese, Japanese and Korean have no italic tradition; neither do Arabic, Hebrew or Devanagari, although slanted styles exist in some of their fonts. When CSS asks for font-style: italic and the font has none, the browser slants the upright glyphs. In these scripts that is a style with no meaning, and in Chinese and Japanese a distortion of carefully balanced characters. The default style sheet italicizes em, cite, i and dfn, so this happens unless you say otherwise.

Emphasis in these languages uses other means. Chinese and Japanese traditionally mark emphasis with small dots or circles beside each character, and CSS draws them with text-emphasis, unprefixed in Chrome since version 99 in 2022 and long supported in Firefox and Safari. Arabic, Hebrew and Devanagari commonly use bold or a contrasting weight. :lang() applies each convention to the right text (one language per :lang(): the list form, :lang(zh, ja), is in Firefox and Safari but not yet in Chrome), and font-synthesis-style: none, supported in Chrome 97, Firefox 111 and Safari 16.4, stops the browser from faking a slant anywhere it would:

@layer base { /* :lang() with a list is not yet in Chrome, so each language is its own :lang(). */ :is(:lang(zh), :lang(ja), :lang(ko), :lang(ar), :lang(fa), :lang(he), :lang(hi)) { font-synthesis-style: none; } :is(em, i, cite, dfn):is(:lang(zh), :lang(ja)) { font-style: normal; text-emphasis: filled dot; } :is(em, i, cite, dfn):is(:lang(ar), :lang(fa), :lang(he), :lang(hi)) { font-style: normal; font-weight: bolder; } }

A slant axis designed for Latin says nothing to these readers either (Variable fonts). And this is why em means emphasis, not italics: the meaning is in the element, and each language draws it its own way.

9. Joined scripts and letter spacing

In Arabic, letters connect, and most of them change shape depending on whether they stand alone or begin, continue or end a connected group. The text stores one character; the font holds several glyphs for it; the browser's shaping engine picks one for each position. A short history of type shows the four forms of ʿayn. The letter heh changes even more:

One Arabic character, four glyphs: the joining forms of heh in Noto Sans ArabicFour panels, drawn from the outlines in this section's Noto Sans Arabic at weight 400, each showing the glyph the font uses for the character U+0647, ARABIC LETTER HEH, in one position. Isolated, glyph uni0647: a loop with an inner curl. Initial, glyph uni0647.init: a similar knotted shape with a stroke leaving on the left to join the next letter. Medial, glyph uni0647.medi: a loop above and below the baseline, joined on both sides. Final, glyph uni0647.fina: a small loop with a stroke entering from the right. Dashed lines mark the side or sides where each form joins its neighbor; Arabic is read right to left, so an initial form joins on its left.One character, U+0647 ARABIC LETTER HEH; four glyphs, chosen by positionisolateduni0647initialuni0647.initmedialuni0647.medifinaluni0647.finaDashed lines: the side where the glyph joins its neighbor. Arabic is read right to left, so an initial form joins on its left.
The character never changes; the shaping engine picks the glyph from its neighbors. Spacing inserted between glyphs would cut the joins.

Now apply letter-spacing. Spacing added after each glyph opens a gap in every join, and a word becomes a row of broken pieces. Nobody does this on purpose; a heading style with letter-spacing: 0.05em does it to every language the heading is translated into.

The specification anticipates this. CSS Text defines a short list of cursive scripts, Arabic, Hanifi Rohingya, Mandaic, Mongolian, N'Ko, Phags Pa and Syriac, and says that a browser "may apply letter spacing to cursive scripts" only "if it is able"; otherwise, if it "cannot expand text from a cursive script without breaking its cursive connections, it must not apply spacing" between that script's letters at all. Browsers disagree. Tested in Chrome 156, Arabic with letter-spacing: 0.3em keeps every letter joined, and only the gaps between words grow. Safari (WebKit 26.6) applies the spacing and draws each letter in its isolated form with a gap after it, so the words fall apart into separate letters. The specification's note anticipates this: authors "should avoid applying letter-spacing to cursive scripts unless they are prepared to accept non-interoperable results."

Devanagari is a different case. The same appendix says that Indic scripts with baseline connectors, "such as Devanagari and Gujarati," are not cursive scripts and "do admit such gaps." Chrome therefore spaces Devanagari, and the sampler shows why that is still a poor idea: each letter carries a piece of the headline, the stroke along the top that ties a word together, and spacing breaks it into segments. Whether to space Devanagari is a decision for someone who reads it, not a side effect of a Latin heading style.

The fix is to track only what you mean to track, and to reset it by language:

@layer components { h2[data-eyebrow] { text-transform: uppercase; letter-spacing: 0.08em; &:is(:lang(ar), :lang(fa), :lang(ur), :lang(hi), :lang(mr), :lang(ne), :lang(th), :lang(zh), :lang(ja), :lang(ko)) { letter-spacing: normal; } } }

Arabic lengthens a line its own way, by stretching the joining strokes (the kashida), which belongs to the font and justification, not to letter-spacing. This is why WCAG 1.4.12 has its exception, and why Size, spacing and measure said never to track a joined script.

10. Line height for tall marks

A line height that suits English may be too tight for scripts with marks far above and below the letters: Devanagari's vowel signs, Thai's stacked vowel and tone marks, Vietnamese double diacritics as in Tiếng Việt, Arabic's vowel marks. When lines are close, the marks collide with neighboring lines, or are cut off by a box that hides its overflow.

Devanagari marks against a line box of line-height 1 and of line-height 1.5The Hindi words हिंदी कृष्ण, drawn from the outlines of this section's Noto Sans Devanagari at weight 400, on a dotted baseline. A plain rectangle shows the line box for line-height 1.5; two red dashed lines show the much smaller line box for line-height 1. The vowel sign of हि and the long ī of दी curl up above the headline and pass above the upper dashed line; the vowel sign under कृ reaches the lower dashed line. Notes say the marks reach 0.896 em above the baseline and 0.268 em below, while a 1em line box reaches only 0.744 em above and 0.256 em below, so the marks cross into the next line. All of the ink fits inside the line-height 1.5 box.line-height: 1.5line-height: 1baselineline-height: 1line-height: 1.5The marks reach 0.896 em above the baseline and 0.268 em below it (font units from Noto Sans Devanagari).A 1em line box reaches only 0.744 em above and 0.256 em below, so the marks cross into the next line.
At line-height: 1, the vowel signs above the headline reach into the line above; at 1.5 they fit. Measured from the font's outlines.

The numbers come from the font. The browser takes the difference between the line height and the font's ascent plus descent, the leading, and adds half of it above and half below the glyphs: half-leading. Noto Sans Devanagari's ascent and descent add up to 1.304 em, so at line-height: 1 the leading is −0.304 em and each side loses 0.152, leaving 0.744 em above the baseline and 0.256 below; its vowel signs reach 0.896. The same fonts also show why line-height: normal is not one value: it is whatever the font's metrics say, and in this section's files it is about 1.21 for Inter, 1.30 for Noto Sans Devanagari and 2.11 for Noto Sans Arabic, whose metrics leave room for its tallest stacked marks. A page that mixes them with normal gets uneven lines; a page that sets 1.2 everywhere gets collisions.

The remedy: a unitless line height per script, set with :lang(), boxes that grow instead of clipping, and a test with real text from a reader of the language.

@layer base { body { line-height: 1.5; } :is(:lang(hi), :lang(mr), :lang(ne), :lang(th), :lang(ar), :lang(fa), :lang(ur)) { line-height: 1.75; } }

11. Chinese, Japanese and Korean fonts

Size and subsetting

A Latin font covers a few hundred characters; a CJK font tens of thousands, up to the 65,535-glyph limit of OpenType (A short history of type). The variable Noto Sans CJK file for Simplified Chinese alone is about 15 MB (Variable fonts). This section's Chinese and Japanese samples are 6,092 and 8,352 bytes, because they hold only the 20 and 19 characters the pages use. Such extreme subsetting works only when the text is known in advance.

Otherwise, split the font into many files by unicode-range so the browser fetches only those the page needs (Web fonts and performance). Google Fonts described its strategy when it launched Japanese support in September 2018: with character frequency data from millions of Japanese pages, it found that dividing the most common 3,000 characters into 20 slices worked best, with clients downloading about 80 percent fewer bytes than the whole font. Self-hosted CJK fonts can be split the same way. Many sites use the reader's system fonts for CJK instead, a reasonable choice: every major operating system ships good ones.

One code point, different glyphs: Han unification

Unicode gives a Chinese character used in Chinese, Japanese and Korean one code point, even when the conventions for drawing it differ by region. Fonts in CSS showed the consequence with system fonts: Chrome chose a Japanese or a Chinese font for the same characters depending only on lang. The diagram shows what differs, drawn from this section's own two sample fonts:

Three unified Han characters drawn by Noto Sans SC and by Noto Sans JPThree pairs of glyphs, drawn from the outlines of this section's Noto Sans SC and Noto Sans JP samples at weight 400, each in its em square. U+9AA8, bone: in the Simplified Chinese form the small stroke inside the top enclosure sits on the left; in the Japanese form it sits on the right. U+76F4, straight: the Chinese form closes the bottom with a separate horizontal stroke; the Japanese form draws the left side and the bottom as one L-shaped stroke. U+89D2, horn or angle: in the Chinese form the central vertical stroke runs down through the bottom of the character; in the Japanese form it stops at the lowest horizontal stroke. Each pair is labeled with lang="zh-Hans" and Noto Sans SC on the left and lang="ja" and Noto Sans JP on the right.U+9AA8bonelang="zh-Hans"Noto Sans SClang="ja"Noto Sans JPU+76F4straightlang="zh-Hans"Noto Sans SClang="ja"Noto Sans JPU+89D2horn, anglelang="zh-Hans"Noto Sans SClang="ja"Noto Sans JP
The same three code points in the HTML; the font chosen for the language draws the regional form. Readers notice the wrong one.

The differences are small, but a reader sees the wrong form as foreign or wrong. Three practices keep the right one:

  • Declare the language, precisely. lang="ja", lang="ko", lang="zh-Hans" for Simplified Chinese and lang="zh-Hant" for Traditional, or a region such as zh-TW or zh-HK where the regional conventions differ.
  • Give each language its font with :lang(), as the code sample below does and the sampler's last panel shows. Where a single Pan-CJK font holds every regional form, as the full Noto Sans CJK and Source Han Sans fonts do, the browser selects the regional glyphs through the font's locl (localized forms) feature, which also depends on the declared language.
  • Order the stacks by language. A Japanese font early in a stack used for Chinese text draws every character in Japanese forms, quietly. A stack with a font for each language, each behind its own :lang(), avoids it.
@layer base { /* :lang(zh-Hans) does not match lang="zh-CN", so list both. */ :is(:lang(zh-Hans), :lang(zh-CN)) { font-family: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif; } :is(:lang(zh-Hant), :lang(zh-TW)) { font-family: "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", sans-serif; } :lang(ja) { font-family: "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif; } :lang(ko) { font-family: "Noto Sans KR", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif; } }

Vertical writing, in outline

Chinese and Japanese are also written in vertical columns, read top to bottom and right to left; Mongolian's columns run left to right. CSS supports this properly, unlike rotated Latin, which Choosing and pairing warned against. Three properties do most of the work:

  • writing-mode: vertical-rl makes lines run top to bottom and stack from right to left. Logical properties follow it, which is one more reason to use them; CSS for people covers that side.
  • text-orientation decides what happens to Latin letters and digits inside vertical text: mixed, the default, turns them sideways; upright stacks them one above another.
  • text-combine-upright: all sets a short run, such as a two-digit number, horizontally within the width of one character, a practice called tate-chū-yoko in Japanese.
@layer components { blockquote:lang(ja) { writing-mode: vertical-rl; text-orientation: mixed; max-block-size: 30rem; & span[data-tcy] { text-combine-upright: all; } } }

writing-mode and text-orientation are supported in every current browser, and text-combine-upright since Safari 15.4 in 2022. Vertical typesetting has many more rules; the W3C's article on styling vertical text and its Requirements for Japanese Text Layout are the place to start.

12. Direction and bidirectional text

Arabic, Hebrew, Persian and Urdu run right to left, with numbers, names and quoted English running left to right inside them. The Unicode Bidirectional Algorithm orders such mixed text, and the HTML sets the base direction, because direction is a property of the content: <html lang="ar" dir="rtl"> for the page, and dir on any passage in another direction. The document lesson introduces lang and dir, and the text lesson covers bdi and bdo.

Text from users, such as a name, may be in either direction, and inserted next to other text it can reorder its neighbors: an Arabic name followed by ": 3 comments" can come out with the number on the wrong side. <bdi>, or dir="auto", isolates such text and takes its direction from its first strong character. For layout, logical properties mirror with the direction (CSS for people).

<li><bdi>مريم</bdi>: 3 comments</li> <li><bdi>Jordan</bdi>: 5 comments</li> <input name="name" dir="auto">

Paired characters such as parentheses and brackets are mirrored automatically in right-to-left text, so the opening parenthesis of an Arabic aside appears on the right, facing the right way, with nothing for the author to do.

13. Fallback across scripts, numbers and punctuation

No font covers every script. When a font lacks a character, the browser tries the next in the stack, then any font on the system; that is why the sampler's Greek and Cyrillic appear although Inter lacks them. When nothing on the device has the character, the reader sees an empty box, tofu.

Help it: put a font for each script the site uses in the stack, give script fonts a unicode-range so they load only when needed, and end every stack in a generic family. The sampler shows a quieter detail: this section's Arabic and Hebrew files contain no ASCII full stop, so the final period comes from the next font, which is harmless if the next font is good.

Google's Noto project is the safety net. Its name comes from "no more tofu," and its families cover more than 100 scripts, with matching proportions and weights across them, under the SIL Open Font License; A short history of type tells its story. A site serving a script the reader's device may lack can self-host the Noto family for it.

Numbers and punctuation across languages

Digits and punctuation are not universal either. The details showed how quotation marks change between English, German, French and Japanese. A few more examples:

  • Digits. Arabic text in Egypt and the Gulf commonly uses Arabic-Indic digits, ٠١٢٣, and Hindi text may use Devanagari digits, ०१२३. Numbers run left to right even inside right-to-left text. Intl.NumberFormat chooses the digits and separators for a locale, so a script never has to: new Intl.NumberFormat('ar-EG').format(1234.5) gives ١٬٢٣٤٫٥.
  • Separators. 1,234.5 in English is 1.234,5 in German and 1 234,5 in French, with a narrow no-break space. Format numbers for the reader's language, and set them with tabular-nums in tables, as The details explained.
  • Sentence punctuation. Hindi ends a sentence with the danda, ।; Chinese and Japanese use an ideographic full stop, 。, and full-width commas; Arabic writes its comma and question mark reversed, ، and ؟; Spanish opens questions with ¿.

Problems come from text assembled by hand: a price built by concatenation, a date format typed into a template, a label typed in capitals. Leave the form of text to the platform.

14. What the bridge now carries

Why type matters promised an appreciation of type and its history, the vocabulary to discuss it, the CSS to use it, an eye for misuse, and the understanding to bridge design and code. It drew the developer as that bridge, the one person who sees every step between a designer's intent and a reader's screen. Ten pages later, the bridge carries a good deal:

  • Why and where from. Type sets tone and trust before a word is read, and most of its vocabulary is older than the web, shaped by metal, film, PostScript and screens.
  • The words for it. Typeface and font, glyph and character, x-height, counters and the em square, so a talk with a designer is a talk between peers.
  • The choices. Legibility first, few typefaces, a clear hierarchy.
  • The CSS and its cost. Stacks that end in generics, real weights and styles, relative sizes, a measure in characters; fonts self-hosted, subset, displayed with a fallback that matches, and kept to a budget.
  • The details and the people. Figures, small caps and punctuation done properly; contrast that accounts for weight; pages that survive the reader's settings; scripts that get their own case, emphasis, spacing, line height, fonts and direction.

Add a few questions to the handoff list: Does the design work in forced colors and at 200 percent? Which languages will this text be translated into, and does the type system rely on capitals, italics or tracking they lack? Is the line height right for every script on the page? Asking early costs a conversation; discovering it after launch costs a redesign.

What to read next

Keep the habit this section has tried to build: look closely at the type you meet, ask why it was chosen, and check how it behaves for a reader who is not you.

15. Summary

Type for people, in brief
ConceptKey points
ContrastWCAG 1.4.3: 4.5:1, or 3:1 for large text. The ratio ignores weight; keep small text sturdy. APCA is not a standard.
The reader's settingsSize, zoom, minimum size, own fonts, reader modes: relative units, generics and growing boxes survive them.
Forced colorsPalette replaced, shadows removed, fonts kept. Build hierarchy from size, weight and space.
All capsShort labels only. Type normal case and use text-transform.
Images of textWCAG 1.4.5: use text, except logos and essential presentation.
DyslexiaOpenDyslexic and Dyslexie showed no benefit; spacing did better. Let readers choose.
MotionFew typefaces lower the load; honor prefers-reduced-motion.
Case and italicsMost scripts have neither. text-transform follows lang. Use font-synthesis-style: none, and text-emphasis or weight per :lang().
Joined scriptsGlyphs chosen by position. Chrome keeps Arabic joined, Safari splits it into letters; Devanagari's headline breaks. Reset tracking by language.
Tall marksNeed more line height; normal differs by font. Set it per :lang().
CJK fontsMegabytes: subset known text, slice by unicode-range, or use system fonts.
Han unificationRegional glyphs for one code point: declare the language and give each its font.
Vertical textwriting-mode: vertical-rl, text-orientation, text-combine-upright; logical properties follow.
Directiondir in HTML; bdi for user text; logical properties.
FallbackPer character, then the system; tofu when nothing has it. Noto is the safety net.
Numbers and punctuationVary by language; Intl formats them.