Can you use if/else conditions in CSS?
Asked Answered
H

19

188

I would like to use conditions in my CSS.

The idea is that I have a variable that I replace when the site is run to generate the right style-sheet.

I want it so that according to this variable the style-sheet changes!

It looks like:

[if {var} eq 2 ]
    background-position : 150px 8px;
[else]
    background-position : 4px 8px; 

Can this be done? How do you do this?

Helvetii answered 15/7, 2009 at 6:20 Comment(0)
G
192

Not in the traditional sense, but you can use classes for this, if you have access to the HTML. Consider this:

<p class="normal">Text</p>

<p class="active">Text</p>

and in your CSS file:

p.normal {
  background-position : 150px 8px;
}
p.active {
  background-position : 4px 8px;
}

That's the CSS way to do it.


Then there are CSS preprocessors like Sass. You can use conditionals there, which'd look like this:

$type: monster;
p {
  @if $type == ocean {
    color: blue;
  } @else if $type == matador {
    color: red;
  } @else if $type == monster {
    color: green;
  } @else {
    color: black;
  }
}

Disadvantages are, that you're bound to pre-process your stylesheets, and that the condition is evaluated at compile time, not run time.


A newer feature of CSS proper are custom properties (a.k.a. CSS variables). They are evaluated at run time (in browsers supporting them).

With them you could do something along the line:

:root {
  --main-bg-color: brown;
}

.one {
  background-color: var(--main-bg-color);
}

.two {
  background-color: black;
}

Finally, you can preprocess your stylesheet with your favourite server-side language. If you're using PHP, serve a style.css.php file, that looks something like this:

p {
  background-position: <?php echo (@$_GET['foo'] == 'bar')? "150" : "4"; ?>px 8px;
}

In this case, you will however have a performance impact, since caching such a stylesheet will be difficult.


On a more high-level note, Ahmad Shadeed shows in this article a lot of very useful techniques to decide if/else questions often coming up in UI development purely within CSS.

Gabardine answered 15/7, 2009 at 6:28 Comment(4)
Good answer, I just want to add one point regarding caching: Since browsers cache stylesheets and usually do not re-download them for every page it's hard to rely on "dynamic" stylesheets generated by PHP/[programming-language-of-choice]. It's not the way it's supposed to work. If you need changing CSS, you can embed it in the page of course, but that starts to go against content/presentation separation.Ardith
In regards to what deceze said, that doesn't mean you can't have a dynamically changing website. The styles sheets can remain the static, and you can change element classes using javascript with or without jquery or PHP that way the elements styling can change.Hectare
and, what about if you want to stylize scrollbars ? (you can't find ::-webkit-scrollbar-button in html)Rustication
Um, yes? What about it? Sass, CSS variables, and serverside processing do not depend on the HTML. Or am I missing something here?Gabardine
J
47

I am surprised that nobody has mentioned CSS pseudo-classes, which are also a sort-of conditionals in CSS. You can do some pretty advanced things with this, without a single line of JavaScript.

Some pseudo-classes:

  • :active - Is the element being clicked?
  • :checked - Is the radio/checkbox/option checked? (This allows for conditional styling through the use of a checkbox!)
  • :empty - Is the element empty?
  • :fullscreen - Is the document in full-screen mode?
  • :focus - Does the element have keyboard focus?
  • :focus-within - Does the element, or any of its children, have keyboard focus?
  • :has([selector]) - Does the element contain a child that matches [selector]? (Sadly, not supported by any of the major browsers.)
  • :hover - Does the mouse hover over this element?
  • :in-range/:out-of-range - Is the input value between/outside min and max limits?
  • :invalid/:valid - Does the form element have invalid/valid contents?
  • :link - Is this an unvisited link?
  • :not() - Invert the selector.
  • :target - Is this element the target of the URL fragment?
  • :visited - Has the user visited this link before?

Example:

div { color: white; background: red }
input:checked + div { background: green }
<input type=checkbox>Click me!
<div>Red or green?</div>
Jose answered 20/10, 2019 at 19:34 Comment(5)
great list but don't cover custom states... Since I set some custom properties on the root html element I added html[style*="--playerState:PAUSED"] to compliment some custom state, could probably also just have used dataset instead but oh well.Heptane
@Endless: --playerState:PAUSED is not a valid style code. You could instead use it as a class value, or even better with data-playerState="Paused" .Jose
i know, data- === dataset. But they don't call it custom properties for nothing. at least i can abuse it from within css media queries also if i like and override stuff from within css if i would likeHeptane
You are right, I haven't played with custom properties enough yet. Sounds like an interesting approach!Jose
Is there any way to have a pseudo-class to match if the text element of the content is number-only?Ternion
H
35

Update Jul 2023:

Modern CSS now has @container queries support for size and soon also style & state, and that basically means a native way for an if/else condition

.wrapper {
  --something: 1;
}

@container style(--something: 1) {
  .foo {...}
}

Limitations of @container queries (that I can think of):

  1. require an actual container element
  2. queries conditions do not apply to the "self" (only one of the parents)

Read more about style-queries


I've written a article regarding the below unique method in CSS-Tricks which goes into further detail


I've devised the below demo using a mix of tricks which allows simulating if/else scenarios for some properties. Any property which is numerical in its essence is easy target for this method, but properties with text values are.

This code has 3 if/else scenarios, for opacity, background color & width. All 3 are governed by two Boolean variables bool and its opposite notBool.

Those two Booleans are the key to this method, and to achieve a Boolean out of a none-boolean dynamic value, requires some math which luckily CSS allows using min & max functions.

Obviously those functions (min/max) are supported in recent browsers' versions which also supports CSS custom properties (variables).

var elm = document.querySelector('div')

setInterval(()=>{
  elm.style.setProperty('--width', Math.round(Math.random()*80 + 20))
}, 1000)
:root{
   --color1: lightgreen;
   --color2: salmon;
   --width: 70;  /* starting value, randomly changed by javascript every 1 second */
}

div{
 --widthThreshold: 50;
 --is-width-above-limit: Min(1, Max(var(--width) - var(--widthThreshold), 0));
 --is-width-below-limit: calc(1 - var(--is-width-above-limit));
 
 --opacity-wide: .4;     /* if width is ABOVE 50 */
 --radius-narrow: 10px;  /* if width is BELOW 50 */
 --radius-wide: 60px;    /* if width is ABOVE 50 */
 --height-narrow: 80px;  /* if width is ABOVE 50 */
 --height-wide: 160px;   /* if width is ABOVE 50 */
 
 --radiusToggle: Max(var(--radius-narrow), var(--radius-wide) * var(--is-width-above-limit));
 --opacityToggle: calc(calc(1 + var(--opacity-wide)) - var(--is-width-above-limit));
 --colorsToggle: var(--color1) calc(100% * var(--is-width-above-limit)), 
                 var(--color2) calc(100% * var(--is-width-above-limit)), 
                 var(--color2) calc(100% * (1 - var(--is-width-above-limit)));
  
 --height: Max(var(--height-wide) * var(--is-width-above-limit), var(--height-narrow));
 
 height: var(--height);
 text-align: center;
 line-height: var(--height);

 width: calc(var(--width) * 1%);
 opacity: var(--opacityToggle);
 border-radius: var(--radiusToggle);
 background: linear-gradient(var(--colorsToggle));

 transition: .3s;
}

/* prints some variables */
div::before{
  counter-reset: aa var(--width);
  content: counter(aa)"%";
}

div::after{
  counter-reset: bb var(--is-width-above-limit);
  content: " is over 50% ? "counter(bb);
}
<div></div>

Another simple way using clamp:

label{ --width: 150 }
input:checked + div{ --width: 400 }

div{
  --isWide: Clamp(0,   (var(--width) - 150) * 99999, 1);
  width: calc(var(--width) * 1px);
  height: 150px;
  border-radius: calc(var(--isWide) * 20px); /* if wide - add radius */
  background: lightgreen;
}
<label>
<input type='checkbox' hidden> 
<div>Click to toggle width</div>
</label>

Best so far:

Update October 2020 - I've found a Chrome bug which I have reported that can affect this method in some situations where specific type of calculations is necessary, but there's a way around it.

Update June 2023 - stopped working due to a Chrome regression issue which I've filed a report for

I have come up with a totally unique method, which is even simpler!

By combining animation paused & duration, it is possible to selectively enable/disable an animation which has a single frame that starts at 0% - a duration of 0s doesn't have an affect, while any greater valuer does, and so, the only keyframes gets rendered permanently (due to the use of paused).

var elm = document.querySelector('div')

// random number between 20 & 80
setInterval(()=>{
 elm.style.setProperty('--width', Math.round(Math.random()*80 + 20))
}, 1000)
:root{
   --color1: salmon;
   --color2: lightgreen;
}

@keyframes wide-container-frames{
  0%{ 
     --height: 160px;
     --radius: 30px;
     --color: var(--color2);
     opacity: .4; /* consider this as additional, never-before, style */
  }
}

@keyframes wide-container-frames--after{
  0%{ 
    content: "true"; 
    color: green; 
  }
}

div{
 --width: 70;           /* must be unitless */
 --height: 80px;
 --radius: 0px;
 --color: var(--color1);
 --widthThreshold: 50;
 --is-width-over-threshold: Min(1, Max(var(--width) - var(--widthThreshold), 0));

 font: 22px Roboto, Arial;
 text-align: center;
 white-space: nowrap;
 
 /* if element is narrower than --widthThreshold */
 width: calc(var(--width) * 1%);
 height: var(--height);
 line-height: var(--height);
 border-radius: var(--radius);
 background: var(--color);

 /* else */
 animation: wide-container-frames calc(var(--is-width-over-threshold) * 1s) paused;
}

/* prints some variables */
div::before{
  counter-reset: aa var(--width);
  content: counter(aa)"% is over 50% width ? ";
}

div::after{
  content: 'false'; 
  font-weight: bold; 
  color: darkred;
  
  /* if element is wider than --widthThreshold */
  animation: wide-container-frames--after calc(var(--is-width-over-threshold) * 1s) paused;
}
<div></div>
Haematogenous answered 12/10, 2020 at 17:25 Comment(5)
I like the animation idea. Unfortunately, it doesn't seem to be working for me on the latest version of Chrome: Version 102.0.5005.115 (Official Build) (64-bit) when I run your code snippet above. Not sure if this is related to the Chrome bug you submitted or not?Coachman
The last method didn't work for me in Chrome (I assume due to the bug) but I found that declaring the properties as integers up front using @property fixed it. @property --is-width-over-threshold { syntax: "<integer>"; inherits: true; initial-value: 0; }Headway
@DaveHoulbrooke - it used to work, i'm seeing the issue with the last example and am searching for why it stopped workingHaematogenous
@Haematogenous Yep I think it's to do with the Chrome bug you linked to — that said that the steps() function required integers to work, and custom properties weren't being interpreted as real integers by Chrome. If you declare the property to be an integer with the @property syntax it works correctly though (I assume this is similar to how custom properties can't transition unless they're declared with @property too). Your answer is really interesting though! Clever technique.Headway
I've reported of a new bug - CSS math inside an animation steps() function renders incorrect keyframeHaematogenous
C
23

You can use calc() in combination with var() to sort of mimic conditionals:

:root {
--var-eq-two: 0;
}

.var-eq-two {
    --var-eq-two: 1;
}

.block {
    background-position: calc(
        150px * var(--var-eq-two) +
        4px * (1 - var(--var-eq-two))
    ) 8px;
}

concept

Chrischrism answered 20/12, 2017 at 16:8 Comment(1)
This is manual and not automatic if/else. Should assume the variable might change at any time and do if/else accordinglyHaematogenous
C
13

Below is my old answer which is still valid but I have a more opinionated approach today:

One of the reasons why CSS sucks so much is exactly that it doesn't have conditional syntax. CSS is per se completely unusable in the modern web stack. Use SASS for just a little while and you'll know why I say that. SASS has conditional syntax... and a LOT of other advantages over primitive CSS too.


Old answer (still valid):

It cannot be done in CSS in general!

You have the browser conditionals like:

/*[if IE]*/ 
body {height:100%;} 
/*[endif]*/

But nobody keeps you from using Javascript to alter the DOM or assigning classes dynamically or even concatenating styles in your respective programming language.

I sometimes send css classes as strings to the view and echo them into the code like that (php):

<div id="myid" class="<?php echo $this->cssClass; ?>">content</div>
Conferva answered 15/7, 2009 at 6:26 Comment(1)
Surprised nobody has indicated that that conditional syntax doesn't exist in CSS. Conditional comments only exist in HTML.Dodeca
C
7

You could create two separate stylesheets and include one of them based on the comparison result

In one of the you can put

background-position : 150px 8px;

In the other one

background-position : 4px 8px;

I think that the only check you can perform in CSS is browser recognition:

Conditional-CSS

Caffeine answered 15/7, 2009 at 6:33 Comment(0)
R
7

CSS is a nicely designed paradigm, and many of it's features are not much used.

If by a condition and variable you mean a mechanism to distribute a change of some value to the whole document, or under a scope of some element, then this is how to do it:

var myVar = 4;
document.body.className = (myVar == 5 ? "active" : "normal");
body.active .menuItem {
  background-position : 150px 8px;
  background-color: black;
}
body.normal .menuItem {
  background-position : 4px 8px; 
  background-color: green;
}
<body>
<div class="menuItem"></div>
</body>

This way, you distribute the impact of the variable throughout the CSS styles. This is similar to what @amichai and @SeReGa propose, but more versatile.

Another such trick is to distribute the ID of some active item throughout the document, e.g. again when highlighting a menu: (Freemarker syntax used)

var chosenCategory = 15;
document.body.className = "category" + chosenCategory;
<#list categories as cat >
    body.category${cat.id} .menuItem { font-weight: bold; }
</#list>
<body>
<div class="menuItem"></div>
</body>

Sure,this is only practical with a limited set of items, like categories or states, and not unlimited sets like e-shop goods, otherwise the generated CSS would be too big. But it is especially convenient when generating static offline documents.

One more trick to do "conditions" with CSS in combination with the generating platform is this:

.myList {
   /* Default list formatting */
}
.myList.count0 {
   /* Hide the list when there is no item. */
   display: none;
}
.myList.count1 {
   /* Special treatment if there is just 1 item */
   color: gray;
}
<ul class="myList count${items.size()}">
<!-- Iterate list's items here -->
<li>Something...</div>
</ul>
Righthander answered 2/10, 2017 at 4:5 Comment(0)
R
5

You can use not instead of if like

.Container *:not(a)
{
    color: #fff;
}
Rigby answered 24/6, 2014 at 23:37 Comment(0)
R
4

Set the server up to parse css files as PHP and then define the variable variable with a simple PHP statement.

Of course this assumes you are using PHP...

Rabelaisian answered 15/7, 2009 at 6:26 Comment(4)
You can replace "PHP" with "Your programming or templating language of choice"Keyser
Problem is, if your CSS is 50kb, and you are only changing a few values, wouldn't it better off being static and cached?Alastair
yep, but you could extract the portions to need dynamics and re-include them via @import url('dynamic.css.php').Gabardine
It depends on what the conditions are. If they are stable on a per user basis, you can output the usual cache control headers.Keyser
I
3

This is a little extra info to the Boldewyn answer above.

Add some php code to do the if/else

if($x==1){
  print "<p class=\"normal\">Text</p>\n";
} else {
  print "<p class=\"active\">Text</p>\n";
}
Interval answered 15/7, 2009 at 6:56 Comment(0)
A
3

CSS has a feature: Conditional Rules. This feature of CSS is applied based on a specific condition. Conditional Rules are:

  • @supports
  • @media
  • @document

Syntax:

@supports ("condition") {

   /* your css style */

}

Example code snippet:

<!DOCTYPE html> 
<html> 
<head> 
    <title>Supports Rule</title> 
    <style>      
        @supports (display: block) { 
            section h1 { 
                background-color: pink; 
                color: white; 
            } 
            section h2 { 
                background-color: pink; 
                color: black; 
            } 
        } 
    </style> 
</head> 
<body> 
    <section> 
        <h1>Stackoverflow</h1> 
        <h2>Stackoverflow</h2> 
    </section> 
</body> 
</html> 
Arrear answered 13/10, 2020 at 12:21 Comment(0)
H
2

As far as i know, there is no if/then/else in css. Alternatively, you can use javascript function to alter the background-position property of an element.

Holocaine answered 15/7, 2009 at 6:24 Comment(0)
A
2

Yet another option (based on whether you want that if statement to be dynamically evaluated or not) is to use the C preprocessor, as described here.

Adherence answered 15/7, 2009 at 8:33 Comment(0)
E
2

You can add container div for all your condition scope.

Add the condition value as a class to the container div. (you can set it by server side programming - php/asp...)

<!--container div-->
<div class="true-value">
   <!-- your content -->
   <p>my content</p>
   <p>my content</p>
   <p>my content</p>
</div>

Now you can use the container class as a global variable for all elements in the div using a nested selector, without adding the class to each element.

.true-value p{
   background-color:green;
}
.false-value p{
   background-color:red;
}
Edy answered 3/3, 2015 at 14:49 Comment(0)
C
2

You can use javascript for this purpose, this way:

  1. first you set the CSS for the 'normal' class and for the 'active' class
  2. then you give to your element the id 'MyElement'
  3. and now you make your condition in JavaScript, something like the example below... (you can run it, change the value of myVar to 5 and you will see how it works)

var myVar = 4;

if(myVar == 5){
  document.getElementById("MyElement").className = "active";
}
else{
  document.getElementById("MyElement").className = "normal";
}
.active{
  background-position : 150px 8px;
  background-color: black;
}
.normal{
  background-position : 4px 8px; 
  background-color: green;
}
div{
  width: 100px;
  height: 100px;
  }
<div id="MyElement">
  
  </div>
Cosher answered 19/9, 2016 at 7:51 Comment(0)
F
2

Besides the answers above, soon another way to directly use if/else -like conditions, and even more closely aligned with other scripting languages, would be via @when / @else conditionals. These conditionals would be implemented to exercise easily recognizable logic chain, for example:

@when supports(display: flex) {
    .container {
        display: flex
    }
} @else media and (min-width: 768px) {
    .container {
        min-width: 768px
    }
} @else {
    .container {
        width: 100%
    }
}

As of February 2022 there is no browser support. Please see this W3C module for more info.

Fingerboard answered 7/3, 2022 at 1:27 Comment(0)
S
1

(Yes, old thread. But it turned up on top of a Google-search so others might be interested as well)

I guess the if/else-logic could be done with javascript, which in turn can dynamically load/unload stylesheets. I haven't tested this across browsers etc. but it should work. This will get you started:

http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml

Sartorial answered 25/2, 2010 at 9:2 Comment(0)
W
0

If you're open to using jquery, you can set conditional statements using javascript within the html:

$('.class').css("color",((Variable > 0) ? "#009933":"#000"));

This will change the text color of .class to green if the value of Variable is greater than 0.

Warfarin answered 12/5, 2016 at 17:23 Comment(0)
S
0

One option that doesn't seem mentioned in the other answers would be an attribute selector. You could set e.g. a data attribute on a div tag

<div data-var="2">

and then define a selector for divs with the right attribute value as the if case, and a div selector without an attribute selector for the else case:

div[data-var="2"] {
    background-position : 150px 8px;
}
div {
    background-position : 4px 8px;
}
Seaport answered 14/3, 2023 at 23:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.