> All in One 586: Poking at the CSS if() Function a Little More: Conditional Color Theming

Ads

Wednesday, June 25, 2025

Poking at the CSS if() Function a Little More: Conditional Color Theming

Chrome 137 shipped the if() CSS function, so it’s totally possible we’ll see other browsers implement it, though it’s tough to know exactly when. Whatever the case, if() enables us to use values conditionally, which we can already do with queries and other functions (e.g., media queries and the light-dark() function), so I’m sure you’re wondering: What exactly does if() do?

Sunkanmi gave us a nice overview of the function yesterday, poking at the syntax at a high level. I’d like to poke at it a little harder in this article, getting into some possible real-world usage.

To recap, if() conditionally assigns a value to a property based on the value of a CSS variable. For example, we could assign different values to the color and background properties based on the value of --theme:

  • --theme: "Shamrock"
    • color: ‌hsl(146 50% 3%)
    • background: hsl(146 50% 40%)
  • --theme: Anything else
    • color: hsl(43 74% 3%)
    • background: hsl(43 74% 64%)
:root {
  /* Change to fall back to the ‘else’ values */
  --theme: "Shamrock";

  body {
    color: if(style(--theme: "Shamrock"): hsl(146 50% 3%); else: hsl(43 74% 3%));
    background: if(style(--theme: "Shamrock"): hsl(146 50% 40%); else: hsl(43 74% 64%));
  }
}

I don’t love the syntax (too many colons, brackets, and so on), but we can format it like this (which I think is a bit clearer):

color: if(
  style(--theme: "Shamrock"): hsl(146 50% 3%);
  else: hsl(43 74% 3%)
);

We should be able to do a crazy number of things with if(), and I hope that becomes the case eventually, but I did some testing and learned that the syntax above is the only one that works. We can’t base the condition on the value of an ordinary CSS property (instead of a custom property), HTML attribute (using attr()), or any other value. For now, at least, the condition must be based on the value of a custom property (CSS variable).

Exploring what we can do with if()

Judging from that first example, it’s clear that we can use if() for theming (and design systems overall). While we could utilize the light-dark() function for this, what if the themes aren’t strictly light and dark, or what if we want to have more than two themes or light and dark modes for each theme? Well, that’s what if() can be used for.

First, let’s create more themes/more conditions:

:root {
  /* Shamrock | Saffron | Amethyst */
  --theme: "Saffron"; /* ...I choose you! */

  body {
    color: if(
      style(--theme: "Shamrock"): hsl(146 50% 3%);
      style(--theme: "Saffron"): hsl(43 74% 3%);
      style(--theme: "Amethyst"): hsl(282 47% 3%)
    );
    background: if(
      style(--theme: "Shamrock"): hsl(146 50% 40%);
      style(--theme: "Saffron"): hsl(43 74% 64%);
      style(--theme: "Amethyst"): hsl(282 47% 56%)
    );
    transition: 300ms;
  }
}

Pretty simple really, but there are a few easy-to-miss things. Firstly, there’s no “else condition” this time, which means that if the theme isn’t Shamrock, Saffron, or Amethyst, the default browser styles are used. Otherwise, the if() function resolves to the value of the first true statement, which is the Saffron theme in this case. Secondly, transitions work right out of the box; in the demo below, I’ve added a user interface for toggling the --theme, and for the transition, literally just transition: 300ms alongside the if() functions:

Note: if theme-swapping is user-controlled, such as selecting an option, you don’t actually need if() at all. You can just use the logic that I’ve used at the beginning of the demo (:root:has(#shamrock:checked) { /* Styles */ }). Amit Sheen has an excellent demonstration over at Smashing Magazine.

To make the code more maintainable though, we can slide the colors into CSS variables as well, then use them in the if() functions, then slide the if() functions themselves into CSS variables:

/* Setup */
:root {
  /* Shamrock | Saffron | Amethyst */
  --theme: "Shamrock"; /* ...I choose you! */

  /* Base colors */
  --shamrock: hsl(146 50% 40%);
  --saffron: hsl(43 74% 64%);
  --amethyst: hsl(282 47% 56%);

  /* Base colors, but at 3% lightness */
  --shamrock-complementary: hsl(from var(--shamrock) h s 3%);
  --saffron-complementary: hsl(from var(--saffron) h s 3%);
  --amethyst-complementary: hsl(from var(--amethyst) h s 3%);

  --background: if(
    style(--theme: "Shamrock"): var(--shamrock);
    style(--theme: "Saffron"): var(--saffron);
    style(--theme: "Amethyst"): var(--amethyst)
  );

  --color: if(
    style(--theme: "Shamrock"): var(--shamrock-complementary);
    style(--theme: "Saffron"): var(--saffron-complementary);
    style(--theme: "Amethyst"): var(--amethyst-complementary)
  );

  /* Usage */
  body {
    /* One variable, all ifs! */
    background: var(--background);
    color: var(--color);
    accent-color: var(--color);

    /* Can’t forget this! */
    transition: 300ms;
  }
}

As well as using CSS variables within the if() function, we can also nest other functions. In the example below, I’ve thrown light-dark() in there, which basically inverts the colors for dark mode:

--background: if(
  style(--theme: "Shamrock"): light-dark(var(--shamrock), var(--shamrock-complementary));
  style(--theme: "Saffron"): light-dark(var(--saffron), var(--saffron-complementary));
  style(--theme: "Amethyst"): light-dark(var(--amethyst), var(--amethyst-complementary))
);

if() vs. Container style queries

If you haven’t used container style queries before, they basically check if a container has a certain CSS variable (much like the if() function). Here’s the exact same example/demo but with container style queries instead of the if() function:

:root {
  /* Shamrock | Saffron | Amethyst */
  --theme: "Shamrock"; /* ...I choose you! */

  --shamrock: hsl(146 50% 40%);
  --saffron: hsl(43 74% 64%);
  --amethyst: hsl(282 47% 56%);

  --shamrock-complementary: hsl(from var(--shamrock) h s 3%);
  --saffron-complementary: hsl(from var(--saffron) h s 3%);
  --amethyst-complementary: hsl(from var(--amethyst) h s 3%);

  body {
    /* Container has chosen Shamrock! */
    @container style(--theme: "Shamrock") {
      --background: light-dark(var(--shamrock), var(--shamrock-complementary));
      --color: light-dark(var(--shamrock-complementary), var(--shamrock));
    }

    @container style(--theme: "Saffron") {
      --background: light-dark(var(--saffron), var(--saffron-complementary));
      --color: light-dark(var(--saffron-complementary), var(--saffron));
    }

    @container style(--theme: "Amethyst") {
      --background: light-dark(var(--amethyst), var(--amethyst-complementary));
      --color: light-dark(var(--amethyst-complementary), var(--amethyst));
    }

    background: var(--background);
    color: var(--color);
    accent-color: var(--color);
    transition: 300ms;
  }
}

As you can see, where if() facilitates conditional values, container style queries facilitate conditional properties and values. Other than that, it really is just a different syntax.

Additional things you can do with if() (but might not realize)

Check if a CSS variable exists:

/* Hide icons if variable isn’t set */
.icon {
  display: if(
    style(--icon-family): inline-block;
    else: none
  );
}

Create more-complex conditional statements:

h1 {
  font-size: if(
    style(--largerHeadings: true): xxx-large;
    style(--theme: "themeWithLargerHeadings"): xxx-large
  );
}

Check if two CSS variables match:

/* If #s2 has the same background as #s1, add a border */
#s2 {
  border-top: if(
    style(--s2-background: var(--s1-background)): thin solid red
  );
}

if() and calc(): When the math isn’t mathing

This won’t work (maybe someone can help me pinpoint why):

div {
  /* 3/3 = 1 */
  --calc: calc(3/3);
  /* Blue, because if() won’t calculate --calc */
  background: if(style(--calc: 1): red; else: blue);
}

To make if() calculate --calc, we’ll need to register the CSS variable using @property first, like this:

@property --calc {
  syntax: "<number>";
  initial-value: 0;
  inherits: false;
}

Closing thoughts

Although I’m not keen on the syntax and how unreadable it can sometimes look (especially if it’s formatted on one line), I’m mega excited to see how if() evolves. I’d love to be able to use it with ordinary properties (e.g., color: if(style(background: white): black; style(background: black): white);) to avoid having to set CSS variables where possible.

It’d also be awesome if calc() calculations could be calculated on the fly without having to register the variable.

That being said, I’m still super happy with what if() does currently, and can’t wait to build even simpler design systems.


Poking at the CSS if() Function a Little More: Conditional Color Theming originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/qn23t4l
via IFTTT

No comments:

Post a Comment

Poking at the CSS if() Function a Little More: Conditional Color Theming

Chrome 137 shipped the if() CSS function , so it’s totally possible we’ll see other browsers implement it, though it’s tough to know exact...