Naming typography CSS variables

Yeah yeah, naming CSS custom properties (CSS variables) or Sass variables is hard. But even that is not the full picture. In ideal world

  • they are shared with design tools like Figma or Sketch.
  • developers, UX, and visual designers can quickly understand what they mean.
  • adding or removing variables isn’t too painful.
  • Changing the values is quick. Meaning that new typography (font-family, font-size and line-height) values for the whole system should be one hour task.
  • they forces for maintainable “design system”.
  • they keep CSS file size as low as possible.
  • ensure better UI consistency.
  • they are part of the CSS architecture.

Let’s start with typography related CSS variables. I sometimes see that font sizes and line heights are just thrown in the mix and hope for the best. That never ends well.

Typography in design systems is excellent article what I’m talking about. Go read that first.

Blocks of letters, looks like wooden.
Photo credit Raphael Schaller – Unsplash.

My current approach for typography CSS variables

My current approach is to use T-shirt sizing and it works OK. Here is simplified example:

/* Font families. */
--font-family-sans: "Roboto", sans-serif;
--font-family-serif: "Playfair Display", serif;

/* Font sizes. */
--font-size-sm: 0.875rem;
--font-size-md: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--font-size-3xl: 2rem;
--font-size-4xl: 3rem;

/* Line heights. */
--line-height-sm: 1.25;
--line-height-md: 1.5;

/* Letter spacings. */
--letter-spacing-sm: 0.05em;
--letter-spacing-md: 0.1em;

/* Use CSS variables later. */
body {
    font-family: var(--font-family-sans);
    font-size: var(--font-size-lg);
    line-height: var(--line-height-md);
}

h1,
.h1 {
    font-family: var(--font-family-serif);
    font-size: var(--font-size-3xl);
    line-height: var(--line-height-sm);
}

h2,
.h2 {
    font-family: var(--font-family-serif);
    font-size: var(--font-size-2xl);
    line-height: var(--line-height-sm);
}

Using T-shirt sizing hasn’t been huge issue in my experience. It’s more about “let’s add another font-size and especially another line-height”. Then it needs some search and replace in the code base.

I think adding new line-height for every font-size stems from design tools where line-height unit is px or pt.

At least I pretty much always get handed values like 16 / 24. Which means 16px font-size and 24px line-height.

But since using relative units are preferred for better accessibility, developers convert these as follows:

  • 16px = 1rem.
  • 24 / 16 = 1.5 as line-height.

Then we need to account that some of the font-sizes are larger on bigger screens, there are some uppercase subtitles etc.

As a summary, there is nothing wrong using T-shirt sizing. It’s more about fragile system for typography, too many ways to mess it up with too many variations, and collaboration between developers and designers.

I’m open for new ideas, fine tuning current approach, and test something like typography presets.

Typography presets

Since font-size, line-height, uppercase, and letter-spacing walks hand in hand, it makes perfect sense to bundle them together as typography presets.

body,
.text-preset-1 {
    font-family: var(--font-family-sans);
    font-size: var(--font-size-lg);
    line-height: var(--line-height-md);
}

h1,
.text-preset-2 {
    font-family: var(--font-family-serif);
    font-size: var(--font-size-3xl);
    line-height: var(--line-height-sm);
}

h2,
.text-preset-3 {
    font-family: var(--font-family-serif);
    font-size: var(--font-size-2xl);
    line-height: var(--line-height-sm);
}

...

.text-preset-7 {
    font-family: var(--font-family-serif);
    font-size: var(--font-size-sm);
    letter-spacing: var(--letter-spacing-sm);
    text-transform: uppercase;
}

7-9 presets should be enough for most sites and applications. In this system, named from 1 to 7, presets are not necessary in font-size order anymore. Adding new option in the end with number 8 would do the trick no matter what is the new font-size.

Couple of notes, me thinking out loud:

  • Maybe I’d still want them to start in font-size order, that’s how my brain has been trained.
  • Maybe we can even remove word preset and use text-1, text-2 etc.
  • Or use text-100, text-200, and then we would be able to add new preset in between (text-150) and keeping the font-size order.
  • Note that I used body, h1, h2 etc. with presets so we would have defaults for HTML elements also.
  • I try to avoid mixins because then I’d be repeating the same styles over and over again in different components. Which would results bigger CSS file size.
  • For font-weight variations I’d use separate utility classes like .font-semibold.

The key idea is that developers and designers would quickly see that here are all the typography presets what we have in this project.

And stick using only those with new components, sometimes adding a new one.

What about responsive typography?

Maybe clamp() for some day but would need more testing with user zooming or user changing default font size. In current method I change the font-size in the variable itself:

:root {
    --font-size-sm: 0.875rem;
    --font-size-md: 1rem;
    --font-size-lg: 1.125rem;
    --font-size-xl: 1.25rem;
    --font-size-2xl: 1.5rem;
    --font-size-3xl: 2rem;
    --font-size-4xl: 2rem;
}

@media (min-width: 56em) {
    :root {
        --font-size-xl: 1.5rem;
        --font-size-2xl: 2rem;
        --font-size-3xl: 2.5rem;
        --font-size-4xl: 3rem;
    }
}

I’d need to test does that make sense when using using presets.

As a conclusion I like the presets idea. Let the test begin!