Typography on the web has always lagged behind its expression in print. This is understandable, as type on the printed page had centuries to develop to a point of complexity that has been hard to capture within the confines of a browser.

However, this is quickly changing thanks to the increasing availability of OpenType features in web fonts and the ability to control those features with CSS.

This article is designed to be an overview of how to control OpenType features using CSS, but remember that whatever web font you are using will also need to support these features.

For live examples, check out my cheat sheet here.

The font-variant- Properties

You can control most OpenType features using various properties beginning with font-variant-. There is also a low-level property font-feature-settings which can be used to support legacy browsers. Whenever possible, however, you should use the more modern font-variant properties. One solution is to use an @supports rule to check if a font-variant property is supported and otherwise use font-feature-settings.

body {
    font-feature-settings: "liga" 1;
}

@supports (font-variant-ligatures: common-ligatures) {
    body {
        font-feature-settings: normal;
        font-variant-ligatures: common-ligatures;
    }
}

font-variant-ligatures

Ligatures are single glyphs made from two or more characters. They typically prevent ugly or awkward letter collisions and, therefore, aid legibility.

common-ligatures

These are ligatures that the type designer has decided should be used in normal conditions. In most circumstances you should use these. They are enabled by default in most browsers.

Common ligatures for fi, ff, fl, and fj.
Common ligatures enabled above, disabled below.
font-variant-ligatures: common-ligatures; /* enable */
font-variant-ligatures: no-common-ligatures; /* disable */

font-feature-settings: 'liga' 1; /* low-level enable */
font-feature-settings: 'liga' 0; /* low-level disable */

discretionary-ligatures

These are ligatures which can be used for typographic purposes, for example to achieve a special effect. These are disabled by default.

Discretionary ligatures for cp, sp, and st.
Discretionary ligatures enabled above, disabled below.
font-variant-ligatures: discretionary-ligatures; /* enable */
font-variant-ligatures: no-discretionary-ligatures; /* disable */

font-feature-settings: 'dlig' 1; /* low-level enable */
font-feature-settings: 'dlig' 0; /* low-level disable */

contextual

These are alternate ligatures that are affected by their surrounding context. They are used to harmonize the shapes of grouped glyphs. These are disabled by default.

Contextual ligatures for bloo, on, and ose.
Contextual ligatures enabled above, disabled below.
font-variant-ligatures: contextual; /* enable */
font-variant-ligatures: no-contextual; /* disable */

font-feature-settings: 'calt' 1; /* low-level enable */
font-feature-settings: 'calt' 0; /* low-level disable */

historical-ligatures

These are ligatures which could be considered a subset of discretionary, but are specifically used to achieve a historical effect. These are disabled by default.

font-variant-ligatures: historical-ligatures; /* enable */
font-variant-ligatures: no-historical-ligatures; /* disable */

font-feature-settings: 'hlig' 1; /* low-level enable */
font-feature-settings: 'hlig' 0; /* low-level disable */

font-variant-position

The proper markup for subscripts and superscripts uses the sub and sup elements. By default, browsers take a regular numeral character, make it smaller using font-size, and raise or lower it with vertical-align. These are not true subscript and superscript characters and typically appear quite ugly, not to mention they can mess up line height.

Thankfully, there is now a way to enable true subscripts and superscripts with font-variant-position. Note that currently this is only supported in Firefox.

Proper subscripts and superscripts are specially designed for a more even texture.
True subscripts and superscripts above (in Firefox only). Browser defaults below.

sub

This enables true subscript characters.

font-variant-position: sub; /* enable */
font-variant-position: normal; /* disable both variants */

font-feature-settings: 'subs' 1; /* low-level enable */
font-feature-settings: 'subs' 0; /* low-level disable */

super

This enables true superscript characters.

font-variant-position: super; /* enable */
font-variant-position: normal; /* disable both variants */

font-feature-settings: 'sups' 1; /* low-level enable */
font-feature-settings: 'sups' 0; /* low-level disable */

font-variant-caps

A capital is not a capital is not a capital. The most significant use of font-variant-caps is to enable small caps, although there are several other options available.

small-caps

Small caps are designed to be the same height as lowercase letters and are used to capitalize words within running text. They make for a more cohesive and readable paragraph.

JPL and NASA in small caps.
Small caps above, regular caps below.
font-variant-caps: small-caps; /* enable */
font-variant-caps: normal; /* disable all variants */

font-feature-settings: 'smcp' 1; /* low-level enable */
font-feature-settings: 'smcp' 0; /* low-level disable */

all-small-caps

The small-caps value with only replace lowercase letters with small caps. To replace all letters with small caps (which is probably what you want) you need to use all-small-caps.

"All Letters" in all small caps.
All small caps above, regular small caps below.
font-variant-caps: all-small-caps; /* enable */
font-variant-caps: normal; /* disable all variants */

font-feature-settings: 'smcp' 1, 'c2sc' 1; /* low-level enable */
font-feature-settings: 'smcp' 1, 'c2sc' 0; /* low-level disable */

petite-caps

Standard small caps will typically appear slightly larger than the x-height of the font. Some typefaces have additional small caps that match the x-height. These are called petite-caps.

font-variant-caps: petite-caps; /* enable */
font-variant-caps: normal; /* disable all variants */

font-feature-settings: 'pcap' 1; /* low-level enable */
font-feature-settings: 'pcap' 0; /* low-level disable */

all-petite-caps

Similarly to all-small-caps, this converts all letters, both lower and uppercase, to petite caps.

font-variant-caps: all-petite-caps; /* enable */
font-variant-caps: normal; /* disable all variants */

font-feature-settings: 'pcap' 1, 'c2pc' 1; /* low-level enable */
font-feature-settings: 'pcap' 1, 'c2pc' 0; /* low-level disable */

unicase

This feature maps upper and lowercase letters to a mixed set of lowercase and small capital forms, creating a single case alphabet. Sometimes the small capitals used are actual small cap glyphs and sometimes they are specially designed unicase forms. The implementation of this feature varies greatly from font to font.

font-variant-caps: unicase; /* enable */
font-variant-caps: normal; /* disable all variants */

font-feature-settings: 'unic' 1; /* low-level enable */
font-feature-settings: 'unic' 0; /* low-level disable */

titling-caps

Standard uppercase letters are designed for use alongside lowercase letters and when they are used in strings of all uppercase letters they can appear too strong. Some fonts include titling capitals specifically for this situation.

font-variant-caps: titling-caps; /* enable */
font-variant-caps: normal; /* disable all variants */

font-feature-settings: 'titl' 1; /* low-level enable */
font-feature-settings: 'titl' 0; /* low-level disable */

font-variant-numeric

The proper display of numerals varies greatly depending on context. Here are some general rules:

  • In running/body text, use proportional old-style numerals
  • In headings, use proportional lining numerals
  • In numeric tables, use tabular lining numerals

You can combine values to achieve, for example, tabular lining numerals like this:

font-variant-numeric: lining-nums tabular-nums;

lining-nums

Lining numerals approximate capital letters and are uniform in height. They should be used in headings or numeric tables. Usually numbers are lining figures by default.

1912 as lining numerals.
Lining numerals above, old-style numerals below.
font-variant-numeric: lining-nums; /* enable */
font-variant-numeric: normal; /* disable all variants */

font-feature-settings: 'lnum' 1; /* low-level enable */
font-feature-settings: 'lnum' 0; /* low-level disable */

oldstyle-nums

Old-style numerals have varying heights and alignments and are therefore more similar to lowercase letters. They should be used in running text.

50 and 1928 as old-style numerals.
Old-style numerals above, lining numerals below.
font-variant-numeric: oldstyle-nums; /* enable */
font-variant-numeric: normal; /* disable all variants */

font-feature-settings: 'onum' 1; /* low-level enable */
font-feature-settings: 'onum' 0; /* low-level disable */

proportional-nums

Proportional numerals have variable spacing and blend in with horizontal text. They should be used in most situations, other than numeric tables where vertical alignment is important. Usually numbers are proportional figures by default.

font-variant-numeric: proportional-nums; /* enable */
font-variant-numeric: normal; /* disable all variants */

font-feature-settings: 'pnum' 1; /* low-level enable */
font-feature-settings: 'pnum' 0; /* low-level disable */

tabular-nums

Tabular numerals have the same width and should be used in numeric tables to allow vertical alignment of numbers.

A table of tabular vs. proportional lining numerals.
Tabular vs. proportional lining numerals. Note the difference in row widths.
font-variant-numeric: tabular-nums; /* enable */
font-variant-numeric: normal; /* disable all variants */

font-feature-settings: 'tnum' 1; /* low-level enable */
font-feature-settings: 'tnum' 0; /* low-level disable */

diagonal-fractions

By default, fractions will display as lowercase letters with a slash. Proper fractions will be formatted to match the height of a lining figure and can be either diagonal or stacked.

3/4 and 1/2 as proper diagonal fractions.
Proper diagonal fractions above, browser default below.
font-variant-numeric: diagonal-fractions; /* enable */
font-variant-numeric: normal; /* disable all variants */

font-feature-settings: 'frac' 1; /* low-level enable */
font-feature-settings: 'frac' 0; /* low-level disable */

stacked-fractions

Stacked fractions are not as common of a feature in most web fonts as diagonal fractions, but should prove useful with heavily scientific or mathematical content.

font-variant-numeric: stacked-fractions; /* enable */
font-variant-numeric: normal; /* disable all variants */

font-feature-settings: 'afrc' 1; /* low-level enable */
font-feature-settings: 'afrc' 0; /* low-level disable */

ordinal

Ordinals like st, nd, rd, and th will appear as standard lowercase letters by default. However, these should ideally appear as smaller raised numbers following the numeral. The ordinal value enables that.

1st, 2nd, and 3rd as proper ordinals.
Proper ordinals above, browser defaults below.
font-variant-numeric: ordinal; /* enable */
font-variant-numeric: normal; /* disable all variants */

font-feature-settings: 'ordn' 1; /* low-level enable */
font-feature-settings: 'ordn' 0; /* low-level disable */

slashed-zero

This enables an alternate zero character with a slash through it.

Slashed zero alternate numeral.
Slashed zero above, default zero numeral below.
font-variant-numeric: slashed-zero; /* enable */
font-variant-numeric: normal; /* disable all variants */

font-feature-settings: 'zero' 1; /* low-level enable */
font-feature-settings: 'zero' 0; /* low-level disable */

font-variant-alternates

Fonts can provide a variety of alternates for any character. The font-variant-alternates property provides many ways of controlling this character substitution.

historical-forms

Historical alternates can be used for a “period” effect. Note the difference between this and historical ligatures. Historical ligatures are historical character combinations, whereas historical forms are substitutions for individual characters.

A historical form for the letter s.
Historical form above, default character below.
font-variant-alternates: historical-forms; /* enable */
font-variant-alternates: normal; /* disable all variants */

font-feature-settings: 'hist' 1; /* low-level enable */
font-feature-settings: 'hist' 0; /* low-level disable */

stylistic(n)

Use this to select stylistic features on an individual basis. For example, select stylistic feature number 1 with font-variant-alternates: stylistic(1).

font-variant-alternates: stylistic(1); /* enable */
font-variant-alternates: normal; /* disable all variants */

font-feature-settings: salt 1; /* low-level enable */
font-feature-settings: salt 0; /* low-level disable */

styleset(1-99)

Use this to select an entire set of alternative glyphs. The glyphs in a set are often designed to work together. Select a particular set by passing in its number, for example: font-variant-alternates: styleset(1) would select styleset number 1.

Alternate styleset with variations for the letters a, g, y, and &.
Stylistic alternates above, defaults below.
font-variant-alternates: styleset(1); /* enable */
font-variant-alternates: normal; /* disable all variants */

font-feature-settings: ss01; /* low-level enable */

character-variant(1-99)

Use this to select specific character variants. Select a particular variant by passing in its number, for example: font-variant-alternates: character-variant(1) would select character variant number 1.

font-variant-alternates: character-variant(1); /* enable */
font-variant-alternates: normal; /* disable all variants */

font-feature-settings: cv01; /* low-level enable */

swash(n)

Swashes can be used to provide typographic interest to headings or more artistic settings of text. They are alternative character designs that are typically exaggerated, or have some sort of typographic flourish. Select a particular swash by passing in its number, for example font-variant-alternates: swash(1) would select swash number 1.

Swashes for the letters M, g, and a.
Swashes above, defaults below.
font-variant-alternates: swash(1); /* enable */
font-variant-alternates: normal; /* disable all variants */

font-feature-settings: swsh 1; /* low-level enable */
font-feature-settings: swsh 0; /* low-level disable */

ornaments(n)

This replaces default glyphs with ornaments, if they are provided in the font.

font-variant-alternates: ornaments(1); /* enable */
font-variant-alternates: normal; /* disable all variants */

font-feature-settings: ornm 1; /* low-level enable */
font-feature-settings: ornm 0; /* low-level disable */

annotation(n)

Annotations are notational forms of glyphs (for example, glyphs placed in open or solid circles, squares, parentheses, diamonds, rounded boxes. etc.).

font-variant-alternates: annotation(1); /* enable */
font-variant-alternates: normal; /* disable all variants */

font-feature-settings: nalt 1; /* low-level enable */
font-feature-settings: nalt 0; /* low-level disable */

Further Resources

There is a huge amount to learn about typography on the web including font variants and much more. Check out the following excellent resources for more information: