Skip to main content
Dublin Library

The Publishing Project

Why we still need custom CSS In WordPress (1)

 

Since the release of Gutenberg as the content editor, and now full site experiences, WordPress has moved to using default styles and the theme.json configuration to manage styles for your theme.

There are times when this is not enough since there are things that you can't do in the editor.

If you're working on bespoke sites or sites using animations and specific font features you will need finer control than what the editor provides.

The post will cover some of these areas and why using CSS (or Javascript) is the better solution.

OpenType Features #

OpenType fonts provide a series of features to enhance the looks of the font on the page.

CSS has two mechanisms to use these features in your code. The first one is to use font-variant-* descriptors individually or via the font-variant shorthand for the following long-hand properties:

The table below shows the OpenType font feature and the font-variant descriptor and value combination used in CSS.

font feature font-variant-* descriptor/value Notes
Kerning (kern) or Vertical Kerning (vkrn) font-kerning: normal The font-kerning property will set the kern or vkrn feature depending on the writing mode.
Standard Ligatures (liga) or Contextual Ligatures (clig) font-variant-ligatures: common-ligatures
Discretionary Ligatures (dlig) font-variant-ligatures: discretionary-ligatures
Historical Ligatures (hlig) font-variant-ligatures: historical-ligatures
Contextual Alternates (calt) font-variant-ligatures: contextual
Subscript (subs) font-variant-position: sub
Superscript (sups) font-variant-position: super
Small Capitals (smcp) font-variant-caps: small-caps
Small Capitals From Capitals (c2sc) font-variant-caps: all-small-caps
Petite Capitals (pcap) font-variant-caps: petite-caps
Petite Capitals From Capitals (c2pc) font-variant-caps: all-petite-caps
Unicase (unic) font-variant-caps: unicase
Titling (titl) font-variant-caps: titling-caps
Lining Figures (lnum) font-variant-numeric: lining-nums
Oldstyle Figures (onum) font-variant-numeric: oldstyle-nums
Proportional Figures (pnum) font-variant-numeric: proportional-nums
Tabular Figures (tnum) font-variant-numeric: tabular-nums
Fractions (frac) font-variant-numeric: diagonal-fractions
Alternative Fractions (afrc) font-variant-numeric: stacked-fractions
Ordinals (ordn) font-variant-numeric: ordinal
Slashed Zero (zero) font-variant-numeric: slashed-zero
Historical Forms (hist) font-variant-alternates: historical-forms
Stylistic Alternates (salt) font-variant-alternates: stylistic() Define which alternate gets used by making an @font-feature-values rule
Character Variant 1 * Character Variant 99 (cv01 * cv99) font-variant-alternates: character-variant() Define which character variant gets used by making an @font-feature-values rule
Swash (swsh) or Contextual Swash (cswh) font-variant-alternates: swash() Define which swash gets used by making an @font-feature-values rule
Ornaments (ornm) font-variant-alternates: ornaments() Define which ornament gets used by making an @font-feature-values rule
Alternate Annotation Forms (nalt) font-variant-alternates: annotation() Define which annotation gets used by making an @font-feature-values rule
JIS78 Forms (jp78) font-variant-east-asian: jis78
JIS83 Forms (jp83) font-variant-east-asian: jis83
JIS90 Forms (jp90) font-variant-east-asian: jis90
JIS2004 Forms (jp04) font-variant-east-asian: jis04
Simplified Forms (smpl) font-variant-east-asian: simplified
Traditional Forms (trad) font-variant-east-asian: traditional
Full Widths (fwid) font-variant-east-asian: full-width
Proportional Widths (pwid) font-variant-east-asian: proportional-width
Ruby Notation Forms (ruby) font-variant-east-asian: ruby

Note that not all browsers support all font-variant descriptors. Check caniuse.com for updated information.

A second, lower level, means to use font-feature-settings to control all OpenType-related features for the font. This is meant as a workaround for when we need access to specific features that may or may not be available via font-variant descriptors.

There are drawbacks to using this lower-level descriptor.

  1. font-feature-settings doesn't cascade. If you change one of the values then you need to set all the values you're using, even if they don't change. The higher-level properties cascade individually. You can set one without setting the whole font-feature-settings list
  2. Some higher-level properties can be synthesized for fonts that do not support the font feature.

The CSS engine in WordPress supports either font-variant-* or font-feature-settings so you have to add them in your own custom CSS stylesheets.

Tools like wakamaifondue produce CSS stylesheets that handle both font-feature-settings and font-variation-settings (discussed in the next section).

The following example is the CSS stylesheet Wakamaifondue Beta produced for the Pacifico font.

@font-face {
    font-family: "Pacifico Regular";
    src: url("Pacifico-Regular.ttf");
    unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-0131, U+0134-017E,
    U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01C4-01CC, U+01E6-01E7,
    U+01EA-01EB, U+01FA-021B, U+022A-022D, U+0230-0233, U+0237, U+0259,
    U+02BC, U+02C6-02C7, U+02C9, U+02D8-02DD, U+0300-0304, U+0306-030C,
    U+030F, U+0311-0312, U+031B, U+0323-0324, U+0326-0328, U+032E, U+0331,U+0394, U+03A9, U+03BC, U+03C0,
		U+0400-045F, U+0462-0463, U+046A-046B,
    U+0472-0475, U+048A-04A5, U+04A8-04FF, U+0510-0513, U+051A-051D, U+0524-0529,
		U+052E-052F, U+1E80-1E85, U+1E9E, U+1EA0-1EF9, U+2013-2014, U+2018-201A,
		U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+2074,
		U+20A1, U+20A3-20A4, U+20A6-20A7,
		U+20A9, U+20AB-20AE, U+20B1-20B2,
		U+20B4-20B5, U+20B8-20BA, U+20BC-20BD,
		U+2116, U+2122, U+2202, U+220F,
		U+2211-2212, U+2215, U+2219-221A,
		U+221E, U+222B, U+2248, U+2260,
		U+2264-2265, U+25CA, U+FB01-FB02;
}

/* Set custom properties for each layout feature */
:root {
	--pacifico-regular-aalt: "aalt" off;
	--pacifico-regular-case: "case" off;
	--pacifico-regular-dlig: "dlig" off;
	--pacifico-regular-frac: "frac" off;
	--pacifico-regular-ordn: "ordn" off;
	--pacifico-regular-salt: "salt" off;
	--pacifico-regular-ss01: "ss01" off;
	--pacifico-regular-ss02: "ss02" off;
	--pacifico-regular-sups: "sups" off;
}

/* If class is applied, update custom property and
   apply modern font-variant-* when supported */
.pacifico-regular-aalt {
    --pacifico-regular-aalt: "aalt" on;
}

.pacifico-regular-case {
    --pacifico-regular-case: "case" on;
}

.pacifico-regular-dlig {
    --pacifico-regular-dlig: "dlig" on;
}

@supports (font-variant-ligatures: discretionary-ligatures) {
    .pacifico-regular-dlig {
        --pacifico-regular-dlig: "____";
        font-variant-ligatures: discretionary-ligatures;
    }
}

.pacifico-regular-frac {
    --pacifico-regular-frac: "frac" on;
}

@supports (font-variant-numeric: diagonal-fractions) {
    .pacifico-regular-frac {
        --pacifico-regular-frac: "____";
        font-variant-numeric: diagonal-fractions;
    }
}

.pacifico-regular-ordn {
    --pacifico-regular-ordn: "ordn" on;
}

@supports (font-variant-numeric: ordinal) {
    .pacifico-regular-ordn {
        --pacifico-regular-ordn: "____";
        font-variant-numeric: ordinal;
    }
}

.pacifico-regular-salt {
    --pacifico-regular-salt: "salt" on;
}

.pacifico-regular-ss01 {
    --pacifico-regular-ss01: "ss01" on;
}

.pacifico-regular-ss02 {
    --pacifico-regular-ss02: "ss02" on;
}

.pacifico-regular-sups {
    --pacifico-regular-sups: "sups" on;
}

@supports (font-variant-position: super) {
    .pacifico-regular-sups {
        --pacifico-regular-sups: "____";
        font-variant-position: super;
    }
}

.pacifico-regular-aalt,
.pacifico-regular-case,
.pacifico-regular-dlig,
.pacifico-regular-frac,
.pacifico-regular-ordn,
.pacifico-regular-salt,
.pacifico-regular-ss01,
.pacifico-regular-ss02,
.pacifico-regular-sups {
    font-feature-settings: var(--pacifico-regular-aalt),
		var(--pacifico-regular-case),
		var(--pacifico-regular-dlig),
		var(--pacifico-regular-frac),
		var(--pacifico-regular-ordn),
		var(--pacifico-regular-salt),
		var(--pacifico-regular-ss01),
		var(--pacifico-regular-ss02),
		var(--pacifico-regular-sups);
}

Finer Control Over Variable Fonts #

While we can define variable fonts when declaring them in theme.json there are still items that are not supported in theme.json and may not be available in fonts.

Variable fonts define four default axes. Like font-feature-settings, these can be set with regular descriptors as explained in the following table.

Variation axis Equivalent Descriptor Notes
Weight (wght) font-weight The font-weight property will set the wght axis if one is present.
Width (wdth) font-stretch The font-stretch property will set the wdth axis if one is present.
Slant (slnt) or Italic (ital) font-style The font-style property will set the slnt or ital axis, depending on its value.
Optical size (opsz) font-optical-sizing The font-optical-sizing property will set the opsz axis if one is present.

A font doesn't need to use all axes. Optical size is one we don't see in too many fonts.

In addition to these default axes, fonts can define any number of custom axes that are exclusive to the font.

Note:

See Microsoft's OpenType Design-Variation Axis Tag Registry documentation for a more in-depth discussion about the OpenType tags and why you would want to register them (or not).

For example, Recursive defines the following custom axes in addition to wght (weight) and slnt (Slant):

  • MONO (Monospace)
  • CASL (Casual)
  • CRSV (Cursive)

Warning:

The name of the axes are case-sensitive. Registered axes are all lowercase and custom axes are all uppercased.

There are no CSS equivalent to these custom features, so we resort to the lower-level font-variation-settings descriptor.

@font-face {
	font-family: "Recursive Sans Linear Light";
	src: url("Recursive_VF_1.084.woff2");
	font-weight: 300 1000;
}

.recursive-sans-linear-light-mono-linear-light {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 300, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-linear-light-italic {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 300, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-casual-light {
	font-variation-settings: "MONO" 1, "CASL" 1, "wght" 300, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-casual-light-italic {
  font-variation-settings: "MONO" 1, "CASL" 1, "wght" 300, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-linear {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 400, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-linear-italic {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 400, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-casual {
	font-variation-settings: "MONO" 1, "CASL" 1, "wght" 400, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-casual-italic {
	font-variation-settings: "MONO" 1, "CASL" 1, "wght" 400, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-linear-medium {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 500, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-linear-medium-italic {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 500, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-casual-medium {
	font-variation-settings: "MONO" 1, "CASL" 1, "wght" 500, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-casual-medium-italic {
	font-variation-settings: "MONO" 1, "CASL" 1, "wght" 500, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-linear-semibold {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 600, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-linear-semibold-italic {
  font-variation-settings: "MONO" 1, "CASL" 0, "wght" 600, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-casual-semibold {
	font-variation-settings: "MONO" 1, "CASL" 1, "wght" 600, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-casual-semibold-italic {
  font-variation-settings: "MONO" 1, "CASL" 1, "wght" 600, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-linear-bold {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 700, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-linear-bold-italic {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 700, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-casual-bold {
	font-variation-settings: "MONO" 1, "CASL" 1, "wght" 700, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-casual-bold-italic {
  font-variation-settings: "MONO" 1, "CASL" 1, "wght" 700, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-linear-extrabold {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 800, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-linear-extrabold-italic {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 800, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-casual-extrabold {
	font-variation-settings: "MONO" 1, "CASL" 1, "wght" 800, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-casual-extrabold-italic {
	font-variation-settings: "MONO" 1, "CASL" 1, "wght" 800, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-linear-black {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 900, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-linear-black-italic {
	font-variation-settings: "MONO" 1, "CASL" 0, "wght" 900, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-casual-black {
  font-variation-settings: "MONO" 1, "CASL" 1, "wght" 900, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-casual-black-italic {
  font-variation-settings: "MONO" 1, "CASL" 1, "wght" 900, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-linear-extrablack {
  font-variation-settings: "MONO" 1, "CASL" 0, "wght" 1000, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-linear-extrablack-italic {
  font-variation-settings: "MONO" 1, "CASL" 0, "wght" 1000, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-mono-casual-extrablack {
  font-variation-settings: "MONO" 1, "CASL" 1, "wght" 1000, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-mono-casual-extrablack-italic {
  font-variation-settings: "MONO" 1, "CASL" 1, "wght" 1000, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-sans-linear-light {
  font-variation-settings: "MONO" 0, "CASL" 0, "wght" 300, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-linear-light-italic {
  font-variation-settings: "MONO" 0, "CASL" 0, "wght" 300, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-sans-casual-light {
  font-variation-settings: "MONO" 0, "CASL" 1, "wght" 300, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-casual-light-italic {
  font-variation-settings: "MONO" 0, "CASL" 1, "wght" 300, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-sans-linear {
  font-variation-settings: "MONO" 0, "CASL" 0, "wght" 400, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-linear-italic {
    font-variation-settings: "MONO" 0, "CASL" 0, "wght" 400, "slnt" -15,
        "CRSV" 1;
}

.recursive-sans-linear-light-sans-casual {
    font-variation-settings: "MONO" 0, "CASL" 1, "wght" 400, "slnt" 0,
        "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-casual-italic {
    font-variation-settings: "MONO" 0, "CASL" 1, "wght" 400, "slnt" -15,
        "CRSV" 1;
}

.recursive-sans-linear-light-sans-linear-medium {
    font-variation-settings: "MONO" 0, "CASL" 0, "wght" 500, "slnt" 0,
        "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-linear-medium-italic {
    font-variation-settings: "MONO" 0, "CASL" 0, "wght" 500, "slnt" -15,
        "CRSV" 1;
}

.recursive-sans-linear-light-sans-casual-medium {
    font-variation-settings: "MONO" 0, "CASL" 1, "wght" 500, "slnt" 0,
        "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-casual-medium-italic {
    font-variation-settings: "MONO" 0, "CASL" 1, "wght" 500, "slnt" -15,
        "CRSV" 1;
}

.recursive-sans-linear-light-sans-linear-semibold {
    font-variation-settings: "MONO" 0, "CASL" 0, "wght" 600, "slnt" 0,
        "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-linear-semibold-italic {
    font-variation-settings: "MONO" 0, "CASL" 0, "wght" 600, "slnt" -15,
        "CRSV" 1;
}

.recursive-sans-linear-light-sans-casual-semibold {
    font-variation-settings: "MONO" 0, "CASL" 1, "wght" 600, "slnt" 0,
        "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-casual-semibold-italic {
    font-variation-settings: "MONO" 0, "CASL" 1, "wght" 600, "slnt" -15,
        "CRSV" 1;
}

.recursive-sans-linear-light-sans-linear-bold {
    font-variation-settings: "MONO" 0, "CASL" 0, "wght" 700, "slnt" 0,
        "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-linear-bold-italic {
    font-variation-settings: "MONO" 0, "CASL" 0, "wght" 700, "slnt" -15,
        "CRSV" 1;
}

.recursive-sans-linear-light-sans-casual-bold {
    font-variation-settings: "MONO" 0, "CASL" 1, "wght" 700, "slnt" 0,
        "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-casual-bold-italic {
    font-variation-settings: "MONO" 0, "CASL" 1, "wght" 700, "slnt" -15,
        "CRSV" 1;
}

.recursive-sans-linear-light-sans-linear-extrabold {
    font-variation-settings: "MONO" 0, "CASL" 0, "wght" 800, "slnt" 0,
        "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-linear-extrabold-italic {
    font-variation-settings: "MONO" 0, "CASL" 0, "wght" 800, "slnt" -15,
        "CRSV" 1;
}

.recursive-sans-linear-light-sans-casual-extrabold {
  font-variation-settings: "MONO" 0, "CASL" 1, "wght" 800, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-casual-extrabold-italic {
  font-variation-settings: "MONO" 0, "CASL" 1, "wght" 800, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-sans-linear-black {
  font-variation-settings: "MONO" 0, "CASL" 0, "wght" 900, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-linear-black-italic {
  font-variation-settings: "MONO" 0, "CASL" 0, "wght" 900, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-sans-casual-black {
  font-variation-settings: "MONO" 0, "CASL" 1, "wght" 900, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-casual-black-italic {
  font-variation-settings: "MONO" 0, "CASL" 1, "wght" 900, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-sans-linear-extrablack {
  font-variation-settings: "MONO" 0, "CASL" 0, "wght" 1000, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-linear-extrablack-italic {
  font-variation-settings: "MONO" 0, "CASL" 0, "wght" 1000, "slnt" -15, "CRSV" 1;
}

.recursive-sans-linear-light-sans-casual-extrablack {
  font-variation-settings: "MONO" 0, "CASL" 1, "wght" 1000, "slnt" 0, "CRSV" 0.501;
}

.recursive-sans-linear-light-sans-casual-extrablack-italic {
  font-variation-settings: "MONO" 0, "CASL" 1, "wght" 1000, "slnt" -15, "CRSV" 1;
}

Fonts may optionally provide one or more named instances. These instances are combinations of the axes available to the font.

The Font Level 4 specification defines the font-named-instance descriptor to provide the initial values for the supported axes of the variable font.

Unfortunately, this descriptor only works in the @font-face at-rule and there is no way to set these values for elements in the body of the page.

We can use CSS classes to get around this restriction using font-variation-settings.

The example below, generated from the Recursive variable font using Wakamaifondue, will produce a sans serif, italic, extra black casual/cursive style for whatever element it is applied to.

Note how we override all of the font's axes every time we make a change. This is necessary because font-variation-settings suffers the same issue as font-feature-settings: Unless you update all the values every time you make a change, the values you don't change will reset to their default values.

By assigning each one of these possible groupings to CSS classes we make it easier to work with. We just add the specific class to the element in the block editor.

.recursive-sans-casual-extrablack-italic {
  font-variation-settings:  "MONO" 0,
          "CASL" 1,
          "wght" 1000,
          "slnt" -15,"CRSV" 1;
}

Edit on Github