Using OpenType features for typographic refinements deliberately
Every professional font file holds far more than a single character set: ligatures, alternate glyphs, small caps and stylistic sets already sit ready inside the font file, but stay unused until something addresses them through CSS. font-variant-alternates and its sibling properties make exactly that possible, without JavaScript and without loading extra font files.
Table of Contents
- 1. OpenType features on the web: why dedicated font-variant properties exist
- 2. font-variant-ligatures: standard, discretionary and historical ligatures
- 3. font-variant-alternates: stylistic sets, character-variant and swash
- 4. font-variant-caps: small caps, unicase and titling caps
- 5. font-feature-settings as a low-level fallback: when it is still needed
- 6. A practical recipe: a robust @font-feature-values fallback chain
- 7. Interaction with variable fonts: font-variation-settings is a different thing
- 8. Performance and browser support: a test strategy for production use
- 9. Practical example: an editorial layout with a swash headline and plain body text
- 10. Summary
- 11. FAQ
1. OpenType features on the web: why dedicated font-variant properties exist
OpenType font files store, in addition to plain glyph outlines, so-called features, named rules inside the file's GSUB and GPOS tables that define how certain character sequences should be substituted or positioned. A feature tagged liga, for instance, replaces the letter sequence fi with a joined ligature glyph, a feature like smcp replaces lowercase letters with small-caps glyphs. Without CSS addressing them, all of these features stay unused, even when the font file ships them.
CSS offers two layers for this: the font-variant-* properties (font-variant-ligatures, font-variant-alternates, font-variant-caps, font-variant-numeric, font-variant-east-asian) address typographic concepts through readable values, without requiring authors to know the underlying four-letter OpenType tags. font-feature-settings, by contrast, works at a lower level directly with those tags. For most use cases the font-variant properties are the better choice, because they read more clearly and the browser falls back cleanly to the default value whenever support is missing.
2. font-variant-ligatures: standard, discretionary and historical ligatures
font-variant-ligatures controls which ligatures the browser activates. The value common-ligatures (the default in most browsers) turns on standard ligatures like fi or fl, which are present in nearly every professional typeface and usually improve legibility without drawing attention. discretionary-ligatures turns on additional, decorative ligatures a typeface optionally ships, for example ornate connections between specific letter pairs meant to stand out deliberately.
historical-ligatures activates ligatures rooted in historical writing conventions, typically used today in editorial or decorative contexts, for example invitation cards or stylized headlines. no-common-ligatures explicitly disables even the standard ligatures, which can make sense in technical contexts where every character sequence must remain individually recognizable, for example in code blocks or when rendering variable names.
3. font-variant-alternates: stylistic sets, character-variant and swash
font-variant-alternates reaches more complex OpenType features that a typeface often ships in several named variants, for example styleset() for stylistic sets (ssXX tags), character-variant() for individual alternate glyph shapes (cvXX tags), and swash() for ornate leading letters. Because these features are named and numbered differently per typeface, font-variant-alternates additionally requires an @font-feature-values block that maps a readable name to the typeface-specific number.
That indirection may look cumbersome at first glance, but it keeps the same CSS code readable even when different fonts with different internal numbering are used behind the scenes. Without a matching @font-feature-values block, the browser silently ignores the styleset() or character-variant() instruction and falls back to the default glyphs, which makes debugging harder if the block is missing or contains a typo.
@font-feature-values "Custom Serif" {
@styleset {
swashy-caps: 1;
}
@character-variant {
alt-ampersand: 2;
}
}
h1 {
font-family: "Custom Serif", serif;
font-variant-alternates: styleset(swashy-caps) character-variant(alt-ampersand);
}
4. font-variant-caps: small caps, unicase and titling caps
font-variant-caps bundles several uppercase variants under one property. small-caps replaces lowercase letters with smaller, genuine small-caps glyphs (not simply scaled-down uppercase letters), which produces a noticeably calmer look than text-transform: uppercase, particularly for abbreviations inside body text. all-small-caps applies the same substitution to already uppercase letters as well, producing a fully uniform small-caps appearance.
titling-caps delivers uppercase glyphs specifically optimized for large sizes, which often look more balanced in headlines than the typeface's regular capitals. unicase turns on a unified glyph shape where uppercase and lowercase become visually nearly indistinguishable, a stylistic device used mostly in reduced, geometric typography. All these values only take effect if the typeface in use actually ships the matching OpenType features, otherwise the rendering stays unchanged.
.abbreviation {
font-variant-caps: small-caps;
}
h1.editorial {
font-variant-caps: titling-caps;
}
5. font-feature-settings as a low-level fallback: when it is still needed
font-feature-settings addresses OpenType features directly through their four-letter tags, for example font-feature-settings: 'liga' 1, 'kern' 1. That is more powerful because it can reach any feature a typeface ships, including ones without a dedicated font-variant property, for example stylistic-set combinations outside the standard range or experimental features from individual foundries.
The downside: font-feature-settings bypasses the semantic layer entirely, a value like 'ss01' 1 says nothing about what that feature actually does, and multiple font-feature-settings declarations at different cascade levels overwrite each other completely instead of merging, unlike the font-variant properties. In practice the rule of thumb is: try font-variant-* first, and reach for font-feature-settings only for features that have no dedicated property.
6. A practical recipe: a robust @font-feature-values fallback chain
Because styleset() and character-variant() numbers vary between typefaces, it pays to write one @font-feature-values block per typeface in use, consistently mapping the same readable names to the correct internal number for each. That keeps the actual CSS code using font-variant-alternates independent of which specific typeface is currently loaded, as long as a matching block exists for each font.
For typefaces without the desired feature, font-variant-alternates simply stays inert, the browser automatically falls back to the default glyphs without producing an error. That property makes font-variant-alternates progressive enhancement in the real sense: whoever loads the matching typeface sees the refinements, whoever uses a different typeface or falls back to a fallback font still sees readable, correct text.
@font-feature-values "Editorial Display" {
@styleset {
swashy-caps: 1;
}
}
@font-feature-values "Fallback Serif" {
@styleset {
swashy-caps: 3;
}
}
.headline {
font-family: "Editorial Display", "Fallback Serif", serif;
font-variant-alternates: styleset(swashy-caps);
}
7. Interaction with variable fonts: font-variation-settings is a different thing
A common misunderstanding is treating font-variant-alternates and font-variation-settings as the same concept, even though they address entirely different technologies. font-variant-alternates turns on OpenType features (GSUB/GPOS) that swap in fixed, alternate glyphs. font-variation-settings, by contrast, controls a variable font's continuous design axes, for example weight (wght) or width (wdth), which interpolate smoothly between defined values instead of replacing one fixed glyph with another.
Both mechanisms can be combined in the same typeface and the same CSS rule block: a variable font can ship both variable axes for weight and width as well as classic OpenType features for ligatures and small caps. In practice that means using font-variation-settings for axis control and applying font-variant-alternates, respectively the other font-variant properties, in parallel to activate fixed glyph substitutions, without mixing the two concepts up.
.headline {
font-family: "Variable Display";
font-variation-settings: "wght" 650, "wdth" 110;
font-variant-ligatures: discretionary-ligatures;
font-variant-caps: small-caps;
}
8. Performance and browser support: a test strategy for production use
The font-variant properties themselves cost no noticeable rendering performance, because glyph substitution happens during text shaping, a step the browser runs for every piece of text rendering anyway. The real cost question concerns the font file: fonts with many OpenType features tend to be larger, because extra glyphs and feature tables need to ship along, which should be weighed when picking a typeface for performance-critical pages.
For checking support, a simple visual comparison across the relevant target browsers is usually enough in practice, because font-variant-alternates and font-variant-caps are fault tolerant: when support or the matching feature in the typeface is missing, the default glyph simply gets shown, never a broken or faulty layout. That allows using these features in production without building elaborate browser branches or @supports checks.
9. Practical example: an editorial layout with a swash headline and plain body text
A typical editorial layout applies OpenType features deliberately and sparingly: the large headline gets swash() for an ornate leading letter and discretionary-ligatures for decorative character, while the body text deliberately keeps only common-ligatures, so legibility across large amounts of text is never put at risk. Too many activated alternates inside body text quickly look busy and distract from the actual content.
Abbreviations inside body text, such as company names or technical acronyms, on the other hand deliberately benefit from font-variant-caps: small-caps, because they blend into the flow of text visually better than fully uppercase words, which inside body text often read like an exclamation. That deliberate, context-dependent combination of different features is the real advantage over a blanket text-transform solution.
| Property | Purpose | Needs @font-feature-values | Typical use |
|---|---|---|---|
| font-variant-ligatures | Standard, discretionary, historical ligatures | No | Body text, headlines |
| font-variant-alternates | styleset(), character-variant(), swash() | Yes | Editorial layouts, logos |
| font-variant-caps | small-caps, titling-caps, unicase | No | Abbreviations, large headlines |
| font-feature-settings | Direct access to any OpenType tag | No | Features with no dedicated property |
| font-variation-settings | Continuous variable font axes | No | Weight, width, optical size |
Mironsoft
Modern CSS, layout architecture and rendering performance
CSS that stays maintainable instead of breaking with every change?
We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.
CSS Audit
Systematically uncovering specificity issues, cascade conflicts and unused selectors.
Architecture Refactoring
Introducing cascade layers, custom properties and design tokens cleanly.
Performance Tuning
Fixing layout thrashing, expensive selectors and rendering bottlenecks.
10. Summary
font-variant-alternates: The Essentials at a Glance
Core idea
OpenType font files hold features in GSUB/GPOS tables that only get activated through CSS properties like font-variant-alternates.
Named values
styleset() and character-variant() need an @font-feature-values block that maps readable names to the typeface-specific feature number.
Fallback safety
If a feature is missing from a typeface, the browser silently falls back to the default glyph, a broken layout never results.
Variable fonts
font-variation-settings controls continuous axes like weight, font-variant-alternates turns on fixed glyph substitutions, and both can be combined.