Container Queries, Responsive Grids, and Fluid Typography
Introduction
This is part guide and part experiment. We'll explore how container queries, the CSS clamp function, and CSS grid, can be combined to create a range of fluid text styles, which are enhanced with a container query.
Example
It's a familiar sight, text in boxes, spanning columns. What's different about this example is that I haven't used media queries to change font sizes, and the same code is used for each individual box of text, regardless of how wide it is, spooky.
The typical approach
Typically, you'd use a combination of media queries and fluid typography to achieve what's happening in the example, e.g.
The example doesn't give any control over what happens with the boxes that don't span the full 12 columns, so you'd also use modifier classes specifically for those cases, e.g.
Or this kind of utility-based approach:
I like how the code can be defined in blocks, but it becomes repetitive, that's due to the use of media queries.
What compounds things is that there's a similar approach with spacing units and every other part of the system, so what you're seeing here is the tip of the iceberg.
Syncing all elements of a design system with every media query is a drain on time.
Text may appear too small on some screens and too large on others.
Let's try another approach.
Using container queries
Define some static font sizes as custom properties
(I always use rem
units for accessibility),
so we can re-use them, I usually add them after any CSS resets.
These values usually come from a design system or a figma file, or you can make up your own.
Create a class, I called it .content
for obvious reasons,
then select the elements you want to apply the responsive font-sizes to.
- I added
container-type: inline-size;
to the parent element. - I then selected elements I want to apply styles to, they have to be children of the container, inb this case it's
p
,ul
,ol
. - Finally, I used a
@container
query to use thecqw
unit, unless you're within a container this has no effect, it let's you get the size of the container and size the text dynamically based on the width of it.
Caution
Always test the effectiveness of using cqw
units for font-size
by zooming (using ctrl + / -). They can cause accessibility failures.
I found using unitless values, e.g. line-height: 1.5
,
the most sensible way
to handle line-height
with fluid & responsive typography.
Unitless values are multipliers,
if you have font-size: 1rem
and apply line-height: 1.5
the computed value of line-height
will be 24px
.
The way I do this is not an exact science (no, I'm not using a graph), and usually "by-eye", so the mid-range values I've used are based on what feels right.
Once you implement that code, to see it take effect, you'll need to change the widths of the containers.
Add containers to a grid
Drop the containers onto a CSS grid, you don't need anything fancy, this kind of thing will do the trick:
I wrote an in-depth guide on grid systems: How to Create your own CSS Grid, Grid System, and something less in-depth here: Responsive CSS Grids, Two Ways
Then, write CSS that places the containers within the columns you want them to appear in. For my example, I created several utility classes.
You might be thinking, hang-on we're using media queries again; either way, we still need a way to lay out the containers on a grid, use whatever method works best.
Final touches
For a bit of variety with the headings, we can create utility classes.
We don't need to touch the paragraphs again as the initial .box
class has container queries that handle that.
To apply fluid spacing between the paragraphs and headings,
we can tap into the cqw
unit again,
and use clamp()
to apply spacing proportionate to the size of the text in each container.
The min and max values could also come from a series of custom properties from a design system.
Conclusion
I'm loosely adhering to a design system, this is intentional,
I don't think
we should get too hung up on absolute perfection with intermediary font-sizes using clamp()
,
but we definitely don't want any irregularities,
just use the static values and go with what feels right in-between.
We've looked at how a typical setup for fluid and responsive typography would typically work, and then seen an alternative, which I prefer, you might too (if you do, I'd love to hear from you).
Always use rem
units to avoid user zoom accessibility issues,
zoom in to test your solution using the browser to make sure.
If you have any suggestions as to how the examples can be made clearer, easier to understand, or how I can add any missing use-cases you feel would help others, I'd love nothing more than to hear from you! Reach out to me on Twitter, or shoot me an email.
Written by Morgan Feeney
I’ve been designing and developing for almost 2 decades.
Read about me, and my work.
For juicy tips and exclusive content, subscribe to my FREE newsletter.