Fluid type scales with Less

03 May 2021
Web development

Using Less CSS to manage fluid type scales.

If you haven’t already, have a look at Utopia: Fluid responsive design. It’s a fantastic resource about fluid typography for the web. Utopia should be your first port of call, as I don’t intend this article to say much (if anything) about the theory of fluid typography—rather, discuss how to implement it in Less.

Utopia provides an excellent type scale calculator, which generates all the styles required to implement a type scale system. I wanted to create styles locally, so I turned to my favourite CSS preprocessor. 

First, I needed to define some basic settings. I tried to keep as many as possible as native CSS variables. Some, however, need to be bound into Less for the generation to work.

Ideally, I would have liked a straightforward mixin to generate all possible type scales. However, I couldn’t figure out how to do this with recursive mixins. So I now have two mixins: Positive and negative.

Here’s the code:

@width-min: 320;
@width-max: 1200;
@base: 16;

@type-min-scale: 1.2;
@type-min-size: 17;
@type-max-scale: 1.2;
@type-max-size: 21;
@typescale-up-steps: 10;
@typescale-down-steps: 2;

:root {
  --fluid-min-width: @width-min;
  --fluid-max-width: @width-max;

  --fluid-screen: 100vw;
  --fluid-bp: calc(
    (var(--fluid-screen) - var(--fluid-min-width) / @base * 1rem) /
      (var(--fluid-max-width) - var(--fluid-min-width))

#type {
    .positive-typescale(@counter: 0) when (@counter <= @typescale-up-steps) {
        @min: round(pow(@type-min-scale, @counter) * @type-min-size, 2);
        @max: round(pow(@type-max-scale, @counter) * @type-max-size, 2);
        :root {
            --step-@{counter}: calc((unit((@min/@base), rem)) + (@max - @min) * var(--fluid-bp));
        #type > .positive-typescale(@counter + 1); 
    .negative-typescale(@counter: 1) when (@counter <= @typescale-down-steps) {
        @min: round((@type-min-size / pow(@type-min-scale, @counter)), 2);
        @max: round((@type-max-size / pow(@type-max-scale, @counter)), 2);
        :root {
            --step--@{counter}: calc((unit((@min/@base), rem)) + (@max - @min) * var(--fluid-bp));
        #type > .negative-typescale(@counter + 1);  
/* start the recursive mixins */

The output from the above code similar to that of Utopia’s calculator. It’s probably worth discussing the @min and @max calculations, as this is hidden under the bonnet in Utopia. These can be written:

(scale ^ scale step) * base text size

For example, with a scale of 1.2 and a base text size of 16, the 3rd type size in this scale would be:

(1.2 ^ 3) * 16 = 27.65

Custom steps

In practice, I found I wanted to bridge more than one step on the scale. For example, a mega style that grows from step 6 on smaller screens to 12 at the maximum width. This is basically Utopia’s custom space steps

Here’s the code:

#type {
  /* translate between any steps within the system: */
  .steps(@minstep, @maxstep) {
	  @min: round(pow(@type-min-scale, @minstep) * @type-min-size, 2);
	  @max: round(pow(@type-max-scale, @maxstep) * @type-max-size, 2);
	  font-size: calc((unit((@min/@base), rem)) + (@max - @min) *var(--fluid-bp));


Want more?

The Roodesign newsletter is the best of the articles directly to your inbox. I'll send you one email (roughly) every month.

You may also like