Kingsley Ubah 21. Web Developer. Technical Writer. African in Tech.

Extending CSS when/else chains: A first look

4 min read 1356

Extending CSS @when and @else chains: A first look

CSS authors use CSS conditional rules to define a set of rules based on the capabilities of a processor or document a style sheet is applied to. Some of these rules enable authors to perform logic within the style sheet.

For example, CSS authors use @media to make media queries and selectively apply styling to a document. Similarly, the @support rule is used for querying a user’s agent’s support for new CSS features.

On their own, these rules are very useful. Many times, however, CSS authors may need to combine these rules to create conditional statements. This is commonly done because different style rules apply under different circumstances.

Presently, this behavior can be quite tricky to implement in CSS since authors must carefully craft the style sheet in a way to account for all conditions.

With regards to these issues, there are two new conditional rules proposed to solve them: @when and @else. At the time of posting, the @when proposal was resolved to be adopted “into the next level of CSS conditionals” by CSSWG.

But what are they and how can we use them?

In this article, we’ll take a first look at these new conditional rules, including some practical uses for them within style sheets.

Creating generalized conditional rules with @when

The proposed @when rule generalizes conditionals, so instead of using a specific conditional rule for a specific task, such as a feature query with @support, you can generalize your rule block to use two or more kinds of queries.

You can think of it as a wrapper for other CSS conditional rules, such as @media and @support.

So, suppose you want to test to see whether a media screen is below 769 pixels and perform two feature queries to check whether the browser supports both grid and flex display features. If true, you also want to set only one item per grid column.

We made a custom demo for .
No really. Click here to check it out.

Without @when, you might have to write something similar to this:

@media (max-width: 769px) {
    @supports (display: grid) and (display: flex) {
        .grid {
            grid-template-columns: 1fr;
        }

        .flex {
            flex-direction: row;
        }
    }
}

In the above code, we check for three conditions before activating the following style sheet and use two different conditional rules.

But with @when, we can create a generalized rule to wrap all the disparate conditions into one statement:

@when media(max-width: 769px) and supports(display: grid) and supports(display: flex) {
    .grid {
        grid-template-columns: 1fr;
    }

    .flex {
        flex-direction: row;
    }
}

This is great, but it raises the following question: what will happen if the conditions (queries) in the @when statement evaluates to false?

Well, technically nothing will happen. If the condition in @when fails, the CSS engine ignores its style block and falls back to the default styles.

Again, this is great for simple cases. But what if you want to create something longer than just two conditions? Say if condition one is true, we want to apply style block one. Otherwise, if condition two is true, we want to apply style two. If neither conditions are true, we fall back to the default style.

The Level 4 specification of CSS conditionals proposes the @else rule for the creation of conditional rule chains in CSS.

Using @when inline

@when can also be used inline. In the following example, we will set a grid of three items per column on an element. Then, we must change to a single item per grid column when width is less than 400 pixels:

.container {
    width: 80%;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    padding: 1rem;
    @when element(max-width: 400px) {
        grid-template-columns: 1fr;
        padding: 2rem;
    }
}

By using @when inside the style block above, we can specify different style rules to activate when the element achieves a stated condition, such as when its width is less than 400px.

Creating conditional chains with @else

The @else conditional rule allows authors to create a series of conditional rules, thereby forming a conditional chain.

In the previous code, we created a generalized conditional to make media, support queries, and apply a set of styles if the condition is true. However, there is no fallback condition in case the conditions specified in @when evaluates to false.

By using @else, we can create a chain of conditional statements, each containing a condition to evaluate.

At the end of the chain lies a fallback if all other conditions evaluate to false:

@when media(min-width: 768px) and supports(display: grid) {
    div {
        display: grid;
        grid-template-columns: 1fr;
    }
  } @else supports(clip-path: circle(1px)) and supports(transform: skewY(1deg)) {
        div {
            display: block;
         }

         .blog img {
          clip-path: circle(50%);
          }

          .showcase {
          transform: skewY(10deg);
          }
    }
  } @else {
    /* Fallback. In case all of the above conditions evaluates to false */
      div {
        display: block;
      }

      img {
        width: 100%;
        height: 100%;
      }
}

Without conditional chaining, this is how you achieve the same thing:

  /* If  width is smaller than 769px */
@media (max-width: 768px) {
    @supports (display: grid) {
      div {
        grid-template-columns: 1fr;
      }
    }
    @supports not (display: grid) {
      @supports (clip-path: circle(1px)) and (transform: skewY(1deg)) {
        div {
            display: block;
        }

        .blog img {
            clip-path: circle(50%);
        }

        .showcase {
            transform: skewY(10deg);
          }
      }
      @supports not ((clip-path: circle(1px)) and (transform: skewY(1deg))) {
        div {
            display: block;
        }

        .blog img {
            width: 100%;
            height: 100%;
        }
      }
    }
  }

  /* If  width is larger than 769px */
  @media (min-width: 769px) {
    @supports (clip-path: circle(1px)) and (transform: skewY(1deg)) {
           div {
            grid-template-columns: repeat(3, 1fr);
            }

            .blog img {
              clip-path: circle(50%);
            }

            .showcase {
              transform: skewY(10deg);
            }
    }
    @supports not ((clip-path: circle(1px)) and (transform: skewY(1deg))) {
         div {
            grid-template-columns: repeat(3, 1fr);
          }

          .blog img {
            width: 100%;
            height: 100%;
          }
      }
  }

Compared to the conditional chain, this code is unreadable and a lot of content was duplicated across the various code blocks. The more complex your styling gets, the more difficult everything becomes.

Note that in a conditional chain, only one rule is picked. For example, if the condition in the first rule evaluates to true, then the following rules in the chain must evaluate to false.

A new way to write media queries

So far, we used min-width and max-width to set breakpoints for styling. Semantically, these terms can be difficult to understand.

min-width refers to larger screens, while max-width refers to smaller screens. This can get quite tricky to make sense of, especially for beginners.

With this in mind, a more semantic way to write query conditions was introduced in the Level 4 specification for media queries.

So, instead of writing your media queries this way:

@media (max-width: 769px) {
    .grid {
        grid-template-columns: 1fr;
    }

    .flex {
        flex-direction: row;
    }
}

@media (min-width: 769px) {
    .grid {
        grid-template-columns: repeat(3, 1fr);
    }

    .flex {
        flex-direction: column;
    }

    body {
        text-align: center;
    }
}

You can soon write them this way instead:

@media (width >= 769px) {
    .grid {
        grid-template-columns: repeat(3, 1fr);
    }

    .flex {
        flex-direction: column;
    }
}

@media (width <= 769px) {
    .grid {
        grid-template-columns: 1fr;
    }

    .flex {
        flex-direction: row;
    }
 }
}

This syntax makes more sense compared to using the min and max properties because you do not need to understand what max-width and min-width actually stands for. Using comparison operators makes media queries much easier to read and understand.

Support extensions for CSS conditional rules

In addition to @when and @else, the Level 4 update includes extensions to support the rules, allowing testing for supported font technologies.

The CSSWG Conditional Rules Module Level 4 draft provides a good example of how the new conditional rules can query for supported font technologies:

@when font-technology(color-COLRv1) and font-technology(variations) {
    @font-face { font-family: icons; src: url(icons-gradient-var.woff2); }
}
@else font-technology(color-SVG) {
    @font-face { font-family: icons; src: url(icons-gradient.woff2); }
}
@else font-technology(color-COLRv0) {
    @font-face { font-family: icons; src: url(icons-flat.woff2); }
}
@else {
    @font-face { font-family: icons; src: url(icons-fallback.woff2); }
}

So, in the above code, we’re testing three different font color technologies. The first rule tests for COLRv1, which supports both gradients and font variations. If the browser supports them, it sets the font face accordingly and ignores the rest of the chain.

Otherwise, it tests the second rule, and so on. In the end, only one font face style will activate.

Conclusion

@when and @else may be coming along, but not without an issue. There is a strong debate against the use of @when since @if is considered a more common name.

But, @when was picked to avoid conflict with Sass, a popular CSS preprocessor.

SASS already uses a rule called @if, which controls whether or not a style block gets evaluated or activated.

Others, however, are suggesting different names, such as @where.

Regardless of its name, one cannot deny that these rules will be very useful when officially implemented by web browsers.

Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web apps — .

Kingsley Ubah 21. Web Developer. Technical Writer. African in Tech.

Leave a Reply