Skip to content

Instantly share code, notes, and snippets.

@tenphi
Last active June 6, 2025 12:50
Show Gist options
  • Select an option

  • Save tenphi/e3bd1c771e05815ba4ea6d68edffa305 to your computer and use it in GitHub Desktop.

Select an option

Save tenphi/e3bd1c771e05815ba4ea6d68edffa305 to your computer and use it in GitHub Desktop.
Conditional Switch Blocks for CSS

Conditional Switch Blocks for CSS

Editor's Draft – 6 June 2025

Status: This document is an unofficial editor's draft. It has no formal standing within the W3C. It is published for discussion and early feedback.


1. Introduction

CSS already provides conditional constructs such as @media, @supports, @container, and the in-progress @when / @else rules. These mechanisms succeed when the condition to test is of a single kind (viewport size, feature support, container size, etc.). Authors of design systems, however, frequently need an exclusive multi-branch switch whose branches can be defined by any Boolean combination of selectors, media queries, container queries, or previously named conditions.

This specification introduces two at-rules and a helper function that together fulfil that need:

  • @condition — defines a reusable Boolean condition alias.
  • @switch / @case / @default — selects exactly one branch whose declarations participate in the cascade; @switch <ident> allows a named switch that can be wholly re-defined later in the same stylesheet.
  • is() and has() — selector-based functions inside Boolean expressions (is() matches the given selector list; has() matches when the element has the selector list in its relative scope).

Design goals:

  • Expressiveness (cross-type Boolean logic, nesting, aliasing).
  • Exclusivity (only one branch applies per @switch).
  • Zero new cascade rules (the winning branch cascades exactly where the @switch appears).
  • Easy compilation to today's CSS.

2. Conformance

The key words MUST, MUST NOT, SHOULD, and MAY in this document are to be interpreted as described in [RFC 2119].


3. Definitions

  • Boolean Expression – an expression composed of media() queries (whose arguments may use operators like width < 30rem), container() queries, the selector functions is() / has(), and/or <ident> names defined by @condition, joined by the keywords not, and, or, xor, and parentheses for grouping.
  • Condition Alias – an <ident> whose Boolean expression is provided by an earlier @condition rule.
  • Winning Branch – the first @case inside an @switch whose Boolean expression evaluates to true at computed-value time, or the @default branch if present and no @case matches.

4. @condition — Condition Alias Rule

4.1 Syntax

@condition <ident>: <boolean-expression> ;
  • <ident> MUST NOT begin with the characters -- (reserved for custom properties).
  • The rule MAY appear either in a top-level stylesheet context or inside a qualified rule (selector block). It MUST NOT appear inside an @switch rule.

4.2 Semantics

  1. The user agent stores the <boolean-expression> under the specified <ident> in a Condition Map associated with the stylesheet.
  2. If the same <ident> is defined again in the same stylesheet, the last definition wins (shadowing earlier ones).
  3. If evaluation of the stored Boolean expression would require referring to the <ident> itself (directly or indirectly), the rule is invalid and ignored (cycle detection).

5. Boolean Expression Grammar

The grammar extends that used by CSS Conditional Rules Level 5.

<boolean-expr>   = <boolean-term> ( 'or'  <boolean-term> )*
<boolean-term>   = <boolean-xor>  ( 'xor' <boolean-xor>  )*
<boolean-xor>    = <boolean-factor> ( 'and' <boolean-factor> )*
<boolean-factor> = [ 'not' ] <boolean-atom>
<boolean-atom>   = media( … )
                | container( … )
                | is( … )
                | has( … )
                | <ident>
                | '(' <boolean-expr> ')'

  • is() and has() use the same selector-argument grammar as their corresponding pseudo-classes. They are evaluated against the element at computed-value time.
  • media() takes any as defined in Media Queries Level 4 and supports modern relational operators such as width < 30rem or pointer = coarse.
  • Evaluation rules for not, and, or, xor follow standard Boolean logic, with operator precedence from highest to lowest: not > and > xor > or.

6. @switch — Exclusive Conditional Group Rule

6.1 Syntax

@switch [<ident>] {
  @case <boolean-expression> { <declaration-list> }
  [ @case <boolean-expression> { <declaration-list> } ]*
  [ @default { <declaration-list> } ]?
}
  • The block MUST NOT contain declarations before the first @case.
  • The block MUST contain at least one @case.
  • At most one @default is allowed and it MUST come after all @case rules.
  • If <ident> is supplied, the switch becomes named; a later @switch with the same <ident> in the same stylesheet replaces the earlier one in its entirety (all previous branches are discarded).

6.2 Parsing

  1. For an unnamed @switch, the UA builds an ordered list of branches consisting of all @case rules (in source order) followed by @default if present.
  2. For a named @switch, the UA stores this branch list under the given <ident>, replacing any previously-stored list with that name from the same stylesheet.
  3. Each @case Boolean expression is parsed using the grammar in § 5, expanding any <ident> names via the Condition Map.

6.3 Evaluation and Cascade

  1. At computed-value time, the UA evaluates each branch in list order until one is true.
  2. The declarations of the winning branch participate in the cascade in the same layer and position the @switch rule occupies in the stylesheet.
  3. Branches evaluated after the winner, as well as those not evaluated (because an earlier branch already won), contribute no declarations.
  4. If no @case evaluates to true and an @default exists, it becomes the winner; otherwise the @switch contributes no declarations.

7. Examples

7.1 Condition Aliases

@condition mobile: media(width < 30rem);
@condition tablet: media(width >= 30rem) and media(width <= 60rem);
@condition touch: media(pointer = coarse);

@condition handheld: mobile or tablet;
@condition touch-handheld: handheld and touch;

7.2 Component Styling

.card {
  /* global defaults */
  padding: 1rem;
  background: var(--surface);

  @switch {
    @case mobile and is(.is-primary) {
      background: var(--brand-dark);
      color: white;
    }

    @case mobile {
      background: var(--surface-alt);
    }

    @case tight {
      border-radius: .25rem;
    }

    @default {
      /* desktop & others */
      transition: box-shadow .3s;
      &:hover { box-shadow: 0 0 .5rem rgb(0 0 0 /.2); }
    }
  }
}

7.3 Error: Cycle in Alias Definitions

@condition handheld: mobile or tablet;
@condition mobile: handheld;   /* ← invalid: self-reference loop */

7.4 Button State Styling

/* Button component using native pseudo-class selectors */
.button {
  padding-inline: var(--space-sm);
  padding-block:  var(--space-xs);
  border: none;
  border-radius: var(--radius-s);
  color: var(--text-on-brand);
  background: var(--brand-primary);
  transition: background-color var(--transition-fast);

  @switch theme {
    @case is(:disabled) {
      background: var(--surface-disabled);
      color: var(--text-disabled);
      cursor: not-allowed;
      opacity: .6;
    }

    @case is(:active) {
      background: var(--brand-primary-dark);
      box-shadow: inset 0 1px 3px rgb(0 0 0 / .3);
      transform: translateY(1px);
    }

    @case is(:hover) or is(:focus-visible) {
      background: var(--brand-primary-light);
      box-shadow: 0 0 0 2px var(--brand-outline);
    }

    @default {
      /* resting state defined above */
    }
  }
}

8. Author-Tooling & AI Benefits (non-normative)

While not part of the formal language definition, the declarative nature of @condition, @switch, and selector-based Boolean functions enables a new class of tooling—human-written or AI-assisted—to make stylesheets smaller, clearer, and easier to maintain.

  • Refactoring assistance – Static analyzers can detect recurring query patterns and suggest extracting them into shared @condition aliases, reducing duplication.
  • Intelligent auto-completion – Editors can surface valid alias names, flag unused ones, and warn against cycles in real time.
  • Explain-why queries – Because each @switch has a single winning branch, tools can trace an element’s computed style back to the exact @case that applies and present that explanation in natural language.
  • Legacy compilation – Build-time transformers can expand @switch blocks to equivalent rules (@media, @supports, selectors) for browsers that lack native support, guided by project-specific compatibility targets.
  • Performance hints – Analytics-driven linters can recommend ordering @cases so the most commonly true condition appears first, minimizing evaluation cost.
  • Test generation – By enumerating Boolean combinations that flip branch outcomes, automated test suites can achieve full visual-regression coverage with fewer handcrafted cases.

These capabilities are purely additive: they require no changes to the runtime behaviour specified in earlier sections, yet they materially enhance the author experience.


9. Security & Privacy Considerations

This feature is not expected to expose new fingerprinting surfaces beyond what exists today with dynamic class changes or attribute mutations. Nonetheless, implementers SHOULD review whether evaluation of arbitrary selectors inside is() or has() can leak user-specific data through timing attacks.


9. Feature Detection

Authors may test UA support using the following @supports pattern:

@supports (condition-alias: true) {
  /* UA understands @condition and @switch */
}

The property name condition-alias is a feature gate token and has no computed value.


10. Change Log

  • 2025-06-06 – Initial editor's draft.

11. Acknowledgements

Early ideas, terminology, and Boolean-grammar inspiration come from the editors of CSS Conditional Rules Level 5, CSS Media Queries Level 4, and from community proposals by Tab Atkins Jr., Miriam Suzanne, Roman Komarov, and countless design-system authors.


12. References

  • CSS Conditional Rules Module Level 5 (WD, 2024-11-05)
  • CSS Media Queries Level 4 (REC)
  • CSS Container Queries Level 3 (CR)
  • Selectors Level 4 (WD)
  • RFC 2119 – Key words for use in RFCs to Indicate Requirement Levels
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment