Conditional styles with CSS custom properties
Asked Answered
T

6

15

I enjoy using CSS custom properties, but there's this thing that I often find that I wish I could do.

I wish to be able to apply some styles conditionally based on the value of a CSS custom property. Here's some pseudo-code:

.something {
  border: var(--is-dark-theme) ? 1px solid : none;
}

I understand that custom properties do not work like this. But may be there is some other way that I'm not aware of that could help me achieve a similar result?

Or perhaps there is some spec proposal that would this possible in the future?

Terrijo answered 2/10, 2018 at 21:55 Comment(1)
Depends if you want to do styles based on a value comparison or text string, both are possible. Look at this MDN reference on attribute selectors for more info. for value comparison answers, you can use the answer by @oridori for string matching you can use Attribute selectors. Here is an excellent reference for string matching. developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectorsGunrunning
T
12

Here is another idea similar to Ori Drori's answer where I rely on the use of an invalid value inside border to remove the border. This can be useful in case you want to use keywords like false/true/yes/no

.something {
  border: var(--is-dark-theme,2px) solid black;
}
<div class="something">Dark theme</div>

<div class="something" style="--is-dark-theme: false">Light theme</div>
Thou answered 2/10, 2018 at 22:14 Comment(1)
Nice! That's actually closer to what the OP wants, since you can do background: var(--is-dark-theme, red);Bodice
B
7

You can sometimes use calc() to get roughly similar results. In this case if --is-dark-theme is 0 or 1, you can multiply it by the width of border, to show or hide it:

.something {
  border: calc(var(--is-dark-theme) * 1px) solid black;
}
<div class="something" style="--is-dark-theme: 1">Dark theme</div>

<div class="something" style="--is-dark-theme: 0">Light theme</div>
Bodice answered 2/10, 2018 at 22:3 Comment(0)
A
7

Just FYI: The following is not working today (2023-02-06), but someday in future there might be this very nice CSS feature called container style queries:

/* not working today (2023-02-06), maybe in future */

@container style(--theme: dark) {
  .box {
    border: 1px solid #aaa;
  }
}
Aback answered 6/2, 2023 at 4:19 Comment(1)
This is now supported by browsers that use Blink: developer.mozilla.org/en-US/docs/Web/CSS/…Dela
A
3

In cases where you have something like "condition xyz is on or off" (like in the light theme vs dark theme example) you can use the following CSS trick (I know this looks completely weird if you see this trick for the first time, but in contrast to the tricks presented in the other answers, this one will work for all CSS properties):

For each on/off condition define two custom properties, one for the 'on' case, one for the 'off' case. Instead of on use the value initial, instead of off use value (space character).

.light-theme {
  --is-light-theme: initial; 
  --is-dark-theme: ;
}

.dark-theme {
  --is-light-theme: ;
  --is-dark-theme: initial;
}

div {
  font-family: Helvetica, Arial, sans-serif;
  padding: 1rem;
  
  color:
    var(--is-light-theme, black)
    var(--is-dark-theme, white);
  
  background-color:
    var(--is-light-theme, #f4f4f4)
    var(--is-dark-theme, black);

  border:
    var(--is-light-theme, none)
    var(--is-dark-theme, 5px solid #aaa);
}
<div class="light-theme">
  light theme
</div>
<div class="dark-theme">
  dark theme
</div>
Aback answered 24/1, 2023 at 3:3 Comment(0)
H
3

This code enables you to apply a class based on a CSS property of a parent. So by applying a different CSS to a parent element, you can change the class applied to a child element:

.outer:hover {
  container-name: dark;
  container-type: inline-size;
}

.inner{background-color:silver;color:black;}

@container dark (min-width: 0px)  {
  .inner{background-color:#333;color:white}
}
<div class="outer">
  <div class="inner">inner</div>
</div>

Here we have element with the class name .inner, By default it has a silver background and white text color.

Above it we have a parent element with the class name .outer. the .outer class no styles by default, but on hover we set the container-name to dark.

We can the target the .inner element when it's inside an element that has the container-name:dark and use an always true predicate that is (min-width: 0px)

Heterothallic answered 23/5, 2023 at 17:43 Comment(0)
A
3

Here's an alternative version of my demo above, using Thorgeir's genius answer (I've added those extra .container divs to make the solution a bit better understandable):

.light-theme {
  --theme: light;
}

.dark-theme {
  --theme: dark;
}

.container {
  container: var(--theme) / inline-size;  
}

.text-box {
  font-family: Helvetica, Arial, sans-serif;
  padding: 1rem;
  color: black;
  background-color: #f4f4f4;
  border: none;
}

@container dark (min-width: 0) {
  .text-box {
    color: white;
    background-color: black;
    border: 5px solid #aaa;
  }
}
  <div class="light-theme">
    <div class="container">
      <div class="text-box">light theme</div>
    </div>
  </div>
  <div class="dark-theme">
    <div class="container">
      <div class="text-box">dark theme</div>
    </div>
  </div>
  
Aback answered 24/5, 2023 at 21:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.