The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Declare a WOFF2 face@font-face { font-family: 'Atlas'; src: url('/fonts/atlas.woff2') format('woff2'); font-weight: 400; font-display: swap; }View examples
Provide a fallback stackbody { font-family: 'Atlas', ui-sans-serif, system-ui, sans-serif; }View examples
Declare a weight range@font-face { font-family: 'Atlas Variable'; src: url('/fonts/atlas-var.woff2') format('woff2'); font-weight: 100 900; }View examples
Select a variable weight.display { font-weight: 725; }View examples
Enable optical sizing.prose { font-optical-sizing: auto; }View examples
Set a custom variation axis.expressive { font-variation-settings: 'wght' 640, 'GRAD' 80; }View examples
Keep text available during loading@font-face { font-family: 'Atlas'; src: url('/fonts/atlas.woff2') format('woff2'); font-display: swap; }View examples
Subset by character range@font-face { font-family: 'Atlas'; src: url('/fonts/atlas-latin.woff2') format('woff2'); unicode-range: U+0000-00FF; }View examples
Observe font readinessawait document.fonts.load("700 2rem 'Atlas'", 'Heading');View examples
Scale fallback glyph metrics@font-face { font-family: 'Atlas Fallback'; src: local('Arial'); size-adjust: 102%; }View examples
Override fallback line metrics@font-face { font-family: 'Atlas Fallback'; src: local('Arial'); ascent-override: 91%; descent-override: 23%; line-gap-override: 0%; }View examples
Align changing numbers.data-table { font-variant-numeric: tabular-nums lining-nums; }View examples
Control discretionary ligatures.display { font-variant-ligatures: common-ligatures discretionary-ligatures; }View examples
Limit synthetic faces deliberately.brand-wordmark { font-synthesis: none; }View examples
Gate variable-font styling@supports (font-variation-settings: normal) { .expressive { font-variation-settings: 'GRAD' 80; }; }View examples

A font is part of the rendering pipeline, not a prerequisite for readable content. Declare only the faces and ranges a page uses, provide credible system fallbacks, map variable axes through high-level properties, and treat low-level features as progressive enhancements. Test slow, failed, and disabled font loading as carefully as the final branded state.

Step by step

Detailed examples

01

Declare each face precisely and retain a system fallback

An @font-face rule describes a face; it does not apply that face until a matching font-family declaration is used. Give every file accurate style and weight descriptors so matching does not select the wrong resource. Put WOFF2 first for web delivery and keep a generic family at the end of every application stack. Fonts are fetched with CORS semantics, so cross-origin hosting needs an appropriate Access-Control-Allow-Origin response. A missing or blocked font must leave visible, readable text rather than an empty interface.

Register normal and italic faces without hiding fallback text
@font-face {
  font-family: 'Atlas';
  src: url('/fonts/atlas-regular.woff2') format('woff2');
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: 'Atlas';
  src: url('/fonts/atlas-italic.woff2') format('woff2');
  font-style: italic;
  font-weight: 400;
  font-display: swap;
}

body {
  font-family: 'Atlas', ui-sans-serif, system-ui, sans-serif;
}
Back to quick reference ↑
02

Map variable axes through high-level font properties

A variable font can store a continuum of designs in one resource. Register the supported range in @font-face, then prefer font-weight, font-style, font-stretch (the widely implemented legacy alias for the standard font-width property), and font-optical-sizing because they participate in font matching and cascade predictably. Use font-variation-settings for custom four-character axes such as GRAD, or for targeted experiments. Direct settings can override values established by higher-level properties for the corresponding registered axes, so do not set wght twice without a clear reason.

Register ranges and use a custom grade axis progressively
@font-face {
  font-family: 'Atlas Variable';
  src: url('/fonts/atlas-variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

.display {
  font-family: 'Atlas Variable', system-ui, sans-serif;
  font-weight: 725;
  font-optical-sizing: auto;
}

@supports (font-variation-settings: normal) {
  .quiet-emphasis { font-variation-settings: 'GRAD' 80; }
}
Back to quick reference ↑
03

Budget font downloads and choose a display policy

font-display controls the block and swap periods, but exact timings are user-agent decisions. swap prioritizes immediate fallback text and eventual brand rendering; optional lets constrained browsers decide not to replace the fallback. Subset only with verified character coverage: unicode-range can avoid irrelevant downloads, but a missing glyph falls through to another face and careless subsets multiply requests. Preload only a critical, immediately used font and ensure its URL, type, and crossorigin mode match the @font-face request, or the browser may download it twice.

Split a family into non-overlapping character coverage
@font-face {
  font-family: 'Atlas';
  src: url('/fonts/atlas-latin.woff2') format('woff2');
  font-display: swap;
  unicode-range: U+0000-00FF;
}

@font-face {
  font-family: 'Atlas';
  src: url('/fonts/atlas-cyrillic.woff2') format('woff2');
  font-display: swap;
  unicode-range: U+0400-04FF;
}
Preload only a measured critical face
<link rel="preload" href="/fonts/atlas-latin.woff2"
      as="font" type="font/woff2" crossorigin>
Observe a non-critical face without gating page content
try {
  await document.fonts.load("700 2rem 'Atlas'", 'Heading');
  document.documentElement.classList.add('atlas-ready');
} catch {
  // The complete fallback stack remains active.
}
Back to quick reference ↑
04

Match fallback metrics to reduce reflow

Fallback and downloaded fonts often occupy different widths and line boxes, causing text to rewrap after a swap. A separate local fallback face can use size-adjust plus ascent-, descent-, and line-gap-override descriptors to approximate measured target metrics. These percentages are font-specific data, not universal recipes: derive them from the exact production font and chosen local fallback, then test multiple scripts and viewport widths. Keep an ordinary system and generic fallback after the adjusted face because the named local font may not exist.

Create a measured metric-compatible fallback face
@font-face {
  font-family: 'Atlas Fallback';
  src: local('Arial');
  size-adjust: 102%;
  ascent-override: 91%;
  descent-override: 23%;
  line-gap-override: 0%;
}

body {
  font-family: 'Atlas', 'Atlas Fallback', system-ui, sans-serif;
}
Back to quick reference ↑
05

Use semantic feature properties and correct language metadata

Prefer font-variant-* properties for common OpenType behavior because their intent is clearer than raw feature tags. Tabular figures stabilize columns; discretionary ligatures suit display text but may reduce clarity in dense interfaces. Features only work when the selected font supplies the needed tables. Set the HTML lang attribute so shaping, glyph selection, hyphenation, and assistive pronunciation can follow the actual language; font CSS cannot repair missing language semantics.

Choose features by content role
<p lang="en" class="display">Efficient typography</p>
<table class="data-table">
  <tr><th>Month</th><th>Revenue</th></tr>
  <tr><td>June</td><td>$12,408</td></tr>
</table>
Request high-level feature groups
.display {
  font-variant-ligatures: common-ligatures discretionary-ligatures;
}

.data-table {
  font-variant-numeric: tabular-nums lining-nums;
}
Back to quick reference ↑
06

Preserve readability across preferences and support levels

Typography must survive zoom, user styles, missing glyphs, and failed downloads. Use relative sizes and unitless line-height, allow text containers to grow, and never encode meaning solely in a special glyph or icon font. Synthetic bold or italic can preserve emphasis when a face is absent; disable synthesis only for a narrow design requirement after supplying every needed face. Isolate custom axes behind @supports, but keep the unenhanced rule complete. Test variable values for legibility rather than assuming every point in an axis range is appropriate for body copy.

Create a readable baseline before variable enhancement
.article {
  max-inline-size: 68ch;
  font-family: 'Atlas', system-ui, sans-serif;
  font-size: clamp(1rem, .96rem + .2vw, 1.125rem);
  line-height: 1.6;
  overflow-wrap: break-word;
}

.brand-wordmark { font-synthesis: none; }

@supports (font-variation-settings: normal) {
  .article-heading { font-variation-settings: 'GRAD' 60; }
}
Back to quick reference ↑

Local code tester

Tune a resilient typography specimen

Adjust the stack, weights, spacing, numeric features, and variable-axis enhancement while retaining a readable system fallback.

Runs in your browser
Preview

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. World Wide Web ConsortiumCSS Fonts Module Level 4w3.org
  2. World Wide Web ConsortiumCSS Font Loading Module Level 3w3.org
  3. World Wide Web ConsortiumWOFF File Format 2.0w3.org
  4. World Wide Web ConsortiumWeb Content Accessibility Guidelines (WCAG) 2.2w3.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback