Scaling the digital type across mediums has always been a difficult problem. In this post, we will talk about an intrinsically scaling typographic system that we have discovered while building our css framework.
As you'll see, this new system resolves most, if not all, of the challenges faced by a web developer. Let's start by solving this meme first:
CSS IS AWESOME!
We'll prevent the overflow and scale the type dynamically according to the dimensions of the element. Consider the following HTML:
<div class="box">
<p>CSS</p>
<p>is</p>
<p>awesome.</p>
</div>
I didn't have to use three separate p
tags, but it offered additional control, so I just went with it.
Now we can start applying the styles like so:
@media only screen and (orientation: portrait) {
:root {
--vmin: calc(100vw/100);
}
}
@media only screen and (orientation: landscape) {
:root {
--vmin: calc(100vh/100);
}
}
:root {
--vmin: 1vmin;
}
From the code above, you can make out that it is our Orientation Query, and it demonstrates the mathematical relationship between the vmin
and vw
/vh
units. vmin
continuously points to the shorter side of the rectangular screen no matter what the orientation of viewing is.
Adopting the vmin.
From the code above, we can draw the following two axioms:
1st axiom:
100vh
in landscape mode =100vw
in portrait mode.
2nd axiom:
vmin
is always the shorter side of the rectangle.
The first axiom is very simple. vw
and vh
units are interrelated and can be swapped easily according to viewport orientation. One can say that device orientation is intrinsic to viewport units.
The second axiom implies that a typographic unit based on the short side of the screen has the lowest delta, i.e., the least variation upon resizing the browser. Even as the window is resized to the point where the orientation is switched—from landscape to portrait—the vmin
unit continues to be the lower of the two values smoothly, and it does so with the absolute minimum change in the value. Of course, vmax
does the opposite—it reflects the maximum possible change in the unit value.
Returning to the meme problem, we know the box around the text is square. This means that its sides are equal. Also, remember—in the world of resizable rectangles, a square is that geometrical point of inflection where the viewport orientation flips.
*, *:after, *:before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
@media only screen and (orientation: portrait) {
:root {
--vmin: calc(100vw/100);
}
}
@media only screen and (orientation: landscape) {
:root {
--vmin: calc(100vh/100); /* 100vh of landscape === 100vw of portrait. */
}
}
:root {
--vmin: 1vmin;
--side: calc(50 * var(--vmin)); /* dimensions of the square. */
--border: calc(2 * var(--vmin)) solid black; /* Outline for the square. */
}
.box {
width: var(--side);
height: var(--side);
border: var(--border);
…
}
This html and css will paint an empty square box on the page with a thick black border, just as it is on the meme.
Scoping the Font Size & Line Height.
Now let's apply an intrinsic font-size and line-height to the text within the box. To do so, we introduce two new variables, --fs
& --lh,
for font size and line height, respectively:
…
:root {
--vmin: 1vmin;
--side: calc(50 * var(--vmin)); /* dimensions of the square. */
--border: calc(2 * var(--vmin)) solid black; /* Outline for the square. */
--fs: calc(var(--side) / 6 ); /* Why the number 6, can you tell? */
--lh: calc(var(--fs) * 1.5);
}
.box {
width: var(--side);
height: var(--side);
border: var(--border);
…
}
.box p {
font-size: var(--fs);
line-height: var(--lh);
}
That's it. Our meme is now entirely resolved. And from now on, our community of css lovers can live free from this stain of incapacity css has had for such a long time. 🙏🏻
Dropping the Orientation Query.
The complete solution is as follows:
/* Baseline reset */
*, *:after, *:before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
@media only screen and (orientation: portrait) {
:root {
--vmin: calc(100vw/100);
}
}
@media only screen and (orientation: landscape) {
:root {
--vmin: calc(100vh/100); /* 100vh of landscape === 100vw of portrait. */
}
}
:root {
--vmin: 1vmin;
--side: calc(50 * var(--vmin)); /* dimensions of the square. */
--border: calc(2 * var(--vmin)) solid black; /* Outline for the square. */
--fs: calc(var(--side) / 6 ); /* Why the number 6, can you tell? */
--lh: calc(var(--fs) * 1.5);
}
.box {
width: var(--side);
height: var(--side);
border: var(--border);
…
}
.box p {
font-size: var(--fs);
line-height: var(--lh);
}
If you observe the code above, the orientation query (from lines 8 to 18) isn't doing anything. We can remove it.
With this vmin
based solution, the text in the box will scale correctly with sub-pixel accuracy no matter how much we resized the browser or even re-oriented the device. The scaling is so good that not even reflow isn't triggered on any of the modern browsers except maybe on Safari, where it works for the most part but has a little quirky behavior.
Feel free to test the demo.
Conclusion
Do you like this technique of scaling content with a simple vmin
unit of css? I love the simplicity of this solution and its ability to help us avoid using combination of multiple css units to achieve the same/similar effect. It also helps me avoid using hardcoded media queries, hardcoded clamp(),
and the unwieldily syntax of container queries, which requires yet another combination of alternative css units.
vmin
is simple. And I think vmin
can rule the entire space of Intrinsic Typography in the future. What are your thoughts? Have you tried web designing with vmin
? If not, that's exactly what we'll talk about in our next post by extending our solution above to a more generic layout. Hang on tightly!