Retrofitting fluid typography

Back in December 2023, Creative Boom launched a refreshed identity and a redesigned website. It’s a terrific magazine and the redesign does a great job of bringing impactful imagery to the fore, with restrained typography supporting the writing really effectively.

Like any modern website, it is responsive and so adapts well to different screen sizes. However it does so using five breakpoints, resulting in six different designs, all of which are fixed width except the smallest.

A row of 6 screenshots showing the Creative Boom website as it looks at each breakpoint

I thought this was a missed opportunity, so as part of my forthcoming Patterns Day talk I decided to rebuild the article page using Utopian fluid typography: one adaptive design with no media queries, less compromise across viewports, and far less design and development debt.

Fluid typography means thinking in terms of type scales and flexible spacing across your defined design space. This means deciding upon the size of the smallest screen and the largest screen (viewport) that you want to concern yourself with. These form the boundaries of your design space. Thereafter you’re just designing at the extremes – design for the smallest screen and the largest viewport, and let maths fill in the gap. This is declarative design in action, rather than the more imperative approach that Creative Boom has taken.

There’s much more detail about this on the Utopia blog, but for now I’ll take you through my process of converting Creative Boom to fluid typography. The general approach is:

  1. Define a type scale and set the spacing for a small screen (@min).
  2. Define a type scale and set the spacing for a large viewport (@max).
  3. Tell the browser to interpolate between the two scales and spacing systems, based on the current viewport width (the Utopia calculator tools do this for you).

Type scales

My first job was to define the body text size for my two extreme viewport sizes. Creative Boom’s biggest media query happens at 1536px so I used that as my @max screen size. At this viewport the text is 20px (assuming no change to the default browser text size).

At Clearleft our design spaces tend to start with a minimum screen size of 320px, so I used that as my @min. Creative Boom uses 20px for its body text regardless of viewport size – I think this impairs the reading experience on a small screen, so I chose a more appropriate 17px.

I could now plug these numbers into Utopia’s type calculator and start playing around to identify the two type scales that work best for @min and @max viewports. In order to determine each type scale I needed to look at the main heading sizes used by Creative Boom: the biggest size of h1 is 60px, the smallest is 40px. Through a quick bit of experimentation I found that setting a type scale increment of 1.2 for my @min viewport, and 1.25 for the @max viewport size gave me type sizes of 42px and 61px respectively at step 5, which are close enough – remember the typography is fluid, so by definition it’s never going to match a fixed design exactly.

Screenshot of Utopia's type tool
Utopia's type tool calculates the text sizes for you, and also generates the necessary CSS

Choosing step 5 on the type scale to represent h1 headings left plenty of smaller steps in the scale to apply to subheadings and other significant text sizes. The type scales also provided sensible smaller text sizes for step -1 and step -2 which I could use for ancillary text elements such as tags and meta data.

Once I’d pasted in Utopia’s generated CSS to make the fluid type scales work, I was able to use the following simple CSS to set type sizes:

h1    { font-size: var(--step-5); }
h2    { font-size: var(--step-1); }
body  { font-size: var(--step-0); }
.nav  { font-size: var(--step--1); }
.tags { font-size: var(--step--2); }

Here's where they were applied:

Screenshot: type steps start with 5 on the <abbr class='c2sc'>H1</abbr>, go through to 0 on the paragraphs and -2 on the tags
Type steps applied to all text elements of the design

I also used a type step token to constrain the overall page width for very large viewports. This was in order to keep the line-length comfortable and the layout in line with the Creative Boom design. I went for 77ems based on the body size (this width also accommodates the sidebar):

.masthead, .article-header, .article-body {
    max-width: calc(var(--step-0) * 77);
}

Fluid spacing

With the type scales defined and applied, I could now turn my attention to the spacing. The Utopia spacing tool sets up simple t-shirt sizes for spacing, giving you tokens related to multiples of your body text size. In other words, as the body text varies with viewport size, so do the spacing tokens. Following a simple reset, I first set about spacing the typography, going down the ‘page’. Using the Creative Boom design as a guide, I just had to ask myself “on a scale of XS to XL, how big is that space?”. That gave me declarations like these:

h1      { margin-block-start:var(--space-m); }
h2      { margin-block:var(--space-s); }
h3      { margin-block-end: var(--space-m); }
h4      { margin-block: var(--space-xs); }
p       { margin-block-end: var(--space-s); }
figure  { margin-block: var(--space-xl); }
.byline { margin-block-end: var(--space-xs); }
date    { margin-block-end: var(--space-xs); }

Which were applied wherever space was required in the vertical plane:

Screenshot
Space tokens applied in the vertical plane

Fluid spacing in the vertical plane is relatively straight forward as scrolling means there is not so much of a limitation to a small viewport. Laying things out and spacing them apart in the horizontal place is a different ballgame. By definition small screens have very little width to play with, whereas large viewports have lots. Defining a fluid spacing scheme that works across both extremes is therefore more challenging.

Fortunately Utopia again comes to the rescue with its space value pairs. These enable you to specify much more dramatic space variance between different viewport sizes. One of the most obvious uses is the padding between the article and the viewport edge. At the @min viewport, I’ve set it to XS – just enough to provide breathing room to the viewport boundary. For the @max I was able to set a suitably generous 2XL amount of spacing. I made these decisions through quick experimentation using variations of a single spacing token:

.masthead, .article-header, .article-body {
  padding-inline: var(--space-xs-2xl);
}

Another bit of spacing of note is the gutter between the article text and the Editor’s Picks sidebar. I used flexbox to provide media-query-free responsive layout (see Every Layout) so I was able to simply set the gap property. Again in keeping with the design, I’m increasing the spacing significantly for wider viewports:

.article-body {
  display: flex;
  gap: var(--space-s-xl);
} 

To ensure a fully flexible design, I’ve used Utopian spacing tokens almost everywhere a space is required in the horizontal plane:

Screenshot, indicating an <abbr class='c2sc'>XS</abbr> to 2XL space value pair padding the page
Space value pairs applied in the horizontal plane

The final result

And here’s the final result in all its fluid glory:

The rebuilt Creative Boom article page fluidly resizing from 320px to 1600px

Feel free to play with the rebuild, although please remember this is just a prototype, so some aspects are missing and the underlying HTML in particular is far from production ready.

In my experience it’s easier to design using Utopian principles first rather than retrospectively. That said, although I rebuilt this page from scratch, it does show you can retrospectively apply fluid typography to a design, although it’s definitely better to think in those terms from the start.