Activate OpenType typographic features in CSS — ligatures, tabular figures, oldstyle numerals, small caps, and contextual alternates — to produce professional-grade web typography.
Most web fonts ship with OpenType features — ligatures, alternate glyphs, tabular figures, small caps — that are disabled by default. Enabling the right features at the right places is what separates professional web typography from default browser rendering. This lesson covers the CSS properties that control these features and when each one makes a real difference to visual quality.
OpenType is the font format standard that allows type designers to include alternate glyphs, spacing rules, and typographic substitutions beyond the base character set. CSS exposes these through two mechanisms: the high-level font-variant-* properties (preferred — readable, keyword-based) and the low-level font-feature-settings (escape hatch — four-letter tags for features without high-level properties). The four-letter tag system: liga (standard ligatures), calt (contextual alternates), kern (kerning), tnum (tabular numbers), onum (oldstyle figures), smcp (small caps), frac (diagonal fractions), sups (superscript), subs (subscript), ordn (ordinals), zero (slashed zero), ss01–ss20 (stylistic sets). Not every font implements every feature — check with the browser font inspector or a tool like Wakamai Fondue (wakamaifondue.com) to see which features a specific font includes.
1/* High-level properties: PREFER these */
2.text {
3 font-variant-ligatures: common-ligatures; /* liga */
4 font-variant-numeric: tabular-nums; /* tnum */
5 font-variant-caps: small-caps; /* smcp */
6}
7
8/* Low-level escape hatch for features with no high-level property */
9.text {
10 font-feature-settings: 'zero' 1; /* Slashed zero */
11}
12
13/* Never disable kern: it is on by default and improves spacing */
14/* Never do: font-feature-settings: 'kern' 0; */Ligatures replace specific character sequences with a single combined glyph that avoids awkward collisions. The classic example is 'fi' — in many serif fonts the top of 'f' collides with the dot of 'i'; the fi ligature draws them as a single connected glyph. Standard ligatures (liga) cover the most common collision pairs: fi, fl, ff, ffi, ffl. These are enabled by default in modern browsers for fonts that include them. Discretionary ligatures (dlig) are stylistic and decorative — st, ct, Th, sp — appropriate for headings and logotypes, not body text. Contextual alternates (calt) are substitutions that depend on surrounding characters — a connecting script font adjusting stroke shapes based on what comes before and after. Enable calt for script and calligraphic typefaces, disable it for plain sans-serif where it adds no value.
1/* Body text: standard ligatures only (default in most browsers) */
2body {
3 font-variant-ligatures: common-ligatures;
4 /* font-feature-settings: 'liga' 1, 'calt' 1; — equivalent */
5}
6
7/* Display headings: add discretionary for decorative flair */
8.editorial-heading {
9 font-variant-ligatures: common-ligatures discretionary-ligatures;
10 font-family: 'Fraunces', serif;
11}
12
13/* Code blocks: disable all ligatures (unless it's a code font like FiraCode) */
14code, pre {
15 font-variant-ligatures: no-common-ligatures;
16 /* Prevents = = becoming a ligature arrow in generic sans fonts */
17}
18
19/* FiraCode: enable ligatures for programming arrows */
20pre.firacode {
21 font-family: 'Fira Code', monospace;
22 font-variant-ligatures: common-ligatures;
23 /* Now => renders as arrow glyph */
24}OpenType fonts can include four combinations of numeral styles. Lining figures (default in most fonts) are the same height as capitals and sit on the baseline — good for headings and standalone numbers. Oldstyle figures have varying heights and descenders (3, 4, 5, 7, 9 drop below baseline) — blend naturally into body text and editorial content. Proportional figures have variable widths (1 is narrower than 8) — natural-looking in running text. Tabular figures have equal widths — every digit occupies exactly the same horizontal space, making columns of numbers align perfectly. For data tables, financial dashboards, and any column of numbers: always use tabular lining figures. For body text with embedded numbers: oldstyle proportional figures read more naturally. Never mix proportional and tabular figures in the same column.
1/* Data tables: tabular lining figures for column alignment */
2table, .data-display {
3 font-variant-numeric: tabular-nums lining-nums;
4 /* font-feature-settings: 'tnum' 1, 'lnum' 1; */
5 font-feature-settings: 'zero' 1; /* Slashed zero: 0 vs O */
6}
7
8/* Editorial body text: oldstyle proportional */
9article, .prose {
10 font-variant-numeric: oldstyle-nums proportional-nums;
11 /* Numbers blend into text flow */
12}
13
14/* Price display: lining tabular + currency */
15.price {
16 font-variant-numeric: tabular-nums lining-nums;
17 font-feature-settings: 'tnum' 1, 'lnum' 1;
18 font-size: 1.5rem;
19 font-weight: 700;
20}
21
22/* Fractions: activate diagonal fraction rendering */
23.fraction {
24 font-variant-numeric: diagonal-fractions;
25 /* Renders 1/4 as proper fraction glyph */
26}Small caps are uppercase letterforms drawn at roughly the x-height of the typeface — slightly larger than lowercase but much smaller than full capitals. They are used for acronyms (CSS, HTML, NASA) in running text so they do not shout, for subheadings that should feel authoritative without the weight of all-caps, and for certain editorial conventions (author names in citations). The CSS property is font-variant-caps: small-caps. Critical: this only works well if the font includes actual drawn small-cap glyphs. If not, the browser synthesises small caps by scaling down regular capitals — these synthetic small caps are noticeably lighter and thinner than the surrounding text, looking like a mistake. Check font support before using. Fonts with real small caps: Lora, EB Garamond, Cormorant, Source Serif.
1/* Acronyms in body text: small caps prevent visual shouting */
2abbr[title] {
3 font-variant-caps: all-small-caps;
4 /* 'all-small-caps' covers both upper and lower case */
5 letter-spacing: 0.05em; /* Small caps typically need slight tracking */
6}
7
8/* Subheading with small caps treatment */
9.section-heading {
10 font-family: 'Lora', Georgia, serif; /* Font with real small caps */
11 font-variant-caps: small-caps;
12 font-weight: 400; /* Real small caps at 400 weight match bold body */
13 font-size: 0.875rem;
14 letter-spacing: 0.06em;
15 text-transform: lowercase; /* Input must be lowercase for smcp */
16}
17
18/* Check: does your font have real small caps? */
19/* If small caps look thinner/lighter than surrounding text: synthetic */
20/* Fix: choose a font with OpenType smcp/c2sc features */The font-variant shorthand can set multiple font-variant-* properties at once. It accepts keywords from font-variant-ligatures, font-variant-caps, font-variant-numeric, font-variant-alternates, and font-variant-east-asian. Beyond the standard features, type designers often include stylistic sets (ss01–ss20) — alternate glyph designs for entire character categories. Inter's ss01–ss04 swap specific glyphs: ss01 gives an alternate 'a' (single-storey vs double-storey), ss03 gives an alternate 'g'. Recursive includes an entirely separate casual cursive style via stylistic sets. Use font-feature-settings for these since font-variant-alternates does not yet have stable cross-browser support for numeric stylistic sets.
1/* font-variant shorthand: set multiple features at once */
2.editorial {
3 font-variant:
4 common-ligatures
5 discretionary-ligatures
6 oldstyle-nums
7 proportional-nums
8 small-caps;
9}
10
11/* Inter: single-storey 'a' via stylistic set 01 */
12.code-like-text {
13 font-family: 'Inter', sans-serif;
14 font-feature-settings:
15 'ss01' 1, /* Single-storey a */
16 'ss03' 1, /* Single-storey g */
17 'zero' 1, /* Slashed zero */
18 'tnum' 1; /* Tabular numbers */
19}
20
21/* Combining high-level and low-level — note: they do NOT merge */
22/* font-feature-settings overrides ALL features set by font-variant */
23/* Use one or the other, not both on the same element */
24.mixed {
25 font-variant-numeric: tabular-nums; /* High-level */
26 /* WRONG: this cancels font-variant-numeric settings */
27 /* font-feature-settings: 'zero' 1; */
28 /* RIGHT: include tnum explicitly in font-feature-settings */
29 font-feature-settings: 'tnum' 1, 'zero' 1;
30}A complete OpenType feature configuration for editorial body text, data tables, and headings.
Next lesson covers dark mode typography — how antialiasing changes on dark backgrounds, contrast recalculation, and weight/opacity adjustments for readable dark-mode type.
Your progress is saved in this browser, on this device only.