CSS 2025 · Experimental

The CSS if() Function

Inline conditionals that react to style, media, and feature queries — without leaving your CSS.

Available from Chrome 137+ · Firefox & Safari support in progress · Not yet Baseline
01 — The Basics

What is if()?

The if() function lets you write conditional values directly inside a CSS property. Instead of duplicating selectors or nesting inside @media, you express the condition right where the value lives.

ANATOMY

The Syntax

property: if(condition: value-if-true; else: fallback);

You can chain multiple conditions. The first one that matches wins. The else clause is optional — but if you omit it and nothing matches, the function returns a guaranteed-invalid value, which behaves like the property was never set (it falls back to its inherited or initial value).

/* Single condition with else */
color: if(style(--theme: dark): #fff; else: #000);

/* Multiple conditions (first match wins) */
font-size: if(
  style(--size: sm): 0.75rem;
  style(--size: lg): 1.5rem;
  else: 1rem
);
⚠ GOTCHA

No Space Before the Parenthesis

Unlike most CSS functions, if() is invalid if there is a space between if and (. The entire declaration is thrown out silently.

/* ❌ INVALID — space before ( */
color: if (style(--theme: dark): white; else: black);

/* ✅ VALID — no space */
color: if(style(--theme: dark): white; else: black);
02 — Three Query Types

What Can You Test?

if() supports three types of conditions, each wrapping a different kind of query.

style()

Test a CSS custom property value on the element itself.

media()

Test a media condition like viewport width or print mode.

supports()

Test whether the browser supports a feature before using it.

Query type Example Use when…
style() style(--variant: danger) Your value depends on a custom property
media() media(max-width: 600px) Your value depends on the viewport or device
supports() supports(display: grid) You need a feature-detection fallback
03 — style() Queries

Reacting to Custom Properties

The most powerful use of if(). Set a --custom-property on an element, then let every CSS value react to it inline.

EXAMPLE A

Theme Toggle

One custom property, multiple properties all reacting inline:

.card {
  --theme: light; /* or "dark" — set this anywhere */

  background: if(style(--theme: dark): #1a1a2e; else: #f0f4ff);
  color:      if(style(--theme: dark): #e0e0ff; else: #2233aa);
  border:     2px solid if(style(--theme: dark): #4444aa; else: #aabbff);
}
Live Demo — click to toggle (note: discrete switch, no CSS transition)
I react to --theme
EXAMPLE B

Size Variants

One --size property controls padding, font-size and border-radius simultaneously:

.btn {
  --size: md;

  padding:     if(style(--size: sm): 6px 12px;
                   style(--size: md): 12px 24px;
                   else: 20px 40px);
  font-size:   if(style(--size: sm): 0.75rem;
                   style(--size: md): 1rem;
                   else: 1.4rem);
  border-radius: if(style(--size: sm): 4px;
                    style(--size: md): 8px;
                    else: 14px);
}
Live Demo — pick a size
Button
💡 Key difference from @container style(): @container style() requires the element to be inside a named container ancestor — you query the parent. With if(style()), the element queries its own custom properties directly. No container-type wrapper needed.
04 — media() Queries

Viewport-Aware Values

You can now write a media condition inside a property value instead of a separate @media block.

EXAMPLE C

Before vs After

/* OLD WAY — two separate blocks */
.box { padding: 24px; }
@media (max-width: 600px) {
  .box { padding: 12px; }
}

/* NEW WAY — inline with if() */
.box {
  padding: if(media(max-width: 600px): 12px; else: 24px);
}
Live Demo — resize your browser below ~600px
My color changes at 600px viewport width
05 — supports() Queries

Feature Detection Inline

Use supports() to provide a safe fallback when a browser doesn't support something yet.

EXAMPLE D

Progressive Enhancement

.box {
  /* Use oklch color if supported, otherwise plain hex */
  color: if(
    supports(color: oklch(0.7 0.185 232)): oklch(0.7 0.185 232);
    else: #00adf3
  );

  /* Use grid if supported, fallback to flex */
  display: if(supports(display: grid): grid; else: flex);
}
Practical note: supports() inside if() is most useful for future CSS features that land after if() itself — since a browser must already support if() to evaluate it. Using it to detect something like display: grid (universally supported) is mainly illustrative.
Live Demo — tests if your browser supports display: grid
Grid is supported ✓ (purple) · Not supported (orange fallback)
06 — Advanced Patterns

Going Further

Four powerful techniques you'll want in your toolkit.

EXAMPLE E

if() Inside a Shorthand

if() can substitute a single token within a shorthand property — only that piece is conditional, the rest is fixed.

.card {
  --variant: default;

  /* Only the border-style part is conditional */
  border: 2px blue if(
    style(--variant: dashed): dashed;
    style(--variant: dotted): dotted;
    else: solid
  );
}
How it works: if() substitutes a single value token within the shorthand. The parser resolves the whole shorthand after substitution — so it works as long as the surrounding tokens form a valid declaration.
EXAMPLE F

Colon : vs Equals = in style()

There are two ways to compare in style(), and they behave very differently with computed values.

/* The ":" notation — string/exact match of computed values */
.box {
  --n: calc(6/2);
  background: if(style(--n: 3): red; else: green); /* → green! --n is "calc(6/2)" not "3" */
}

/* The "=" notation — numeric comparison (evaluates the math) */
.box {
  --n: calc(6/2);
  background: if(style(--n = 3): red; else: green); /* → red! 6/2 == 3 ✓ */
}
Rule of thumb: Use : for string-like values (--status: error, --theme: dark). Use = for numeric comparisons when calc() is involved. The = notation evaluates the math first; : compares raw token strings.
EXAMPLE G

Register with @property to Fix calc() Matching

When using the : notation, if your custom property holds a calc() expression, the browser treats it as a string — not a number. Registering it with @property forces evaluation.

/* ❌ Won't match — browser sees "calc(var(--n)/2)", not 3 */
.box {
  --f: calc(var(--n)/2);
  background: if(style(--f: 3): red; else: green); /* → green */
}

/* ✅ Works — @property tells the browser --f is a <number> */
@property --f {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
.box {
  --f: calc(var(--n)/2);
  background: if(style(--f: 3): red; else: green); /* → red ✓ */
}
EXAMPLE H

Read HTML data-* Attributes with attr()

Bridge HTML state directly into CSS — no JavaScript class-toggling needed. Combine attr() with if() to style components based on their data-* attributes.

/* HTML */
<div class="card" data-status="pending">...</div>
<div class="card" data-status="complete">...</div>

/* CSS — read the attribute into a custom property, then test it */
.card {
  /* attr() with type(<custom-ident>) reads the data attribute */
  --status: attr(data-status type(<custom-ident>));

  border-color: if(
    style(--status: pending):  royalblue;
    style(--status: complete): seagreen;
    else: gray
  );
  background-color: if(
    style(--status: pending):  #eff7fa;
    style(--status: complete): #f6fff6;
    else: #f7f7f7
  );
}
Simulated demo (JS sets the data attribute, CSS if() reacts)
Task A
Task B
Task C
EXAMPLE I

Store if() Results in Custom Properties

You can assign an if() expression to a custom property. This keeps your declarations clean and avoids repeating the same conditions everywhere.

:root {
  --theme: "Emerald";

  /* Compute once, reuse everywhere */
  --bg: if(
    style(--theme: "Emerald"):  hsl(146 50% 40%);
    style(--theme: "Amber"):    hsl(43 74% 64%);
    else: hsl(0 0% 90%)
  );
  --fg: if(
    style(--theme: "Emerald"):  hsl(146 50% 3%);
    style(--theme: "Amber"):    hsl(43 74% 3%);
    else: hsl(0 0% 10%)
  );
}

/* Now just use the variables */
body  { background: var(--bg); color: var(--fg); }
header { border-bottom: 2px solid var(--bg); }
Design tip: This is the foundation of clean design-token systems. Set a single --theme variable at the root and let all your computed colours derive from it via if()-powered variables.
EXAMPLE J

Check If a Custom Property Exists

You can test whether a custom property has any value (not just a specific value) by omitting the value in the condition. Useful for opt-in features.

/* Only show the icon element if --icon-family has been set */
.icon {
  display: if(
    style(--icon-family): inline-block;
    else: none
  );
}

/* The component is hidden unless the caller sets --icon-family */
/* <span class="icon" style="--icon-family: 'Material Icons'"> → shown */
/* <span class="icon">                                         → hidden */

Quick Quiz

Seven questions. Pick the best answer for each.

1. Which syntax is correct for a basic CSS if() statement?
Think about the colon and semicolon separators.
2. Which query type would you use to apply different styles for print vs screen?
Think about what type of condition controls print vs screen.
3. Multiple conditions are listed in an if(). Which one applies?
What happens when more than one condition is true?
4. What does supports() inside if() test?
When would you choose supports() over style() or media()?
5. A key advantage of style() inside if() vs @container style() is…
Think about where the custom property lives relative to the element.
6. You write style(--n: 3) but --n is set to calc(6/2). Does the condition match?
Think about how the browser stores unregistered custom property values.
7. What happens if no if() condition matches and there is no else?
What does the spec say about an unmatched if() with no fallback?