Using CSS (only!) and Math for Fluid Font Size

K Hughes
8 min readOct 25, 2021

--

Responsive Font Size

In responsive web design, we use media query breakpoints in order to make a website’s content respond in a certain way depending on the viewport’s width.

Consider the following example of responsive font size:

@media (max-width: 414px) {
h1 {
font-size: 16px;
}
}
@media (min-width: 415px) and (max-width: 768px) {
h1 {
font-size: 32px;
}
}
@media (min-width: 769px) {
h1 {
font-size: 48px;
}
}

The CSS defines the font-size property of the h1 element when the viewport is at various breakpoints.

  • For viewports up to 414px wide, the font size is 16px.
  • For viewports between 415px and 768px, the font size is 32px.
  • For viewports of at least 769px wide, the font size is 48px.

This responsive approach causes the font size to “jump” from 16px to 32px when the viewport’s width goes from 414px to 415px. This “jump” from one font size to another also happens when the width goes from 768px to 769px, or from 769px to 768px, or from 415px to 414px.

Basic Fluid Font Size

One way to avoid this “jump” side effect (and achieve a more “fluid” effect, instead) is to use the viewport’s current width — as opposed to using predefined breakpoints — when setting the font size. We can do this by using CSS’s viewport unit for width: vw.

There are four viewport-based units in CSS:

  • vw
  • vh
  • vmin
  • vmax

vw is a relative length unit representing a percentage of the viewport’s width. For example, 50vw is 50% of the viewport’s width. So, given a viewport that is 1280px wide, 50vw means 50% of that 1280px, which is 640px.

You can learn more about viewport-based units on the MDN Web Docs.

Let’s update our example and set the font-size property of the h1 element to 10vw:

h1 {
font-size: 10vw;
}

As the viewport’s width changes, the value of the font-size property adjusts proportionally:

  • When the viewport’s width is 400px, the font-size property is set to 10% of 400px, which is 40px.
  • When the viewport’s width is 800px, the font-size property is set to 10% of 800px, which is 80px.

The changes to the font size will now present with fluidity.

Intermediate Fluid Font Size

One drawback to setting the value of the font-size property to a vw unit is precision control. What if you wanted the font size to be:

  • 32px when the viewport’s width is 414px,
  • 48px when the viewport’s width is 768px, and
  • proportionally in between 32px and 48px when the viewport’s width is in between 414px and 768px?

A simple assignment of a vw value to the font-size property is no longer an option. Instead, we need to use a combination of CSS’s calc() function and a couple of mathematical formulas.

According to the MDN Web Docs,

The calc() CSS function lets you perform calculations when specifying CSS property values.

And for the purpose of fluid font size, the calc() function will suffice.

The first mathematical formula we need to be aware of is the point-slope form equation of a line:

y - y₁ = m(x - x₁)

Where:

y = y coordinate of the second point

y₁ = y coordinate of the first point

m = slope of the line formed by the two points with coordinates (x, y) and (x₁, y₁)

x = x coordinate of the second point

x₁ = x coordinate of the first point

y represents the unknown font size for a given viewport width (x). The coordinates (x₁, y₁) represent any given point on the line.

Before we can use the point-slope form, we first need to compute what slope m is. The formula to calculate slope m (also known as the gradient of a line) is:

m = (y₂ - y₁) / (x₂ - x₁)

Where:

m = slope of the line formed by the two points (x₁, y₁) and (x₂, y₂)

(x₁, y₁) = coordinates of the first point

(x₂, y₂) = coordinates of the second point

We want our font size to be 32px when the viewport is 414px wide. So, let:

x₁ = 32px
y₁ = 414px

Thus, the coordinates of our first point are:

(414, 32)

We want our font size to be 48px when the viewport is 768px wide. So, let:

x₂ = 768px
y₂ = 48px

Thus, the coordinates of our second point are:

(768, 48)

Now, let’s plug in the given values to the formula m = (y₂ — y₁) / (x₂ — x₁) to compute our slope:

m = (48 - 32) / (768 - 414)
m = 16 / 354
m = 0.04519774011

Now that we have the value of slope m, we can get back to our point-slope form:

y - y₁ = m(x - x₂)

Note that the coordinates (x₁, y₁) represent a point on the line with a slope m = 0.04519774011 (based on our previous calculations).

Using the formula y — y₁ = m(x — x₁), we can now solve for the unknown font size (y) when viewport width (x) is, for example, 591px (which is halfway between 414px and 768px; we can intuitively estimate that the unknown font size (y) should therefore be halfway between 32px and 48px):

y - 32 = 0.04519774011 (591 - 414)
y - 32 = 0.04519774011 (177)
y - 32 = 7.9999999977
y = 7.9999999977 + 32
y = 39.9999999977

As expected, font size (y) is halfway between 32px and 48px.

Now, how do we use all this math in CSS to achieve fluid font size? This is where the CSS calc() function comes in.

Let’s update our example to use CSS’s calc() function:

:root {
/* Declare without units so we can compute the slope */
--min-vw: 414;
--max-vw: 768;
--min-h1-font-size: 32;
--max-h1-font-size: 48;
/* m = (y2 - y1) / (x2 - x1) */
--rise: calc(var(--max-h1-font-size) - var(--min-h1-font-size));
--run: calc(var(--max-vw) - var(--min-vw));
--slope: calc(var(--rise) / var(--run));
}
h1 {
/* Due to the way addition and subtraction works in calc(), we
need to "type cast" --min-vw and --min-h1-font-size to
appropriate units */
--min-vw-px: calc(var(--min-vw) * 1px);
--min-h1-font-size-px: calc(var(--min-h1-font-size) * 1px);
/* y - y1 = m(x - x1) */
--fluid-font-size-px: calc(
var(--slope) * (100vw - var(--min-vw-px)) + var(--min-h1-font-size-px)
);
font-size: var(--fluid-font-size-px);
}

The key to making fluid font size work in our example is the use of 100vw for the value of x in the point-slope formula.

We have now achieved the precision control we were aiming for. When the viewport’s width is 414px, the font size is 32px. When the viewport’s width is 768px, the font size is 48px. Solving for any other viewport width (x) and font size (y) on the same line with a slope of m = 0.04519774011, we can use the point-slope formula y — 32 = 0.04519774011(x — 414).

Advanced Fluid Font Size

Another drawback of setting the font-size property to a vw unit is that this may result in extremely small or large font sizes. What if you wanted the font size to never go below 32px or above 48px?

In order to set boundaries for the font size, we need to use CSS’s clamp() function.

According to the MDN Web Docs,

the clamp() CSS function clamps a value between an upper and lower bound. clamp() enables selecting a middle value within a range of values between a defined minimum and maximum. [It] takes three comma separated expressions as its parameter, in the order of minimum value, preferred value, maximum value.

So, if we were to set the first parameter to 32px and the third parameter to 48px, we would have a font size that would never go below 32px or above 48px.

Let’s update our example to use CSS’s clamp() function:

:root {
/* Declare without units so we can compute the slope */
--min-vw: 414;
--max-vw: 768;
--min-h1-font-size: 32;
--max-h1-font-size: 48;
/* m = (y2 - y1) / (x2 - x1) */
--rise: calc(var(--max-h1-font-size) - var(--min-h1-font-size));
--run: calc(var(--max-vw) - var(--min-vw));
--slope: calc(var(--rise) / var(--run));
}
h1 {
/* Due to the way addition and subtraction works in calc(), we
need to "type cast" --min-vw and --min-h1-font-size to
appropriate units */
--min-vw-px: calc(var(--min-vw) * 1px);
--min-h1-font-size-px: calc(var(--min-h1-font-size) * 1px);
/* y - y1 = m(x - x1) */
--fluid-font-size-px: calc(
var(--slope) * (100vw - var(--min-vw-px)) + var(--min-h1-font-size-px)
);
/* "Type cast" --max-vw to px */
--max-h1-font-size-px: calc(var(--max-h1-font-size) * 1px);
font-size: clamp(
var(--min-h1-font-size-px),
var(--fluid-font-size-px),
var(--max-h1-font-size-px)
);
}

As you can see, when the viewport is 414px and below, the font size is 32px. And when the viewport is 768px and above, the font size is 48px. The font size is only fluid when the viewport’s width is between 414px and 768px.

Some Final Thoughts

--

--

K Hughes

Certified Agile Project Manager, Certified Agile Business Analyst, Senior Technologist