The vary syntax isn’t a brand new factor. We‘re already in a position to make use of it with media queries to question viewport dimensions and resolutions, in addition to container dimension queries to question container dimensions. Having the ability to use it with container type queries — which we will do beginning with Chrome 142 — implies that we will evaluate literal numeric values in addition to numeric values tokenized by customized properties or the attr() perform.
As well as, this characteristic involves the if() perform as nicely.
Right here’s a fast demo that reveals the vary syntax being utilized in each contexts to match a customized property (--lightness) to a literal worth (50%):
#container {
/* Select any worth 0-100% */
--lightness: 10%;
/* Applies it to the background */
background: hsl(270 100% var(--lightness));
shade: if(
/* If --lightness is lower than 50%, white textual content */
type(--lightness < 50%): white;
/* If --lightness is greater than or equal to 50%, black textual content */
type(--lightness >= 50%): black
);
/* Selects the youngsters */
* {
/* Particularly queries mother and father */
@container type(--lightness < 50%) {
shade: white;
}
@container type(--lightness >= 50%) {
shade: black;
}
}
}
Once more, you’ll need Chrome 142 or increased to see this work:
Each strategies do the identical factor however in barely alternative ways.
Let’s take a better look.
Vary syntax with customized properties
Within the subsequent demo arising, I’ve minimize out the if() stuff, leaving solely the container type queries. What’s taking place right here is that we’ve created a customized property referred to as --lightness on the #container. Querying the worth of an abnormal property isn’t potential, so as a substitute we put it aside (or part of it) as a customized property, after which use it to kind the HSL-formatted worth of the background.
#container {
/* Select any worth 0-100% */
--lightness: 10%;
/* Applies it to the background */
background: hsl(270 100% var(--lightness));
}
After that we choose the container’s youngsters and conditionally declare their shade utilizing container type queries. Particularly, if the --lightness property of #container (and, by extension, the background) is lower than 50%, we set the shade to white. Or, if it’s greater than or equal to 50%, we set the shade to black.
#container {
/* and many others. */
/* Selects the youngsters */
* {
/* Particularly queries mother and father */
@container type(--lightness < 50%) {
shade: white;
}
@container type(--lightness >= 50%) {
shade: black;
}
}
}
/rationalization Notice that we wouldn’t be capable to transfer the @container at-rules to the #container block, as a result of then we’d be querying --lightness on the container of #container (the place it doesn’t exist) after which past (the place it additionally doesn’t exist).
Previous to the vary syntax coming to container type queries, we might solely question particular values, so the vary syntax makes container type queries rather more helpful.
In contrast, the if()-based declaration would work in both block:
#container {
--lightness: 10%;
background: hsl(270 100% var(--lightness));
/* --lightness works right here */
shade: if(
type(--lightness < 50%): white;
type(--lightness >= 50%): black
);
* {
/* And right here! */
shade: if(
type(--lightness < 50%): white;
type(--lightness >= 50%): black
);
}
}
So, on condition that container type queries solely look up the cascade (whereas if() additionally appears to be like for customized properties declared throughout the similar CSS rule) why use container type queries in any respect? Properly, private desire apart, container queries permit us to outline a particular containment context utilizing the container-name CSS property:
#container {
--lightness: 10%;
background: hsl(270 100% var(--lightness));
/* Outline a named containment context */
container-name: myContainer;
* {
/* Specify the title right here */
@container myContainer type(--lightness < 50%) {
shade: white;
}
@container myContainer type(--lightness >= 50%) {
shade: black;
}
}
}
With this model, if the @container at-rule can’t discover --lightness on myContainer, the block doesn’t run. If we needed @container to look additional up the cascade, we’d solely have to declare container-name: myContainer additional up the cascade. The if() perform doesn’t permit for this, however container queries permit us to manage the scope.
Vary syntax with the attr() CSS perform
We will additionally pull values from HTML attributes utilizing the attr() CSS perform.
Within the HTML under, I’ve created a component with a knowledge attribute referred to as data-notifs whose worth represents the variety of unread notifications {that a} person has:
We need to choose [data-notifs]::after in order that we will place the quantity inside [data-notifs] utilizing the content material CSS property. In flip, that is the place we’ll put the @container at-rules, with [data-notifs] serving because the container. I’ve additionally included a peak and matching border-radius for styling:
[data-notifs]::after {
peak: 1.25rem;
border-radius: 1.25rem;
/* Container type queries right here */
}
Now for the container type question logic. Within the first one, it’s pretty apparent that if the notification depend is 1-2 digits (or, because it’s expressed within the question, lower than or equal to 99), then content material: attr(data-notifs) inserts the quantity from the data-notifs attribute whereas aspect-ratio: 1 / 1 ensures that the width matches the peak, forming a round notification badge.
Within the second question, which matches if the quantity is greater than 99, we swap to content material: "99+" as a result of I don’t assume {that a} notification badge might deal with 4 digits. We additionally embody some inline padding as a substitute of a width, since not even three characters can match into the circle.
To summarize, we’re mainly utilizing this container type question logic to find out each content material and elegance, which is admittedly cool:
[data-notifs]::after {
peak: 1.25rem;
border-radius: 1.25rem;
/* If notification depend is 1-2 digits */
@container type(attr(data-notifs kind()) <= 99) {
/* Show depend */
content material: attr(data-notifs);
/* Make width equal the peak */
aspect-ratio: 1 / 1;
}
/* If notification depend is 3 or extra digits */
@container type(attr(data-notifs kind()) > 99) {
/* After 99, merely say "99+" */
content material: "99+";
/* As a substitute of width, a bit padding */
padding-inline: 0.1875rem;
}
}
However you’re seemingly questioning why, once we learn the worth within the container type queries, it’s written as attr(data-notifs kind( as a substitute of attr(data-notifs). Properly, the reason being that once we don’t specify a knowledge kind (or unit, you possibly can learn all concerning the latest modifications to attr() right here), the worth is parsed as a string. That is fantastic once we’re outputting the worth with content material: attr(data-notifs), however once we’re evaluating it to 99, we should parse it as a quantity (though kind( would additionally work).
The truth is, all vary syntax comparatives have to be of the identical information kind (though they don’t have to make use of the identical items). Supported information sorts embody , , , , , , and . Within the earlier instance, we might really specific the lightness with out items for the reason that fashionable hsl() syntax helps that, however we’d must be in keeping with it and make sure that all comparatives are unit-less too:
#container {
/* 10, not 10% */
--lightness: 10;
background: hsl(270 100 var(--lightness));
shade: if(
/* 50, not 50% */
type(--lightness < 50): white;
type(--lightness >= 50): black
);
* {
/* 50, not 50% */
@container type(--lightness < 50) {
shade: white;
}
@container type(--lightness >= 50) {
shade: black;
}
}
}
Notice: This notification depend instance doesn’t lend itself nicely to if(), as you’d want to incorporate the logic for each related CSS property, however it's potential and would use the identical logic.
Vary syntax with literal values
We will additionally evaluate literal values, for instance, 1em to 32px. Sure, they’re completely different items, however bear in mind, they solely must be the identical information kind and these are each legitimate CSS s.
Within the subsequent instance, we set the font-size of the aspect to 31px. The inherits this font-size, and since 1em is the same as the font-size of the guardian, 1em within the scope of can also be 31px. With me up to now?
In line with the if() logic, if 1em is the same as lower than 32px, the font-weight is smaller (to be exaggerative, let’s say 100), whereas if 1em is the same as or larger than 32px, we set the font-weight to a chunky 900. If we take away the font-size declaration, then 1em computes to the person agent default of 32px, and neither situation matches, leaving the font-weight to additionally compute to the person agent default, which for all headings is 700.
Mainly, the thought is that if we mess with the default font-size of the , then we declare an optimized font-weight to keep up readability, stopping small-fat and large-thin textual content.
h1 {
/*
The default worth is 32px,
however we overwrite it to 31px,
inflicting the primary if() situation to match
*/
font-size: 31px;
span {
/* Right here, 1em is the same as 31px */
font-weight: if(
type(1em < 32px): 100;
type(1em > 32px): 900
);
}
}
CSS queries have come a great distance, haven’t they?
In my view, the vary syntax coming to container type queries and the if() perform represents CSS’s greatest leap by way of conditional logic, particularly contemplating that it may be mixed with media queries, characteristic queries, and different varieties of container queries (bear in mind to declare container-type if combining with container dimension queries). The truth is, now can be a nice time to clean up on queries, in order a bit parting reward, listed below are some hyperlinks for additional studying:








