Thursday, November 13, 2025

The Vary Syntax Has Come to Container Model Queries and if()


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. With the ability to use it with container model queries — which we are able to do beginning with Chrome 142 — signifies that we are able to evaluate literal numeric values in addition to numeric values tokenized by customized properties or the attr() operate.

As well as, this characteristic involves the if() operate as nicely.

Right here’s a fast demo that exhibits 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));

  coloration: if(
    /* If --lightness is lower than 50%, white textual content */
    model(--lightness < 50%): white;
    /* If --lightness is greater than or equal to 50%, black textual content */
    model(--lightness >= 50%): black
  );

  /* Selects the kids */
  * {
    /* Particularly queries mother and father */
    @container model(--lightness < 50%) {
      coloration: white;
    }

    @container model(--lightness >= 50%) {
      coloration: black;
    }
  }
}

Once more, you’ll need Chrome 142 or increased to see this work:

Each strategies do the identical factor however in barely other ways.

Let’s take a more in-depth look.

Vary syntax with customized properties

Within the subsequent demo arising, I’ve minimize out the if() stuff, leaving solely the container model queries. What’s occurring right here is that we’ve created a customized property referred to as --lightness on the #container. Querying the worth of an atypical property isn’t attainable, 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 coloration utilizing container model queries. Particularly, if the --lightness property of #container (and, by extension, the background) is lower than 50%, we set the coloration to white. Or, if it’s greater than or equal to 50%, we set the coloration to black.

#container {
  /* and so on. */

  /* Selects the kids */
  * {
    /* Particularly queries mother and father */
    @container model(--lightness < 50%) {
      coloration: white;
    }

    @container model(--lightness >= 50%) {
      coloration: black;
    }
  }
}

/clarification Notice that we wouldn’t have the ability 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 model queries, we might solely question particular values, so the vary syntax makes container model queries far more helpful.

Against this, the if()-based declaration would work in both block:

#container {
  --lightness: 10%;
  background: hsl(270 100% var(--lightness));

  /* --lightness works right here */
  coloration: if(
    model(--lightness < 50%): white;
    model(--lightness >= 50%): black
  );

  * {
    /* And right here! */
    coloration: if(
      model(--lightness < 50%): white;
      model(--lightness >= 50%): black
    );
  }
}

So, on condition that container model queries solely look up the cascade (whereas if() additionally seems for customized properties declared inside the similar CSS rule) why use container model queries in any respect? Effectively, private desire apart, container queries enable us to outline a selected 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 identify right here */
    @container myContainer model(--lightness < 50%) {
      coloration: white;
    }

    @container myContainer model(--lightness >= 50%) {
      coloration: 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 must declare container-name: myContainer additional up the cascade. The if() operate doesn’t enable for this, however container queries enable us to regulate the scope.

Vary syntax with the attr() CSS operate

We will additionally pull values from HTML attributes utilizing the attr() CSS operate.

Within the HTML beneath, I’ve created a component with an information 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 are able to 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 model queries right here */
}

Now for the container model question logic. Within the first one, it’s pretty apparent that if the notification rely 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 change to content material: "99+" as a result of I don’t suppose {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 principally utilizing this container model question logic to find out each content material and magnificence, which is de facto cool:

[data-notifs]::after {
  peak: 1.25rem;
  border-radius: 1.25rem;

  /* If notification rely is 1-2 digits */
  @container model(attr(data-notifs sort()) <= 99) {

    /* Show rely */
    content material: attr(data-notifs);

    /* Make width equal the peak */
    aspect-ratio: 1 / 1;
  }

  /* If notification rely is 3 or extra digits */
  @container model(attr(data-notifs sort()) > 99) {
    /* After 99, merely say "99+" */
    content material: "99+";

    /* As a substitute of width, a bit padding */
    padding-inline: 0.1875rem;
  }
}

However you’re possible questioning why, after we learn the worth within the container model queries, it’s written as attr(data-notifs sort() as a substitute of attr(data-notifs). Effectively, the reason being that after we don’t specify an information sort (or unit, you possibly can learn all concerning the current modifications to attr() right here), the worth is parsed as a string. That is nice after we’re outputting the worth with content material: attr(data-notifs), however after we’re evaluating it to 99, we should parse it as a quantity (though sort() would additionally work).

In actual fact, all vary syntax comparatives have to be of the identical information sort (though they don’t have to make use of the identical models). Supported information sorts embody , , , , , , and . Within the earlier instance, we might really categorical the lightness with out models because the fashionable hsl() syntax helps that, however we’d need to be in line with it and make sure that all comparatives are unit-less too:

#container {
  /* 10, not 10% */
  --lightness: 10;

  background: hsl(270 100 var(--lightness));

  coloration: if(
    /* 50, not 50% */
    model(--lightness < 50): white;
    model(--lightness >= 50): black
  );

  * {
    /* 50, not 50% */
    @container model(--lightness < 50) {
      coloration: white;
    }

    @container model(--lightness >= 50) {
      coloration: black;
    }
  }
}

Notice: This notification rely instance doesn’t lend itself nicely to if(), as you’d want to incorporate the logic for each related CSS property, however it's attainable 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 models, however keep in mind, they solely need to be the identical information sort and these are each legitimate CSS s.

Within the subsequent instance, we set the font-size of the component to 31px. The inherits this font-size, and since 1em is the same as the font-size of the mother or father, 1em within the scope of can also be 31px. With me to this point?

Based on 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 higher 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 concept is that if we mess with the default font-size of the , then we declare an optimized font-weight to take care of 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(
      model(1em < 32px): 100;
      model(1em > 32px): 900
    );
  }
}

CSS queries have come a great distance, haven’t they?

In my view, the vary syntax coming to container model queries and the if() operate represents CSS’s largest leap by way of conditional logic, particularly contemplating that it may be mixed with media queries, characteristic queries, and different varieties of container queries (keep in mind to declare container-type if combining with container dimension queries). In actual fact, now could be a nice time to clean up on queries, in order a bit parting reward, listed here are some hyperlinks for additional studying:

Related Articles

Latest Articles