Published on

Beyond Bold and Italic: A Deep Dive into CSS `font-variant`

Authors

'Beyond Bold and Italic: A Deep Dive into CSS font-variant'

Unlock advanced typographic features hidden in your fonts with this comprehensive guide to the CSS font-variant property, from small-caps to ligatures and beyond.

Table of Contents

Beyond Bold and Italic: A Deep Dive into CSS font-variant

As web developers, we spend a lot of time tweaking font-family, font-size, and font-weight. These are the bread and butter of web typography. But what if I told you there's a whole world of typographic control hiding just beneath the surface, waiting to be unlocked? A world of elegant small-caps, sophisticated ligatures, perfectly aligned numbers, and stylistic alternates that can elevate your design from merely functional to truly beautiful.

Welcome to the world of the CSS font-variant property.

This often-overlooked property is your key to accessing the rich set of OpenType features embedded within many modern fonts. It's the difference between just using a font and truly mastering it.

In this guide, we'll take a comprehensive journey through the font-variant shorthand and its powerful longhand properties. We'll explore what they do, how to use them, and when they can make a significant impact on your design. Get ready to level up your CSS typography game!

What is font-variant? The 10,000-Foot View

At its core, font-variant is a CSS property that provides access to various typographic features of a font. Think of a font file not just as a collection of letter shapes, but as a small program with optional features. It might contain alternate glyphs for certain character combinations (ligatures), different styles of numbers, or special capital letters for titles.

Initially, font-variant was a simple property that could only be set to normal or small-caps. But with the advent of CSS Fonts Module Level 3, it has evolved into a powerful shorthand property that encompasses a wide range of features. These features are broken down into several longhand properties:

  • font-variant-caps
  • font-variant-ligatures
  • font-variant-numeric
  • font-variant-alternates
  • font-variant-position
  • font-variant-east-asian

While you can use the shorthand, we'll see that using the longhand properties often leads to clearer and more maintainable code. The good news? Browser support for these properties is excellent across all modern browsers, so you can start using them today.

The font-variant Shorthand: A Quick Look

The shorthand property allows you to set multiple font-variant values in a single declaration. Its syntax can look a bit intimidating at first:

/* Keyword values */
font-variant: normal;
font-variant: none;

/* Setting multiple features */
font-variant: small-caps common-ligatures lining-nums;
font-variant: discretionary-ligatures oldstyle-nums;

The value normal resets all longhand properties to their initial values. The value none is a bit more drastic—it's equivalent to setting font-variant-ligatures: none and font-variant-caps: normal and other properties to their defaults. It's primarily for disabling any default features like common ligatures.

While the shorthand is concise, it can be ambiguous. Does font-variant: normal; affect ligatures? Or numeric styles? It's often better to be explicit with the longhand properties. This approach is self-documenting and easier for you and your team to understand later.


Diving Deep: The Longhand Properties

This is where the real power lies. Let's break down each longhand property with practical examples. For these examples to work, you'll need a font that supports these features. Many Google Fonts, like Source Sans Pro, Fira Sans, or EB Garamond, are great choices.

1. font-variant-caps: The King of Capitals

This property is all about controlling capitalization. It goes far beyond text-transform: uppercase by providing access to typographically correct small-caps and other variations.

Values: normal, small-caps, all-small-caps, petite-caps, all-petite-caps, unicase, titling-caps.

small-caps

This is the most common value. It replaces lowercase letters with smaller, specially designed capital letters. This is perfect for subheadings, acronyms, or adding emphasis without the jarring effect of full caps.

Important Note: There's a huge difference between true small-caps (designed by the typographer and included in the font file) and faux small-caps (where the browser just shrinks down regular capital letters). True small-caps have a balanced weight and proportion, while faux ones often look thin and out of place. font-variant-caps: small-caps; will always try to use the true version if available.

<p class="small-caps-example">This is an Example of Small Caps</p>
.small-caps-example {
  font-family: 'EB Garamond', serif;
  font-variant-caps: small-caps;
}

all-small-caps

This value is similar to small-caps, but it also turns any uppercase letters into small-caps. This is useful for ensuring uniformity in text that might have mixed casing, like acronyms.

titling-caps

Many fonts include special capital letters designed for titles. They are often slightly lighter and more spaced out to look better at larger sizes. titling-caps enables these specific glyphs.

<h1 class="title">A Grand Title</h1>
.title {
  font-family: 'Source Sans Pro', sans-serif;
  font-size: 3rem;
  font-variant-caps: titling-caps;
}

Other values like petite-caps (even smaller caps) and unicase (a mix of small-caps for uppercase and regular lowercase) are less common and depend heavily on font support.

2. font-variant-ligatures: Connecting Characters Gracefully

A ligature is when two or more letters are joined into a single glyph to improve readability and aesthetics. The most common examples are fi, fl, and ffl.

Values: normal, none, common-ligatures, no-common-ligatures, discretionary-ligatures, no-discretionary-ligatures, historical-ligatures, no-historical-ligatures, contextual, no-contextual.

common-ligatures

These are the standard ligatures (fi, fl, etc.) that are often enabled by default in modern browsers (font-variant-ligatures: normal usually includes them). You can explicitly enable or disable them.

<p>Default: A fine flowing finish.</p>
<p class="no-ligatures">No Ligatures: A fine flowing finish.</p>
.no-ligatures {
  /* Explicitly disable common ligatures */
  font-variant-ligatures: no-common-ligatures;
}

Disabling them can sometimes be useful for code snippets or when you need precise letter spacing.

discretionary-ligatures

These are more ornamental and stylistic ligatures (like st or ct) that are disabled by default. They can add a touch of personality or historical flavor to your text. Use them sparingly, typically in headings or logos.

<h2 class="fancy-heading">A Classic Story</h2>
.fancy-heading {
  font-family: 'EB Garamond', serif;
  /* Enable both common and discretionary ligatures */
  font-variant-ligatures: common-ligatures discretionary-ligatures;
}

In the example above, if the font supports it, the Cl and st combinations might be rendered with special, connected glyphs.

historical-ligatures are, as the name implies, for historical texts and are rarely used in modern web design.

3. font-variant-numeric: Perfecting Your Numbers

This is one of the most practical and impactful font-variant properties. It gives you precise control over how numbers, fractions, and zeros are displayed.

Values: normal, lining-nums, oldstyle-nums, proportional-nums, tabular-nums, diagonal-fractions, stacked-fractions, ordinal, slashed-zero.

lining-nums vs. oldstyle-nums

  • lining-nums: These are the standard numbers you're used to. They all align on the baseline and are the same height as capital letters. They are ideal for data tables, price tags, and anywhere numbers need to stand out.
  • oldstyle-nums: Also called "text figures," these numbers have varying heights and alignments, much like lowercase letters (e.g., the '3', '4', '7', '9' might have descenders). They blend beautifully into paragraphs of text, making them less jarring to the eye.
<p class="story">In 2023, we sold 1,497 widgets for $58,340.</p>
<p class="table-data">In 2023, we sold 1,497 widgets for $58,340.</p>
.story {
  font-family: 'Source Sans Pro', sans-serif;
  font-variant-numeric: oldstyle-nums; /* Blends with text */
}

.table-data {
  font-family: 'Source Sans Pro', sans-serif;
  font-variant-numeric: lining-nums; /* Aligns neatly */
}

proportional-nums vs. tabular-nums

  • proportional-nums: Each number has a different width (e.g., '1' is narrower than '8'). This is best for numbers within sentences, as it provides more natural spacing.
  • tabular-nums: Every number has the same width. This is essential for data tables, spreadsheets, or any time you need numbers to align vertically in columns.
.financial-report td {
  font-family: monospace; /* A common way to get tabular numbers */
}

.financial-report-pro td {
  font-family: 'Fira Sans', sans-serif;
  /* The better, more flexible way! */
  font-variant-numeric: tabular-nums lining-nums;
}

Notice you can combine values! Here, we're asking for both tabular widths and lining style.

Other Numeric Heroes

  • diagonal-fractions: Turns 1/2 into a beautiful diagonal fraction glyph (½).
  • slashed-zero: Renders the number '0' with a slash through it to distinguish it from the letter 'O'. Invaluable for displaying product codes, serial numbers, or in code editors.
  • ordinal: Styles ordinal markers like 1st and 2nd with proper superscript glyphs.
<p>Code: <span class="code">GZ-007A</span></p>
<p>Fraction: <span class="fraction">3/4</span></p>
<p>Ordinal: <span class="ordinal">1st Place</span></p>
.code { font-variant-numeric: slashed-zero; }
.fraction { font-variant-numeric: diagonal-fractions; }
.ordinal { font-variant-numeric: ordinal; }

4. font-variant-alternates: Unlocking Stylistic Sets

This is where typography gets really creative. Many fonts come with alternate characters or entire stylistic sets that can dramatically change the look and feel of the text.

Values: normal, historical-forms, stylistic(), styleset(), character-variant(), swash(), ornaments(), annotation().

The most useful and well-supported of these are styleset() and swash().

  • styleset(): Modern fonts can bundle alternate glyphs into sets, labeled ss01, ss02, etc. For example, a font might have a different design for the letter 'a' or 'g' in ss01. You can enable these sets by name.
  • swash(): Enables more flamboyant, calligraphic versions of certain letters, often at the beginning or end of a word.

How do you know what sets a font supports?

You need to inspect the font file. An amazing free tool for this is Wakamai Fondue. Just drop a font file (or a Google Fonts URL) onto the site, and it will show you all the available OpenType features, including the names of stylistic sets.

For example, the font Fira Sans has a stylistic set ss01 that provides a more traditional, two-story 'a'.

<p class="standard-a">A great paragraph.</p>
<p class="alternate-a">A great paragraph.</p>
@import url('https://fonts.googleapis.com/css2?family=Fira+Sans&display=swap');

.standard-a, .alternate-a {
  font-family: 'Fira Sans', sans-serif;
  font-size: 2rem;
}

.alternate-a {
  /* In CSS, styleset() maps to the font-feature-settings value 'ss01' */
  font-variant-alternates: styleset(ss01);
}

Note: Browser support for font-variant-alternates can be less consistent than for other font-variant properties. For maximum compatibility, you might need to fall back to the lower-level font-feature-settings property, e.g., font-feature-settings: 'ss01' on;.

5. font-variant-position: Superscript and Subscript, The Right Way

We all know the <sup> and <sub> HTML tags. They work, but they often produce typographically inferior results. The browser simply shrinks the character and shifts it up or down, which can disrupt the line height and look spindly.

font-variant-position uses the font's own, specially designed superscript and subscript glyphs, which are weighted and positioned correctly.

Values: normal, sub, super.

<p>HTML way: H<sub>2</sub>O and E=mc<sup>2</sup></p>
<p>CSS way: H<span class="sub">2</span>O and E=mc<span class="super">2</span></p>
.sub {
  font-variant-position: sub;
}
.super {
  font-variant-position: super;
}

When you compare them side-by-side with a good font, the CSS version will look more balanced and professional.


Practical Use Cases & Best Practices

Let's put it all together. Theory is great, but how can we use this in real-world projects?

Use Case 1: The Elegant Blog Post Header

Combine titling-caps for a refined look at large sizes and enable discretionary-ligatures for a touch of class.

h1.blog-title {
  font-family: 'EB Garamond', serif;
  font-size: 4rem;
  font-variant-caps: titling-caps;
  font-variant-ligatures: common-ligatures discretionary-ligatures;
}

Use Case 2: The Professional Data Table

Ensure all your numbers align perfectly, are easy to read, and that zeros can't be mistaken for 'O's.

.data-table td.numeric {
  font-family: 'Fira Mono', monospace;
  /* For maximum clarity and alignment */
  font-variant-numeric: lining-nums tabular-nums slashed-zero;
}

Use Case 3: The Fancy Brand Name or Pull Quote

Use stylistic alternates or swashes to make a brand name or a special quote stand out.

.brand-name {
  font-family: 'My Custom Brand Font', sans-serif;
  font-variant-alternates: swash(fancy);
}

Best Practices to Remember

  1. Font Support is Key: These properties do nothing if the font doesn't contain the required OpenType features. Always test. Use tools like Wakamai Fondue to check what your chosen font is capable of.

  2. Use Longhands for Clarity: Prefer font-variant-numeric: tabular-nums; over the shorthand. It's more readable, maintainable, and less prone to accidental resets.

  3. Progressive Enhancement: font-variant is a perfect example of progressive enhancement. If a browser or font doesn't support a feature, it simply won't apply it. The text remains perfectly readable. There's very little risk in using these properties.

  4. Readability First: It can be tempting to enable all the fancy features. Resist the urge. discretionary-ligatures, swash, and historical-forms are best used for display text, not long paragraphs of body copy where they can hinder readability.

  5. Consider font-feature-settings as a Fallback: For cutting-edge features or broader compatibility with older syntax, the lower-level font-feature-settings property is your friend. It's more powerful but also more complex. font-variant is the modern, high-level API for the most common use cases.

Conclusion: Go Forth and Be Variant!

The CSS font-variant family of properties is a testament to how far web typography has come. It bridges the gap between the raw power of OpenType fonts and the practical application in CSS, giving us, the developers and designers, an incredible amount of control.

By moving beyond the basics, you can create interfaces that are not only more functional (hello, tabular-nums!) but also more expressive and aesthetically pleasing. So next time you're working on a project, take a moment to inspect your font, see what hidden gems it contains, and use font-variant to bring them to life.

Your typography will thank you for it.