Fluid Leading and Tracking

Jesús Olano,  2nd January 2026

Leading and tracking are two of the most basic components of any typographic system—and in extent of any design system—, but shockingly the indications about how to set them are often very loose.

Leading best practices

While apparently leading is not a controversial topic, if we go through the literature, things start to get tricky.

Longer measures require more leading that short ones. Dark faces need more lead than light ones. Faces that are large on the body need more lead than those that are small.[…] And unserified faces often need more lead than their serified counterparts.

Brighurst points out four distinct variables to look into. The line length, as longer lines need more leading in order to keep readability. The weight, with dark faces needing more leading than light ones. The x-height, as typefaces with small x-heights need more line-spacing that those with larger. Finally, the font family, as sans serifs will need more leading than serifs. Further leading must also be given to texts with plenty of superscripts and subscripts, mathematical formulas or frequent use of full capitals—languages like f.ex. German tend to have more capitals than others.

If we look for hard numbers, we can see a pattern but hardly a consensus. Butterick recommends setting the leading between 120% and 145%, while the WCAG mentions 1.5 as the minimum threshold. There are other mentions to 145% - 150% as the sweet spot, and even a study from 2009 that found out that 1.48 was the median value for the web. Finally, Tim Brown's article on A List Apart recommends using the Golden Ratio (1.618) as a good approximation.

A leading to rule them all

In order to account for differences between serifs vs. sans serifs and also between light vs. bold weights, we could use CSS variables to set different leadings for each font-family and/or weight combinations. But there is still no way to make CSS aware of those differences programatically, so we're going to keep them away of our formula for now.

We can start playing with font size, cap height and x-height and try to set a formula that reacts programatically to the different variations of those parameters.

a * font-size + b * (cap-height - x-height) - damping-factor

With this formulation, the leading is determined by a multiple of the font-size and a multiple of the difference between the cap height (cap) and the x-height (ex). I will add a damping factor inspired in the compressed typography article by Mathias Ott, that will make leading decrease as font-size increases and viceversa.

      --min-leading: 0.8em;

--cap-minus-x-height: calc(1cap - 1ex);
--font-size-unitless: calc(1em / 1px);

--size-dampening: calc(
  0.01em * 
  (
    var(--font-size-unitless) - 16
  )
);

--leading: max(
  var(--min-leading), 
  calc(
    2.2em - 
    2.4 * var(--cap-minus-x-height) - 
    var(--size-dampening)
  )
);
    

When translating this to CSS, I set the font-size parameter to 2.2, and the cap height minus x-height parameter to 2.4; these are the values that made sense for me as they set an eye pleasing leading both for the serifs and the sans serifs, but obviously that's a matter of each one's taste.

Typography is the art and technique of arranging type to make written language legible, readable and appealing when displayed. The arrangement of type involves selecting typefaces, point sizes, line lengths, line-spacing (leading), and letter-spacing (tracking), as well as adjusting the space between pairs of letters (tracking). Leading controls the vertical rhythm of text and affects readability. Proper leading ensures that the reader's eye can easily flow from one line to the next without confusion or strain.



Font Size:0.00px
Line Height:(0.000) 0.00px
Cap Height:0.00px
X-height:0.00px
Cap minus x-height:0.00px

While this formula works well on their own, we could also make a tweak in order to allow higher leading when the container size is narrower than a certain width. I was initially reluctant to do so, but if you check out the blockquote elements included in this text, you will see that they benefit for a more generous leading—and it would not look as well if we apply the general formula. So in my opinion, it is a good tweak to be used when we need to have text within narrow containers. For more conventional body text, if our typography is already fluid, this effect is already taken into account, as smaller viewports will already have smaller font sizes and thus smaller leading.

In order to do so, I will use CSS container queries. This requires adding the container-type property to the parent/container element, and then add some CSS variables to our text. There is an alternative method based in scroll driven animations,—but it is also a bit hacky—so I will stick with container queries.

      :root {
    --max-ch: 60;
    --min-ch: 18;
    --leading-ratio-adjustment-max: 0.15; 
  );
}

blockquote {
  container-type: inline-size;
}

blockquote span {
  --container-width: 100cqw;
  --ch: calc(
    var(--container-width) / 1ch
  );
  
  --clamped-ch: clamp(
    var(--min-ch),
    var(--ch),
    var(--max-ch)
  );
  
  --slope: calc(
    (
      var(--max-ch) - var(--clamped-ch)
    ) / 
    (
      var(--max-ch) - var(--min-ch)
    )
  );
  
  --leading-ratio-adjustment: calc(
    var(--leading-ratio-adjustment-max) * 
    var(--slope)
  );
  
  --adapted-leading: calc(
    var(--leading) * 
    (
      1 + var(--leading-ratio-adjustment)
    )
  );
  
  line-height: var(--adapted-leading);
}
    

This approach has the drawback that you cannot declare it globally as you need to declare all variables on one of the containers below the container-type declaration. In order to use it in a different component with different container width, we would have to redeclare again the local variables—the ones declared in blockquote span in our example—and give them another name. In order to avoid this, in the near future—the following code is still not working in Chrome 143 due to the use of typed arithmetic—we will be able to declare instead some CSS functions globally and reuse them locally.

      :root {
  --max-ch: 60;
  --min-ch: 18;
  --leading-ratio-adjustment-max: 0.15;

  @function --getWidthInCh(--container-width, --one-ch) {
    result: calc(--container-width / --one-ch);
  }

  @function --clamped-ch(--container-width, --one-ch) {
    result: clamp(
      var(--min-ch), 
      --getWidthInCh(--container-width, --one-ch), 
      var(--max-ch)
    );
  }

  @function --getLeadingSlope(--container-width, --one-ch) {
    result: calc(
      (
        var(--max-ch) - --clamped-ch(--container-width, --one-ch)
      ) / 
      (
        var(--max-ch) - var(--min-ch)
      )
    );
  }

  @function --leading-ratio-adjustment(--container-width, --one-ch) {
    result: calc(var(--leading-ratio-adjustment-max) * --getLeadingSlope(--container-width, --one-ch));
  }

  @function --adapted-leading(--container-width, --one-ch) {
    result: calc(var(--leading) * (1 + --leading-ratio-adjustment(--container-width, --one-ch)));
  }
}

blockquote {
  container-type: inline-size;
}

blockquote span {
  line-height: --adapted-leading(100cqw, 1ch)
}
    

This method calculates the number of characters using the container width (cw) and character units (ch), and then calculates the slope. We also set a maximum leading to be applied, so for narrow containers—18ch or less—, it will add to our already fluid leading an extra 15% at most, and for wide containers—60ch or more—it won't add anything. You can play with these parameters—or even remove hard limits—until you get to your desired measures.

Container Width: 0.00ch (drag bottom-right corner to resize)

Typography is the art and technique of arranging type to make written language legible, readable and appealing when displayed. The arrangement of type involves selecting typefaces, point sizes, line lengths, line-spacing (leading), and letter-spacing (tracking), as well as adjusting the space between pairs of letters (tracking). Leading controls the vertical rhythm of text and affects readability. Proper leading ensures that the reader's eye can easily flow from one line to the next without confusion or strain.



Font Size:0.00px
Adapted Line Height:(0.000) 0.00px
Base Line Height:(0.000) 0.00px
Container Width (ch):0.00ch
Cap minus x-height:0.00px

Fluid tracking

In typography, tracking is the adjustment of space between characters in a whole word—not to be confused with kerning, the adjustment of space between individual character pairs, and letterspacing, the adjustment of space between words. In CSS we cannot differenciate tracking and letterspacing, as both are controlled through the letter-spacing property, so in this part we are going to refer indistinctly to both.

A man who would letterspace lower case would steal sheep, Frederic Goudy liked to say. If this wisdom needs updating, it is chiefly to add that a woman who would letterspace lower case would steal sheep as well.

Brighurst advice in this case is to “letterspace all strings of capitals and small caps, and all long strings of digits as well”. He also argues about not letterspacing lowercase letters, as it hampers legibility, but makes a point about modern typefaces with very tight letterforms that could benefit from a slight increase in tracking. The amount of letterspacing in caps should amount to a 5% to 10% increase. Butterick also advices against letterspacing lowercases, and gives a 5% to 12% range for text in all caps or small caps.

The rationale for loosening letterspacing in all or small caps is that “default kerning pairs for upper case letters aim to reduce the spaces in combinations like AV o WA, so that they don't look apart. This is what creates the dense spots for a whole word or a title in all caps letters”.

As for lowercase, there seems a consensus not to add letterspacing, but as pointed out by Mathieu Bandimon—previous lead designer at Adobe—, there are good arguments in order to slightly decrease it as font size grows from a certain point. As the human eye perceives increased spacing as font sizes grow, he proposes to decrease letterspacing linearly on a 1% each 30px the font-size increases, setting a hard floor of -4% for large font sizes, and setting it at 0 for 25px and below. D'Amato does a great job translating it to CSS, but I took the liberty to simplify it—as we no longer need to use the trick to convert an unit into unitless on browsers that support typed arithmetic

      :root {
--rem: 1rem;

/* Min size 25px */
--min-size: 1.5625rem;

/* Tracking will decrease 1% for every 30px increase in font size */
--slope: -30;
--percent: calc(
  (
    1em - 
    var(--min-size)
  ) / 
  var(--slope)
);

--fluid-tracking: clamp(
  -0.04em,
  var(--percent),
  0em
);

/* Min size 14px */
--min-size-caps: 0.875rem;
--percent-caps: calc(
  0.075rem + 
  (
    1em - 
    var(--min-size-caps)
  ) / 
  var(--slope)
);

--fluid-tracking-caps: clamp(
  -0.04em,
  var(--percent-caps),
  0.12em
);

--font-size-small-caps: 1cap;
}
    

In our CSS, we create two different leadings, one is meant to be used in all texts, and the other, more generous, will be used for all caps or small caps. For the generic one, we used the same measures as Bandimon, but for the all caps one we made it so letter-spacing starts growing at a smaller size, and we allow positive letter-spacing the smaller the type size. This makes a really good tradeoff, as small caps on large font-size will still have some positive letter-spacing while the surrounded text will have it negative, reaching a nice balance between readability and aesthetics.

Typography is the art and technique of arranging type to make written language legible, readable and appealing when displayed. The arrangement of type involves selecting typefaces, point sizes, line lengths, line-spacing (leading), and letter-spacing (tracking). HTML and CSS provide powerful tools for web typography. Proper tracking ensures that the reader's eye can flow smoothly across text, especially when using ALL CAPS or SMALL CAPS.



Font Size:0.00px
Letter Spacing:(0.00%) 0.00px
Letter Spacing (Small Caps):(0.00%) 0.00px