Can type scale fluidly with screen size?
What is fluid type?
Type design on the web can be separated into two categories: static and responsive.
Static type ignores the dimensions of its screen; its characteristics are independent of it.
Responsive type responds to the dimensions of its screen and adjusts its characteristics based off of them.
Responsive type can be further categorized into adaptive and fluid.
Adaptive type only adjusts its characteristics at certain screen dimensions, called breakpoints.
Fluid type adjusts its characteristics in proportion to screen dimensions constantly.
Adjust your window’s width to see how each of these three categories of type behave under changing screen width (all three are identical at screen widths > 1600px):
[codepen:/mikevonwang/pen/OjbPgE]
Why would someone use fluid type?
Fluid type will always stay at a constant cpl, and therefore at a constant aspect ratio. Any layout which requires a block of text to stay at the same aspect ratio (text boxes next to images, for example) without scrolling, wrapping, or truncating would require the use of fluid type.
Plus, using fluid type in a website always gets some rad street cred.
Why would someone not use fluid type?
The central goal for typesetters is readability. To that end, font size and characters per line (cpl) contribute the greatest. As a general rule, body text should be twixt 50-75 characters per line, and 14-16px tall.
Static text quickly drops below optimal cpl as screen width decreases, but its constant font size means that the text is always legible. Adaptive and fluid text, by contrast, stay close to optimal cpl at most screen sizes, but their decreasing font size also means that the text quickly becomes unreadable and then illegible.
This is the key drawback of fluid type. For type set at a large size (titles, headers), this issue is not significant. For body text that is already at a small font size, this issue quickly prevents fluid type from being the most effective choice.
How does someone create fluid type?
The key to fluid type is linear interpolation. If we know two input/output pairs (x0, y0) and (x1, y1), and a desired input value x, we can draw a line twixt the two pairs, and calculate the output value y corresponding to x from that line. The formula for this calculation is:
y = y0 + (x - x0) * ((y1 - y0) / (x1 - x0));
For example, if we know that at a screen width of 1000px, a block of text should have a font size of 24px, and at 500px, it should have a font size of 16px, we can use the following CSS rule to describe this behavior:
.fluid-type {
font-size: calc(0% + 16px + (100vw - 500px) * ((24 - 16) / (1000 - 500)));
}
Note the presence of 0%
at the beginning of the calc()
function. This is necessary, because Safari will not allow viewport units inside calc()
functions unless at least one operand has units of percent.
Also note the absence of units from the last four numeric values in the calc()
function. This is necessary, because calc()
does not allow multiplication or division by non-scalar values. The 2nd numeric value can have any length unit, but px
should be used for consistency.
This CSS rule applies to all screen widths, not just to screen widths twixt 500px and 1000px. In fact, any two input/output pairs that lie on the line between (500px, 16px) and (1000px, 24px) could be selected, and the rule would function identically. For example, if we changed 500px to 750px and 16px to 20px, the new rule would be indistinguishable from the previous:
.fluid-type {
font-size: calc(0% + 20px + (100vw - 750px) * ((24 - 20) / (1000 - 750)));
}
In order to restrict the rule to certain minimum/maximum font sizes, one would have to use media queries. For example, to restrict the text from the previous example to a minimum font size of 16px and a maximum of 24px, we can use the following CSS rules:
.fluid-type {
font-size: 24px;
}
@media (max-width: 1000px) {
.fluid-type {
font-size: calc(0% + 16px + (100vw - 500px) * ((24 - 16) / (1000 - 500)));
}
}
@media (max-width: 500px) {
.fluid-type {
font-size: 16px;
}
}
If we use SCSS, we can write functions to do linear interpolation for us, so we don’t have to write out the formulae for every single piece of fluid type we want to implement.
@function st($n) {
@if type-of($n) == 'number' and not unitless($n) {
@return $n / ($n * 0 + 1);
}
@else {
@return $n;
}
}
@function li($x0, $x1, $y0, $y1, $x) {
@return calc(0% + #{$y0} + (#{unquote($x)} - #{$x0}) * (#{st($y1 - $y0)} / #{st($x1 - $x0)}));
}
st()
strips an input of its unit, i.e. turns it into a scalar.
li()
performs linear interpolation. Note that the first four inputs must all have the same units, preferably px
.
Note the presence of the unquote()
function within li()
. This is necessary if we wish to use a calc()
function for the value of $x
, because Safari does not allow nested calc()
functions. Values for $x
that are not calc()
functions can be passed in as is, but values that are need to be passed in as a string, i.e. '(((100vw - 80px) / 3) - 60px)'
.
Now, we can simply call li()
to implement fluid type:
.fluid-type {
font-size: 24px;
}
@media (max-width: 1000px) {
.fluid-type {
font-size: li(500px, 1000px, 16px, 24px, 100vw);
}
}
@media (max-width: 500px) {
.fluid-type {
font-size: 16px;
}
}