DEV Community

Cover image for Conditional CSS
Tracy Gilmore
Tracy Gilmore

Posted on

Conditional CSS

Using Boolean logic to conditionally apply CSS rules, no-JS

Acknowledgements

Before we begin, I must thank Lea Verou for her talk on CSS Variable Secrets at CSS Day 2022 that inspired this post, and for the slide deck she kindly made available.

In Lea’s talk, she highlights some of the lesser-known capabilities of CSS Custom Properties and reveals some future enhancements. On slide 58 Lea mentioned how Logical Operations could be performed to conditionally apply styling using some simple arithmetic. In this post I plan to explore this concept in a little more depth.

Introduction

There are three fundamental logical operations: NOT, AND and OR, all of which operate on Boolean values TRUE or FALSE. However, with the possible exception of the checked pseudo class, CSS does not support Boolean operations well. In order for us to overcome this limitation we will use Binary values (1/0) instead of the Boolean values (TRUE/FALSE) respectively.

We will explore the mechanics of how we can simulate the logical operations using Custom Properties and demonstrate how the values 1 and 0 will help us apply styling. Before that we will briefly confirm our understanding of the actual Boolean operations as can be found in most programming languages such as JavaScript.

NOT Operation (“!” in JavaScript)

The NOT operation takes a single input value and inverts it. True becomes False, False becomes True, or in our case, an input of 1 results in an output of 0, and 0 in becomes 1 out.

AND Operation (“&&” in JavaScript)

Both AND and OR take two (or more) inputs and produce a single output. With the AND operation a True is only output when all inputs are True, otherwise the output is False. With our ‘functions’ using just two inputs, only when input-A AND input-B are 1 will 1 be output.

OR Operation (“||” in JavaScript)

The OR operation produces an output of TRUE if any input is TRUE, only FALSE if all inputs are FALSE. The following truth table illustrates the simple behaviour.

Truth Table

CSS Logical Operations

We will need some additional values to support our operations, defined within the context of CSS classes, as follows.
Boolean (Binary) values
Define the Boolean values True and False as 1 and 0.

.not-operation, .and-operation, .or-operation {
    --TRUE: 1;
    --FALSE: 0;
}
Enter fullscreen mode Exit fullscreen mode

Default input values
Prepare input values for the operations.

.not-operation, .and-operation, .or-operation {
    --INPUT: var(--TRUE);
    --INPUT-A: var(--FALSE);
    --INPUT-B: var(--TRUE);
}
Enter fullscreen mode Exit fullscreen mode

Logical Operations

Using the following three basic operations there are many other operations that can be derived such as NAND (NOT AND), NOR (NOT OR) and XOR etc.

NOT

Using numeric values in place of Boolean values, the NOT operation can be achieved by subtracting the input (I) from 1. When I = 1, 1 - 1 = 0, when I = 0, 1 - 0 = 1.

.not-operation {
    --OUTPUT: calc(1 - var(--INPUT));
}
Enter fullscreen mode Exit fullscreen mode

AND

Again using 1’s and 0’s, the AND operation is achieved through the multiplication of the two inputs (I1 and I2).
Numeric AND operation

Multiplying and value by zero results in zero so only when all (both) inputs are one can the output be one.

.and-operation, {
    --OUTPUT: calc(var(--INPUT-A) * var(--INPUT-B));
}
Enter fullscreen mode Exit fullscreen mode

OR

Finally we have the OR operation that is additive with a clamp. Adding the two inputs (I1 & I2) will result in:
(partial Numeric OR operation

We need to clamp the output of 1 + 1 from 2 to 1, which can be done in two ways. We could deduct the output of performing an ADD operation on the same inputs but CSS can help through its ‘min’ function as follows.

.or-operation {
    --OUTPUT: min(var(--INPUT-A) + var(--INPUT-B), 1);
}
Enter fullscreen mode Exit fullscreen mode

Demonstration

To demonstrate the above operations we will output the result using the CSS content property of the ::after pseudo-element of a DIV element with a class of ‘result’ combined.

.result::after {
    counter-reset: logic-result var(--OUTPUT);
    content: counter(logic-result);
}
Enter fullscreen mode Exit fullscreen mode

The [OPERATION] we want to perform will be stipulated using a CSS class. The input values will be supplied by assigning the appropriate custom property (-ise) via the style attribute.

Each test case will take the following form.

<div class="result [OPERATION]" style="--INPUT: [BOOLEAN_VALUE]"></div>

<!-- or -->

<div class="result [OPERATION]"
  style="--INPUT-A: [BOOLEAN_VALUE_A] --INPUT-B: [BOOLEAN_VALUE_B]"></div>
Enter fullscreen mode Exit fullscreen mode

Here are the test cases

<div class="result not-operation" style="--INPUT: var(--FALSE)"></div>
<div class="result not-operation" style="--INPUT: var(--TRUE)"></div>

<div class="result and-operation"
  style="--INPUT-A: var(--FALSE); --INPUT-B: var(--FALSE)"></div>
<div class="result and-operation"
  style="--INPUT-A: var(--FALSE); --INPUT-B: var(--TRUE)"></div>
<div class="result and-operation" 
  style="--INPUT-A: var(--TRUE); --INPUT-B: var(--FALSE)"></div>
<div class="result and-operation" 
  style="--INPUT-A: var(--TRUE); --INPUT-B: var(--TRUE)"></div>

<div class="result or-operation"
  style="--INPUT-A: var(--FALSE); --INPUT-B: var(--FALSE)"></div>
<div class="result or-operation"
  style="--INPUT-A: var(--FALSE); --INPUT-B: var(--TRUE)"></div>
<div class="result or-operation"
  style="--INPUT-B: var(--FALSE); --INPUT-A: var(--TRUE)"></div>
<div class="result or-operation"
  style="--INPUT-B: var(--TRUE); --INPUT-A: var(--TRUE)"></div>
Enter fullscreen mode Exit fullscreen mode

The source code can be exercised in my CodePen.

Top comments (0)