Creating Responsive Font Sizes Based on the Viewport

Introduction

Previously, responsive typography required a series of media queries, font definitions, and other complexities. All of that has changed thanks to widespread browser support for variable fonts and the all-important CSS clamp() function.

I'll show you how to linearly scale text between a set of minimum and maximum sizes when the width of the viewport changes, with the goal of making its behaviour at different screen sizes more predictable without the use of media queries and with only one line of CSS.

Although this article concentrates on responsive font sizes, you can use clamp() very effectively with any other object you need to size with limits, particularly containers and images.

Included is a handy on-screen calculator to build your font sizes and view the results in real-time.

fa-solid fa-triangle-exclamation Warning
You must be using a variable font for this to be effective. Using non-variable fonts will likely result in blocky or fuzzy edges in between whole pixel values. There is a round() function coming to CSS which would allow you round to the nearest pixel, but this is some way off yet at the time of writing. Google Fonts lets you filter by variable font type.

Clamp Overview

The clamp() CSS function clamps a value in the middle of a range of values between a specified minimum and maximum constraint. There are three arguments to the function: a minimum value, a preferred value, and a maximum allowed value.

MDN provides a quick visualisation of this with the demo below. Click on the examples to see the results. Depending on your screen resolution, you may or may not see the changes.

When you use clamp() for font sizes, you can specify a font-size that increases with the size of the viewport but never falls below a minimum font-size or exceeds a maximum font-size.

Considerations for Minimum and Maximum Sizes

So why not just use calc() and make the font size a direct proportion of the viewport?

There are a few reasons here:

  • For smaller fonts, you can risk them becoming illegible on smaller screens if you don't set a lower limit on how much it shrinks, particularly on mobile.
  • If you don't set an upper limit, your fonts will keep growing well beyond the acceptable range on wide screens. Think about how your site would look displayed on a 2000+px widescreen display.
  • If your site has a maximum display width (such as this one), it doesn't make sense to keep growing the fonts beyond that range.

Determining the clamp() Parameters (the long route with explanation)

Determining Minimum and Maximum Values

For a single font style, the obvious thing to do is to use the Inspect panel with browser resolution at reasonable desktop value and set the style on a text element or class to see the effect and find your maximum value. Similarly, use the mobile preview option in Inspect to visually determine the best minimum font size.

To calculate the minimum and maximums across a whole body/heading suite of sizes, I find it best to determine <body> and <h1> then use Excel to fill in the <h2> to <h5> values using the fill series feature to create a linear progression between the two sizes. I treat <h6> as a subscript size separately.

Example Minimum/Maximum Font Sizes

Font Element h6 body h5 h4 h3 h2 h1
Minimum 0.90 1.00 1.20 1.40 1.60 1.80 2.00
Maximum 1.00 1.10 1.45 1.80 2.10 2.45 2.75

Font sizes in rem

This gives me a set of font sizes that looks like the following:

graph showing the range of minimum and maximum font sizes for standard text elements
Font sizes in rem

Our goal now is to create a set of clamp declarations for each text element that will return a font size either on, or between, the minimum and maximum value depending on the width of the viewport.

Calculating the Clamp Preferred Size

If this is getting a bit too mathsy, or just TMI, you can skip directly to the calculator tool below.

Step 1: Determine Minimum & Maximum Font Sizes and Viewports

We should know our minimum and maximum font sizes by now, as determined above.

  • The minimum viewport I normally take to be 380px, an arbitrary figure for a small mobile.
  • For maximum, it really depends on your site design and more. For this site, as mentioned above, the content column grows to a maximum of 1800px. After that, the remainder is white space to either side. In this case, 1800px is the widest viewport I want the font to be variable over.
  • This in itself is arbitrary. You can set your minimum and maximum width at any point suitable for your website, it doesn't need to be the viewport range that your site will be viewed at.

Step 2: Convert Font Sizes and Widths to rem

Why use rem? That's a long discussion, but well explained here. In short, the standard browser setting uses 16px/rem. The vast majority of visitors will have this. However, anyone with visual impairment for example might have this set higher to 20px/rem or more, or perhaps it's a quirk of the device being used. Specifying font by pixels will display the font at the same size no matter what, whereas rem will scale according to browser setting.

Why use rem and not em? The problem with em is that it uses the parent element's font-size, so you can end up in all sorts of mess. Use rem for consistency.

In the example below, I'll assume 16px per rem. The calculator tool below will read your browser setting.

fa-solid fa-triangle-exclamation Warning
If the rem-to-pixel ratio is changed in the browser while viewing your site, the page will need reloading, it will not resize in response to this change. Listening for this event would take an unreasonable amount of overhead for the remote chance that this happens.

Step 3: Determine the Formula for the Preferred Font Size

With our values for minimum and maximum font and viewport sizes, we can plot these on a graph and determine slope and intercept. With that, using linear interpolation, we can determine preferred font size for any viewport width. To do this, we need to calculate slope and y-intercept (the hypothetical font size if we had a zero-width viewport).

Taking our <h1> example from above, our minimum font size is 2rem @ 380pixels \((380/16 = 23.75rem)\) and 2.75rem @ 1800pixels \((1800/16 = 112.5rem)\)

The slope is given by:

\[slope=\frac{\left({\rm FontSize}_{max}\ -\ {\rm FontSize}_{min}\right)}{\left({\rm Width}_{max}\ -\ {\rm Width}_{min}\right)}\]\[=\frac{(2.75-2)}{(112.5-23.75)}=0.0085\]

The y-intercept is given by:

\[y_{intercept}={\rm FontSize}_{min}-\left({\rm Width}_{min}\times slope\right)\]\[=\ 2-\left(23.75\times0.0085\right)=\ 1.799\]

Plotting this on a graph in Excel we can visualise this and confirm our calculations are correct:

graph plotting minimum and maximum font sizes with viewport sizes to calculate preferred font size
Calculating the preferred font size for the h1 tag

Step 4: Create the Font Size clamp() Function

Because viewport vw is described as a percentage, we need to multiply the slope by 100 to achieve our clamp function:

\[{\rm FontSize}_{preferred}=y_{intercept}+\left(slope\times100\right)vw\]\[=1.799rem+0.85vw\]

So our <h1> definition becomes:

.h1, h1 {
  font-size: clamp(2rem, 1.799rem + 0.85vw, 2.75rem);
}

Using root Variables to Declare Font Sizes

It's really better practice to declare your font families and sizes as variables in the root of your css then reference those everywhere else. This way, you're only ever setting them once and any changes are reflected throughout your site.

For example the root, and subsequent <body> definition, for this site is defined as:

:root {
  --font-family-headings: "Quicksand", sans-serif;
  --font-family-body: "Work Sans", sans-serif;
  --font-family-monospace: "Roboto Mono", "Courier New", monospace;
  --font-size-1: clamp(2rem, 1.7993rem + 0.8451vw, 2.75rem);
  --font-size-2: clamp(1.8rem, 1.6261rem + 0.7324vw, 2.45rem);
  --font-size-3: clamp(1.6rem, 1.4662rem + 0.5634vw, 2.1rem);
  --font-size-4: clamp(1.4rem, 1.293rem + 0.4507vw, 1.8rem);
  --font-size-5: clamp(1.2rem, 1.1331rem + 0.2817vw, 1.45rem);
  --font-size-6: clamp(0.9rem, 0.8732rem + 0.1127vw, 1rem);
  --font-size-body: clamp(1rem, 0.9599rem + 0.169vw, 1.15rem);
}
body {
  margin: 0;
  font-family: var(--font-family-body);
  font-size: var(--font-size-body);
  font-weight: 400;
  line-height: 1.5;
}
 

Variable Font Size Clamp Calculator

The good news is that you don't need to fiddle about with all that calculating each time. Use the handy tool below to instantly visualise your minimum, preferred and maximum font sizes and get the ready made clamp formula to paste into your CSS.


Viewport Width

Select Viewport Width Range to Scale Font Over (pixels)

Font Size

Select Font Size Range Minimum and Maximum (rem)

Rendered Font Sizes - rendered at

Minimum Font Size

Dynamic Font Size

Maximum Font Size

CSS Code (Click code to copy)

font-size: clamp

 


Conclusion

This article covered using the CSS clamp() function to easily create responsive font sizes that scale linearly with the viewport, including:

  • the advantage of using clamp(), with it's minimum and maximum value, over calc()
  • determining minimum and maximum font sizes, and over which viewport range to scale the font over
  • how to calculate the preferred (scaled) font size

At the end, I provided a simple tool to help calculate your clamp() values.

fa-solid fa-circle-info Although this article concentrated on font sizes, you could easily use the same technique for <div>, <image> and more. The only difference in the calculation methodology is that you would use pixels rather than rem as these objects would require an absolute dimension which pixels give us.

  Please feel free to leave any questions or comments below, or send me a message here