Skip to main content

· Typography · 6 min read

OpenType Features in CSS: font-variant or font-feature-settings?

Turn on tabular figures, small caps, fractions, and stylistic sets in CSS. When to use the font-variant properties and when font-feature-settings is the only option.

Most professional fonts contain more than one way to draw some characters: ligatures for “fi” and “fl”, small capitals, figures that line up in tables, real fractions, alternate letter shapes. These are OpenType features, and CSS can switch them on and off.

There are two ways to do it: the font-variant-* properties, and the lower-level font-feature-settings. This guide explains which to use, with the CSS for the most useful features.

Features are switches inside the font

Each feature has a four-letter tag defined in the OpenType feature tag registry, such as liga for standard ligatures or tnum for tabular figures. A feature only works if the font includes it. Asking for small caps in a font without them does nothing, or, in one case described below, makes the browser imitate them.

Browsers turn some features on by default, including kerning (kern), standard ligatures (liga), and contextual alternates (calt). Most others, such as small caps, old-style figures, or fractions, stay off until you ask for them.

To see what a font offers, open it in the font viewer. Its “OpenType features” panel lists the features in the file, split into “On by default” and “Off by default”. Switching one on shows the change in the preview, and “CSS for these settings” gives you the CSS for your choices.

The rule of thumb: font-variant first

MDN’s font-feature-settings reference recommends the font-variant properties whenever possible and describes font-feature-settings as a low-level tool for special cases. There are three practical reasons.

They read like what they do. font-variant-numeric: tabular-nums is clearer than "tnum" 1.

They combine. Each font-variant-* property is separate, so a table can set numeric features while an abbreviation inside it sets capitals, and both apply. font-feature-settings is a single property: a later declaration replaces the whole list. A child rule that sets "smcp" silently turns off the "tnum" its parent set.

They can fall back. If a font has no small caps, font-variant-caps: small-caps lets the browser create imitation small capitals from scaled-down capitals. font-feature-settings: "smcp" does nothing in that font. MDN says specifically that font-feature-settings should not be used to enable small caps.

Which property controls which feature

CSSFeature tag(s)Effect
font-variant-ligatures: no-common-ligaturesliga, cligTurns off standard ligatures such as fi, fl
font-variant-ligatures: discretionary-ligaturesdligTurns on decorative ligatures
font-variant-ligatures: no-contextualcaltTurns off contextual alternates
font-variant-caps: small-capssmcpLowercase letters as small capitals
font-variant-caps: all-small-capssmcp, c2scCapitals become small capitals too
font-variant-numeric: oldstyle-numsonumFigures with ascenders and descenders
font-variant-numeric: lining-numslnumFigures the height of capitals
font-variant-numeric: tabular-numstnumEvery figure the same width
font-variant-numeric: proportional-numspnumFigures with their natural widths
font-variant-numeric: diagonal-fractionsfrac1/2 drawn as a real fraction
font-variant-numeric: slashed-zerozeroA zero with a slash, unlike the letter O
font-variant-numeric: ordinalordnRaised letters in 1st, 2nd
font-variant-position: super / subsups / subsSuperscript or subscript forms
font-kerning: nonekernTurns off kerning

MDN documents each property: font-variant-ligatures, font-variant-caps, font-variant-numeric, font-variant-position, and font-kerning.

Recipes

Numbers that line up in tables and prices. Proportional figures look good in text but make columns ragged. Tabular figures all have the same width:

table,
.price {
  font-variant-numeric: tabular-nums;
}

Figures that blend into body text. Old-style figures rise and fall like lowercase letters:

article p {
  font-variant-numeric: oldstyle-nums;
}

Abbreviations without shouting. Small capitals for acronyms, with a little extra spacing:

abbr {
  font-variant-caps: all-small-caps;
  letter-spacing: 0.05em;
}

Fractions in recipes or specifications. Apply it to the fraction only, so it does not affect dates such as 3/4/2026:

.fraction {
  font-variant-numeric: diagonal-fractions;
}

Code without ligatures. Some programming fonts join characters such as => or != into one symbol. If you prefer to see the actual characters:

code,
pre {
  font-variant-ligatures: none;
}

none turns off every kind of ligature and contextual alternate.

Two behaviors that surprise people

Letter-spacing turns off optional ligatures. The CSS Text specification says browsers should not apply optional ligatures when letter-spacing adds space between characters. That is usually what you want: spaced-out capitals would look odd with “fi” joined into one shape. But if a heading with letter-spacing loses a ligature you expected, this is why.

Settings inherit. Features set on a parent apply to everything inside it. To undo them for one element, reset the property:

article {
  font-variant-numeric: oldstyle-nums;
}

/* Back to the font's default figures inside tables */
article table {
  font-variant-numeric: normal;
}

font-feature-settings: normal does the same for that property. Because each font-variant-* property is separate, resetting one leaves the others in place.

When font-feature-settings is the right tool

Some features have no font-variant value at all. The usual examples:

  • Stylistic sets, ss01 to ss20: alternate designs for groups of letters, such as a single-story “a”.
  • Character variants, cv01 to cv99: alternates for individual characters.
  • Case-sensitive forms, case: punctuation and brackets raised to suit all-capital text.

For these, font-feature-settings is fine. Put every feature that element needs in one declaration, because it replaces any earlier list:

.brand-name {
  font-feature-settings:
    'ss01' 1,
    'case' 1;
}

Values can be 1 or on to enable a feature and 0 or off to disable it. Some features take a number to pick one of several alternates.

A higher-level option for stylistic sets

CSS also has a named way to use stylistic sets: declare a name with @font-feature-values and use it with font-variant-alternates:

@font-feature-values 'Brand Sans' {
  @styleset {
    single-story-a: 1;
  }
}

.brand-name {
  font-variant-alternates: styleset(single-story-a);
}

Here single-story-a: 1 refers to ss01. MDN’s compatibility data lists support from Chrome 111, Firefox 34, and Safari 9.1. “Brand Sans” is a placeholder, and which set does what depends entirely on the font.

Check before you ship

  • Confirm the feature exists. Features differ from font to font, and even between weights of one family. Check each file you use in the font viewer.
  • Keep the features when you process the font. Converting a TTF or OTF to WOFF2 packs the same font data, so features come through. Subsetting tools may drop features they do not keep by default; see How to subset a web font. Converting between TTF and OTF rebuilds the outlines; FontPivot’s converter copies the layout features back from the original afterwards, but the hinting is lost, so keep your original file.
  • Test with real text. A feature that looks great in a sample can surprise you in content, such as a discretionary ligature in a product name.

To get a web-ready file, start with TTF to WOFF2 or OTF to WOFF2, or the font converter for other formats.

Summary

  • Use font-variant-* and font-kerning whenever a value exists for the feature you want.
  • Use font-feature-settings for stylistic sets, character variants, and other features without a font-variant value, and list all of an element’s features in one declaration.
  • A feature only works if the font has it; check first.
  • Small caps should always go through font-variant-caps.

Further reading

Share:
Back to guides

Related guides

All guides »