Like many blogs, this site is basically just text, so options for making it visually interesting are mostly limited to typography. I quite like using big, bold headlines for this, and think the effect at 3.5rem
on a large display is pretty nice.
However, that aspect of the design doesn’t work very well on a small display, and is just plain bad when the headline is long and/or includes long words:
I really want at least the first paragraph of content to be readable on a small display without having to scroll. Media queries could help, but in cases like this they’re a bit of a blunt instrument. I’d need to set one or more breakpoints at which the size would increase, accounting for both width and height, and there’d still be some sizes at which it wasn’t quite right.
CSS has other tools available to us in the form of functions, where we can compute values based on various other values, including the current font size and the viewport width and height.
After some experimentation, here’s what I have now:
h1 {
font-size: 2rem;
font-size: clamp(2rem, min(10vw, 10vh), 3.5rem);
line-height: 1em;
}
The 2rem
declaration is in as a safe fallback for older browsers; if the syntax of the next declaration isn’t recognised, it’ll be ignored and the 2rem
will stand.
The next declaration using a combination of clamp()
and min()
breaks down like this:
Use whichever is the smaller of 10% of the viewport width or 10% of the viewport height[1], but prevent it from being any smaller than 2x the root font size or any larger than 3.5x the root font size.
Finally, a line-height
of 1em
provides an appropriate leading based on the computed font size.
clamp()
, min()
and other similar functions are things you can’t do at build time with preprocessors, because the values are evaluated at runtime.
My example isn’t anything special, but I think the ability to nest functions, as well as their support for mixing units as I have done here, makes them very powerful for achieving precision in contexts that are both complex and dynamic, and adaptiveness in ways that aren’t tied to specific pixel breakpoints. This is especially true of calc()
, which (as I sometimes forget) is supported all the way back to Internet Explorer 9.
Stray observations:
- As you might expect, we have
max()
to go withmin()
- Though most examples show them used with two values,
min()
andmax()
can take as many different values as you’d like - Take care with
vh
; things like the disappearing browser chrome on iOS Safari can make it behave in unexpected ways, so I tend to avoid it for layout - but it’s great for things like this
The 10% number just came out of trial and error. ↩︎