timetowonder
timetowonder

Reputation: 5421

Conditional styles with CSS custom properties

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?

Upvotes: 19

Views: 18295

Answers (6)

Natasha
Natasha

Reputation: 796

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;
  }
}

Upvotes: 8

Natasha
Natasha

Reputation: 796

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>
  

Upvotes: 3

Temani Afif
Temani Afif

Reputation: 272816

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>

Upvotes: 12

Thorgeir
Thorgeir

Reputation: 4433

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)

Upvotes: 3

Natasha
Natasha

Reputation: 796

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>

Upvotes: 6

Ori Drori
Ori Drori

Reputation: 191976

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>

Upvotes: 7

Related Questions