# Font

## What it is

A font, called a webfont when a site serves it and a font face in the CSS
specifications, is the file holding the drawn letters of one design in one
weight and style, along with the tables that tell software which drawing stands
for which character and how to space them.

Typographers keep the design and its files apart. Ubuntu is a typeface; Ubuntu
Light and Ubuntu Bold Italic are fonts of it. CSS keeps the same split. A font
family groups faces under one name, and each `@font-face` rule describes a
single face: the family it joins, where to fetch it, the weight and style it
covers, the characters it can draw and what to show while it loads.

```css
@font-face {
    font-family: "Reading Serif";
    src:
        local("Reading Serif Regular"),
        local("ReadingSerif-Regular"),
        url("/fonts/reading-serif-400.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

body {
    font-family: "Reading Serif", Georgia, serif;
}
```

The `font-family` property on an element is a list read from left to right. The
specification chooses a family and a face for each character in a run of text,
taking the first face that matches the requested weight and style and contains a
glyph for that character. One line can therefore be set in two fonts when the
first lacks a letter, and the generic name at the end of the list, `serif`,
`sans-serif`, `monospace` or `system-ui`, means some installed font always takes
the remainder.

On the web the file is almost always WOFF2: the same OpenType or TrueType
outlines and tables, repackaged for transfer and compressed with Brotli. Inside,
the `cmap` table maps each character code to a glyph, and other tables carry the
numbers a layout engine needs, such as ascent, descent, line gap and where an
underline sits and how thick it is. Those numbers decide line height and spacing
as much as the letterforms decide the look.

## Why it matters

Every weight and style is a separate download. A design using regular, italic
and bold puts three files between a reader and the text set in them, and each
one adds bytes to the first visit. The `unicode-range` descriptor lets a family
be split into pieces by script, and the specification forbids a browser from
downloading a piece for characters outside its range, so a page fetches only the
pieces its text uses.

Text in a webfont cannot be drawn before the file arrives, so the browser needs
a policy for the wait. The `font-display` descriptor sets it. With `block`, the
specification recommends a block period of 3s, during which the text is laid out
but drawn invisibly. With `swap`, the block period is 100 ms or less, the
fallback font appears almost at once, and the webfont replaces it whenever it
lands. With `fallback`, the swap window is short, with 3s recommended, after
which the page keeps the fallback. With `auto`, the browser decides, and many
treat it like `block`.

Where the file lives matters too. Font requests use CORS, so a font on another
origin loads only when that server allows it, and every request to another
company's server tells it the reader's address and which site they are on.
Serving fonts from the site's own host removes both the extra connection and the
observer.

A font is software under a licence. The SIL Open Font License allows use, study,
modification and redistribution as long as the fonts are not sold by themselves,
and bars a modified version from using a reserved font name without permission.
A font bought for print may carry different terms for the web, so read the
licence before the file goes on a server.

Coverage is the last concern. A font draws only the characters its `cmap` lists.
Most of the Cyrillic alphabet sits in the run U+0400–045F, but the Ukrainian
letter ґ lives at U+0490–0491, so a subset or a range written for Russian text
leaves it to whatever fallback comes next.

## What it looks like in practice

The site's [credits page](/credits/) lists what reaches a reader's browser as
five things, and three of them are webfonts. Ubuntu, by Dalton Maag for
Canonical, is under the Ubuntu Font Licence. Ubuntu Mono, the same family's
fixed-width cut, sets the code. Shantell Sans, based on the artist Shantell
Martin's felt-marker handwriting, is under the SIL Open Font License and writes
the margin notes. All three come from the site's own machine, and the page says
why: a webfont from someone else's CDN is a third party watching you read.
Ubuntu Mono and Shantell Sans load only on the pages that carry code or a margin
note.

Splitting one typeface by script is what
[the Safari underline note](/notes/safari-underline-font-subset/) is about. The
site serves Ubuntu in six pieces, three weights of Latin and three of Cyrillic,
each declaring the characters it covers, so English and Danish pages fetch three
Latin files and no Cyrillic ones, and Ukrainian pages the reverse. All six
shared one family name, and in Safari a face with a non-Latin range degrades
skip-ink, the gap an underline leaves around a descender, for every character in
its family, Latin text included. Deleting the three Cyrillic declarations at
runtime took the measured gaps from 1.8/1.0/1.0px to 4.5/11.0/5.0px. The fix
gave the Cyrillic faces a family name of their own and put it in the stack after
Ubuntu: three renamed declarations and one stack entry, with Chrome untouched
and Ukrainian still set in Ubuntu at identical widths. The same note shows how
much a font's own numbers vary: Ubuntu declares an underline thickness of
0.056em for Light, 0.020em for Medium and 0.120em for Bold.

The landing page in front of the company's
[own git forge](/portfolio/deployed-the-companys-own-git-forge-121/) had been a
hand-maintained copy of the main site's design, and every difference found
between the two was an accident rather than a decision. Two of the five were
about fonts: a font declaration that collapsed two weights to one on any machine
with the family installed, and a weight used with no font shipped for it. None
of the five was visible in a screenshot. The copy was deleted, and the landing
page is now built by the main site's own templates.

## Where it goes wrong

- **Text stays invisible while the file loads.** Under `block`, and often under
  `auto`, a slow connection shows a laid-out page with no words in it for up to
  the block period. Use `swap` for body text and preload the one or two files
  the first screen needs. A font preload needs the `crossorigin` attribute even
  for a same-origin file, because the preload has to match the CORS request it
  stands in for.
- **The page jumps when the font arrives.** A fallback with different widths and
  line heights breaks lines in other places, and the swap reflows them. The
  specification advises fallbacks whose metrics closely match the webfont. Where
  none does, a `local()` face for the fallback with `ascent-override`,
  `descent-override` and `line-gap-override` brings its vertical metrics into
  line, so the swap changes letterforms rather than layout.
- **Bold and italic nobody drew.** When a family has no bold face, the browser
  may thicken the regular one, and it may slant the regular one when there is no
  italic. Strokes smear and roman letters lean. Ship every face the design uses,
  or set `font-synthesis` to `none` so a missing face shows up in review as
  upright, regular-weight text rather than a fake.
- **`local()` takes a face, not a family.** The name inside `local()` matches a
  single face by its full name or its PostScript name. Writing the family name
  there for every weight can match one installed face for all of them, so bold
  renders as regular on exactly the machines that have the family installed.
  Name each face, both ways, or leave `local()` out.
- **A missing glyph mixes typefaces.** A face that lacks a character hands it to
  the next family, so one word can show two designs. A subset cut too tightly
  does the same for a whole script. Check coverage against the text the site
  actually publishes, in every language it publishes.

## Further reading

- [CSS Fonts Module Level 4](https://www.w3.org/TR/css-fonts-4/) — `@font-face`,
  the matching algorithm, `unicode-range`, `font-display` and the synthesis
  controls.
- [WOFF File Format 2.0](https://www.w3.org/TR/WOFF2/) — how OpenType and
  TrueType fonts are packaged and compressed for the web.
- [OpenType cmap table](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap)
  — the table that decides which characters a font can draw.
- [SIL Open Font License](https://openfontlicense.org/open-font-license-official-text/)
  — the licence itself, with its conditions on copies, modified versions and
  reserved names.
- [WebKit bug 255159](https://bugs.webkit.org/show_bug.cgi?id=255159) — the
  Safari report on underline skip-ink behind the site's note.

<https://engineer.company/glossary/font/>
