Video summary
Fluid typography is a persistent trend in web design that offers dynamic scaling based on viewport size, yet it often encounters issues when content containers reach their maximum dimensions. The primary problem arises when text continues to grow even after its container has stopped expanding, creating an awkward visual effect where the font size exceeds the available space. This occurs because standard clamp functions rely on the viewport width rather than the specific container's size. To resolve this, developers can utilize container query units, which allow elements to scale relative to their immediate parent container instead of the entire screen. By explicitly defining a wrapper element as a container, the typography system correctly references that specific boundary, ensuring that font sizes stop growing precisely when the wrapper hits its limit, thus maintaining a clean and proportional layout.
However, implementing container queries across multiple nested elements introduces a new challenge regarding consistency. When different containers within a layout are defined as separate query scopes, declaring identical font size rules for each can result in inconsistent visual weights, which often causes anxiety among design system practitioners who prefer uniformity. For instance, if cards inside a wrapper are also defined as individual containers, they might calculate their own sizes independently, leading to mismatched typography even when the same CSS declaration is used. To address this inconsistency without sacrificing the benefits of container queries, the video introduces the use of registered custom properties via the `@property` rule. This approach allows developers to define a specific type for a property, such as a length, which ensures that the browser computes and stores a precise numerical value rather than treating it as a raw string or unitless number.
The solution involves registering a custom property with an initial value and setting inheritance to true, enabling the system to dynamically compute the correct font size based on the specific container context while maintaining visual consistency across different levels of the hierarchy. When a child element redeclares this registered property, the browser recognizes it as a computed length derived from the parent's container query units, effectively synchronizing the scaling behavior across the entire layout. This method allows for granular control where specific containers can override the default behavior if needed, while still inheriting the correct calculated values elsewhere. Although the syntax requires an extra step of registration and careful handling of inheritance rules, it provides a robust way to ensure that fluid typography remains both responsive to container changes and visually consistent throughout complex, multi-container layouts.
Read the full video transcript
Hello, my friend and friends. Fluid
typography on the web is something I
really like. I know not everyone's a
huge fan of it, but I think it's here to
stay even if you're not the biggest fan
of it, but there's some problems that
can come up when we're using it that I
think I found a fix for.
Let's look at the first problem here is
that as this is getting bigger, my font
size continues to grow even though I
don't necessarily want it to. The reason
I don't want it to is because my wrapper
here that I have has hit a maximum size,
so the space these cards are living in
in the title is living in isn't getting
any bigger, but the text is still
getting bigger through this like squishy
zone over here. And the reason that's
happening is because I'm using a clamp
to set a maximum size, but it's based on
the viewport size. And because it's
looking at the viewport inline, which is
the logical property for viewport width,
because it's looking at the viewport,
it's saying, "Well, the viewport
continues to get bigger. I haven't hit
my maximum value yet, so it sort of it
just keeps on getting bigger and it's
kind of ugly." The solution here is one
I've talked about before, which is to
use container query inline instead cuz
container units can see their container
sizes.
People don't like this. We'll get to why
they don't like this and then we'll get
to the fix. But even before we talk
about that, this hasn't actually fixed
anything yet because if I take a look
here, right now it's it's doing the
exact same thing where we still have
this squishy zone over here when my
container hits its maximum size.
And the reason that's happening is
because I when you switch to container
query units, unless you have a defined
container, they just look at the
viewport anyway. So, the very first
thing I'd have to do is to grab in this
case my wrapper and say that my wrapper
is a container. And this is why I don't
like the container
name for wrappers anymore. I use wrapper
cuz we have containers that are part of
the container query spec and all of
that. And then if you have a dot
container that's then I don't know. It
all gets weird. Name things however you
want. I call them wrappers and So,
wrapper is now a container with the name
of wrapper.
Uh but regardless of all of that and
however we're naming things, now
anything that's inside of my wrapper
will be looking at the size of the
wrapper for its container query inline
size, which means we've now fixed that
first problem. And we can see that when
we hit that and my wrapper gets to its
maximum size right there, the font sizes
all stop growing, which to me makes a
lot more sense than them getting bigger
and bigger and bigger even though their
space they're in doesn't continue to
grow. So, I like this and this works
pretty well. And if this is all you're
doing, you might not run into any more
problems. But, now that we have
containers, you might be using them in
multiple places. For example, I have
those cards that I had here and now I've
sort of changed the layout. Before they
were stacked and now they're going next
to each other. And if I shrink this
down, it's going to break the layout cuz
I do want them to stack at one point
where we have overflow. Oh my goodness,
they get all the way down here and then
they stack.
Uh and so ideally, what I would be doing
is actually using an at container to
look at the size available and have the
cards stack at you know when when they
reach too small of a size. The problem
is right now my only defined container
is the wrapper, which is the entire
layout. I want them to look at their
individual card sizes instead. So, now
what I can do is say that my card is
also a container. I fixed my layout
issues. We can see now that's actually
working and they'll stack and and grow
and it works the way I want it to. But,
the problem is now look at the
inconsistent font sizes that come up.
And this is the problem most people have
with container query units uh for font
sizes is it quickly leads to this type
of problem. And it is kind of weird that
we have the same declaration on all
three of them and the font sizes are
different in all three places.
A lot of people don't like this and I
understand why, especially like design
system people. This This I think gives
them anxiety looking at this type of
inconsistency we have that is going on
here right now. But, there is a
potential fix for this type of thing
now, as well, which is using @property.
But, before I get into how this works, I
just want to say a very big thank you to
Ana Tudor. I'm going to link to an
article she put out a little while ago
that I didn't know about until recently,
uh down in the description, that is what
gave me the idea for this solution. And
that's because with @property, we can
register custom properties, but it saves
their values in a different way than
unregistered custom properties do. So,
if you're not used to @property, the
first thing we need to do is give it a
name, the same way you would name any
custom property that you're doing. So,
my step two, exactly the same as I had
before.
But, with this, we do need three things.
We need to have in syntax, initial
value, and whether or not it inherits.
The syntax, just look up what you want.
In this case, I'd want it to be a
length. Also, the syntax highlighting
here is weird. Syntax highlighting with
@property, still triangle brackets in
CSS, it it it tends to look weird in a
lot of places that do syntax
highlighting, but this is completely
valid. It works in all browsers. Has
very good support right now. So, my
syntax is my length, and the reason I'm
doing a length is because my initial
value is going to be 1 rem. I would just
have something here that's a valid font
size. But, if it's not just a number,
like number would be 10, right? Unitless
number. But, if I want it to be rem or
pixels or anything like that, that's
actually a length. And whether or not it
inherits, in this case, I don't think it
matters, though I do think inherits true
No, I'm going to go back on that. I
think you really do want this to
inherit, cuz you If something is set on
that, you want it to also be able to be
set on its children.
There are times where inherits false is
actually pretty good. I'll link to a
video in the description about one of
those use cases uh that I came across,
where you actually want inherits to be
false, and it makes life a lot easier.
But yeah, once we've set this up, we've
now registered this custom property, and
that means that, well, I can have my
step two declared like this, I can then
do something like this. And this is
re-declaring the the step two as the
same thing that I have here. And this
might seem a little bit weird, but the
reason this is working is because it's a
registered custom property. If we go
back quickly, it knows that it's a
length. So, it knows it's going to be
getting an actual like computed number
value here. So, because it knows that,
knows it's a computed length, it's
actually going to compute this length
and save that number. And then we're
doing the same thing here. We're going
to compute this length and we're going
to save that number.
I am doing this on the children of my
wrapper. And this is important as well,
because what it's doing is because I
need it to look at the size of the
wrapper itself.
So, as the wrapper is changing, it's
going to continue to compute that
number. But unlike a regular custom
property that we just redeclare like
this value over and over again, it's
getting the calculated value of this and
saving that as my step two. And again,
that value can change as the viewport or
as the container size is changing, but
it's always going to be that final
result. So, by taking that extra step of
registering the custom property and then
setting this up where we're sort of
redefining step two right here,
that means now
all of our font sizes are consistent. I
have my container queries still set up.
So, as this shrinks down, we can see
that my layout is snapping and changing
over here. Uh but all three of those
font sizes are computing to the same
value, which is based on the container
size of the wrapper. And it's not
looking at the container size of the
container that we're in right here. And
it's a little bit weird, but we've now
sort of defined which container we're
going to be looking at for the container
query units. And this is what's so cool.
That's what Anna was talking about just
with a different use case in her blog
post that she wrote. Uh so yeah, super
useful to do this. And again, that means
that step two is now defined and set to
this wrapper.
Uh but it's kind of weird like
redeclaring things like this.
And sometimes you might also want to go
back to the initial one, right? You
might be like, "Oh, I don't want to be
using this one for whatever reason.
Like, I need to reset this along the
way." So, the way that we can do that is
actually to change this to like a step
to reset, or you might have to declare
it twice. You'd have your step two in
your root and your step two reset. And
this is where it's a little bit weird,
but this would be an on-registered
custom property. And by being an
on-registered custom property, it means
this isn't getting a computed value.
It's just saving this entire string as a
value right there. And that means we
could do something like this, where I
mean, it makes it a little bit easier to
declare it instead of doing the entire
declaration every single time. And so,
like, really quickly, this is working
exactly as it was working before. We
haven't actually made a change at this
point. But, what this means is not only
can we have it set here, if you did need
it to really be a container query unit
for that specific container, where you
can do that, where my wrapper can set
step two, and then I can also then go on
my card, where I have a container, I can
redeclare what step two is, and then
it's going to be set for the card. And
remember I said inherits true being on
is good. And the reason for that is I'm
only declaring this on the direct
children of my wrapper, but then that
value would inherit into anywhere that's
using it. Uh the same way here, then my
card, we've reset it, but then it's
going to inherit into all of its
children as well. And so, with this
reset on the cards now, we can see we
have the inconsistent sizing that has
come back there. Not something I'd want,
and if you always want this to happen, I
wouldn't go through all this trouble of
registering it in the first place. And
so, like, this probably isn't the best
use case for how you would actually use
the reset, but I wanted to show that it
was something that was actually
possible. Uh I think there's a lot of
different ways and interesting ways that
we could do something like this, and I
hope you like this trick. It's a little
bit of a weird one. Takes a bit of time
for your mind to wrap around it, but I
think it could be really useful. So, I
do hope that you enjoyed this video. Uh
if you want the one where I looked at
why you'd want inherits false, the video
is right here for your viewing pleasure
and linked down in the description. And
with that I'm going to say a very big
thank you to my enabler of awesome
Johnny as well as all my other patrons
and channel members for their continued
support. And of course, until next time,
don't forget to make your corner of the
internet just a little bit more awesome.