CSS font-variation-settings and Custom Axes Guide

The font-variation-settings property in CSS provides low-level control over variable font features by directly manipulating the font's underlying OpenType axes. While modern CSS includes dedicated, high-level properties for standard axes—such as font-weight for wght or font-stretch for wdth—the font-variation-settings property remains essential for accessing and fine-tuning custom, proprietary axes defined by font creators.

Understanding font-variation-settings

Variable fonts contain one or more axes of variation that allow a single font file to behave like multiple distinct font instances. The font-variation-settings property provides a mechanism to set these axes using a key-value syntax, where the key is a case-sensitive, four-character OpenType tag and the value is a numeric variable within that axis’s supported range.

selector {
  font-variation-settings: 'tag1' value, 'tag2' value;
}

Standard Axes vs. Custom Axes

OpenType specifications divide axes into two distinct categories:

  1. Standard (Registered) Axes: Standardized axes that have universal meanings across all fonts. They are identified by four lowercase characters:

    • 'wght' (Weight)
    • 'wdth' (Width)
    • 'slnt' (Slant)
    • 'ital' (Italic)
    • 'opsz' (Optical Size)

    W3C specifications recommend using standard CSS properties (font-weight, font-stretch, font-style, and font-optical-sizing) instead of font-variation-settings when targeting standard axes to ensure semantic correctness and proper accessibility mappings.

  2. Custom (Vendor-Specific) Axes: Proprietary variations designed by a type foundry for a specific typeface. Common examples include grade, serif size, flare, or roundness. Custom axes are identified by four uppercase characters, distinguishing them from registered axes.

Implementing Custom Axes

To use a custom axis, inspect the variable font's metadata or foundry documentation to identify the four-character uppercase tag and its allowed numerical range (e.g., 0 to 100, or -1 to 1).

For instance, the font Recursive includes custom axes such as Casual ('CASL') and Cursive ('CRSV'). You target them directly through font-variation-settings:

.casual-heading {
  font-family: 'Recursive', sans-serif;
  font-weight: 700; /* Standard axis handled by standard property */
  font-variation-settings: 'CASL' 1, 'CRSV' 0.5; /* Custom axes */
}

Another widespread custom axis is Grade ('GRAD'), used to adjust the optical weight of text without changing its physical metrics or layout boundaries:

.dark-mode-text {
  font-family: 'Roboto Flex', sans-serif;
  font-variation-settings: 'GRAD' -0.5;
}

Animating and Transitioning Custom Axes

Because values declared in font-variation-settings are numerical, custom axes can be animated or transitioned smoothly using standard CSS transitions and keyframes:

.interactive-button {
  font-family: 'ExampleVariableFont', sans-serif;
  font-variation-settings: 'FLAR' 0;
  transition: font-variation-settings 0.3s ease-in-out;
}

.interactive-button:hover {
  font-variation-settings: 'FLAR' 100;
}

The Inheritance Caveat

The primary drawback of font-variation-settings is that it acts as an all-or-nothing property; it does not cascade on an axis-by-axis basis. If a child element defines font-variation-settings to modify one axis, it overrides and resets all other custom axes declared on parent elements unless they are explicitly re-declared.

To avoid unintended resets, manage custom axes using CSS custom properties (variables):

:root {
  --axis-casl: 0;
  --axis-crsv: 0;
}

body {
  font-family: 'Recursive', sans-serif;
  font-variation-settings: 'CASL' var(--axis-casl), 'CRSV' var(--axis-crsv);
}

.playful {
  --axis-casl: 1;
}

By binding custom axes to CSS variables, individual axes can be updated modularly without wiping out other declared variations in the cascade.