0Support

CSS if() playground

Choose padding values with style(), media() or supports() conditions. Adjust branches, observe native CSS, and share your settings. Includes a guide with examples and fallbacks.

Read the guide

Ask AI

CSS if() playground

Response generated with openai/gpt-5.4-nano. AI can make mistakes. Always review the result.

The space between the outer border and the inner box is the padding resolved by CSS.

CSS if()

Native preview: the browser accepts this if() syntax.

Fallback active: fixed 12px padding. This browser does not accept this if() syntax.

if(
    style(--status: complete): 32px;
    style(--status: pending): 20px;
    else: 8px
  )
HTML
<article class="if-demo">
  <p>CSS if()</p>
</article>
CSS
.if-demo {
  --status: complete;
  box-sizing: border-box;
  inline-size: 100%;
  padding: 12px;
  border: 2px solid #1e40af;
  border-radius: 16px;
  corner-shape: squircle;
  color: #172554;
  background: #dbeafe;
  font: 600 1rem/1.5 system-ui, sans-serif;
  overflow-wrap: anywhere;
}

.if-demo p {
  margin: 0;
  padding: 0.5rem;
  border: 1px dashed #1e40af;
  background: #fff;
}

@supports (padding: if(
  style(--status: complete): 32px;
  style(--status: pending): 20px;
  else: 8px
)) {
  .if-demo {
    padding: if(
      style(--status: complete): 32px;
      style(--status: pending): 20px;
      else: 8px
    );
  }
}

@media (forced-colors: active) {
  .if-demo {
    color: CanvasText;
    background: Canvas;
    border-color: CanvasText;
  }

  .if-demo p {
    background: Canvas;
    border-color: CanvasText;
  }
}

Educational guide to if()

Conditional values inside a CSS declaration

Choose which value a property receives with style(), media() and supports(). Learn to order branches, choose an else, and keep a readable base when the browser does not understand if().

Back to the playground

Mental model

Query → first true branch → property value.

  1. 1 · Test the condition

    style(--status: complete) queries a custom property on the element.

  2. 2 · Choose the first true

    Its value, 32px, is substituted inside padding.

  3. 3 · Keep a final option

    else: 8px is used when no earlier condition is true.

A path from top to bottomStatic diagram. With --status: complete, the first branch returns 32px; subsequent branches do not participate.

if(condition: value; else: alternative) takes the place of a CSS value. It does not create rules or execute JavaScript statements. Separate branches with semicolons and each condition from its value with a colon. The final semicolon is optional.

style(): the element’s own state

The token can be defined on the element or arrive through inheritance.

Here --status is on the same card that receives padding. Unlike @container style(), there is no need to query an ancestor or define container-type. Use custom properties: querying ordinary properties such as display is not an interoperable foundation for this example.

Three states, three values

Card state

CSS if() · conditional padding

Native if() active.

Fallback active: 12px padding; this browser does not accept this example’s if() syntax.

Change the state with the radios. complete chooses 32px, pending chooses 20px, and idle reaches else: 8px. Without support, the 12px base remains.
Three states, three values
<div class="if-scene-style">
  <fieldset>
    <legend>Card state</legend>
    <label><input type="radio" name="if-guide-state" value="complete" checked> complete</label>
    <label><input type="radio" name="if-guide-state" value="pending"> pending</label>
    <label><input type="radio" name="if-guide-state" value="idle"> idle → else</label>
  </fieldset>
  <article class="sample"><p>CSS if() · conditional padding</p></article>
  <p class="native">Native if() active.</p>
  <p class="fallback">Fallback active: 12px padding; this browser does not accept this example’s if() syntax.</p>
</div>
:root {
  color-scheme: light dark;
}

.if-scene-style {
  display: grid;
  gap: 1rem;
  min-inline-size: 0;
  color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
  font: 1rem/1.6 system-ui, sans-serif;
  overflow-wrap: anywhere;

  > p {
    margin: 0;
  }

  .sample {
    --status: complete;
    box-sizing: border-box;
    min-inline-size: 0;
    padding: 12px;
    border: 2px solid #1e40af;
    border-radius: 16px;
    corner-shape: squircle;
    color: #172554;
    background: #dbeafe;

    p {
      margin: 0;
      padding: 0.5rem;
      background: #fff;
      border: 1px dashed #1e40af;
    }
  }

  .native {
    display: none;
  }

  fieldset {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    min-inline-size: 0;
    padding: min(1rem, 5vi);
    border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
  }

  legend {
    padding-inline: 0.5rem;
  }

  label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem;
    cursor: pointer;
  }

  input {
    accent-color: light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
  }

  input:focus-visible {
    outline: 2px solid light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
    outline-offset: 3px;
  }

  @supports (padding: if(
    style(--status: complete): 32px;
    style(--status: pending): 20px;
    else: 8px
  )) {
    .sample {
      padding: if(
        style(--status: complete): 32px;
        style(--status: pending): 20px;
        else: 8px
      );
    }

    .native {
      display: block;
    }

    .fallback {
      display: none;
    }
  }

  &:has(input[value="pending"]:checked) .sample {
    --status: pending;
  }

  &:has(input[value="idle"]:checked) .sample {
    --status: idle;
  }

  @media (forced-colors: active) {
    .sample {
      color: CanvasText;
      background: Canvas;
      border-color: CanvasText;
    }

    .sample p {
      background: Canvas;
      border-color: CanvasText;
    }
  }
}

pending and complete are unquoted CSS tokens. "complete" and complete are different values in an unregistered custom property. Keep the same representation when assigning and querying.

media(): the page environment

An inline query for the viewport or preferences.

media() uses media query conditions. This example changes padding when the viewport reaches 800px. This card’s width does not decide the condition: if() does not turn a media query into a size container query.

A card that follows the viewport

CSS if() · conditional padding

Native if() active.

Fallback active: 12px padding; this browser does not accept this example’s if() syntax.

With support, padding is 32px from a viewport width of 800px and 8px below it. Resize the window; the fallback stays at 12px.
A card that follows the viewport
<div class="if-scene-media">
  <article class="sample"><p>CSS if() · conditional padding</p></article>
  <p class="native">Native if() active.</p>
  <p class="fallback">Fallback active: 12px padding; this browser does not accept this example’s if() syntax.</p>
</div>
:root {
  color-scheme: light dark;
}

.if-scene-media {
  display: grid;
  gap: 1rem;
  min-inline-size: 0;
  color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
  font: 1rem/1.6 system-ui, sans-serif;
  overflow-wrap: anywhere;

  > p {
    margin: 0;
  }

  .sample {
    --status: complete;
    box-sizing: border-box;
    min-inline-size: 0;
    padding: 12px;
    border: 2px solid #1e40af;
    border-radius: 16px;
    corner-shape: squircle;
    color: #172554;
    background: #dbeafe;

    p {
      margin: 0;
      padding: 0.5rem;
      background: #fff;
      border: 1px dashed #1e40af;
    }
  }

  .native {
    display: none;
  }

  fieldset {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    min-inline-size: 0;
    padding: min(1rem, 5vi);
    border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
  }

  legend {
    padding-inline: 0.5rem;
  }

  label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem;
    cursor: pointer;
  }

  input {
    accent-color: light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
  }

  input:focus-visible {
    outline: 2px solid light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
    outline-offset: 3px;
  }

  @supports (padding: if(
    media((width >= 800px)): 32px;
    else: 8px
  )) {
    .sample {
      padding: if(
        media((width >= 800px)): 32px;
        else: 8px
      );
    }

    .native {
      display: block;
    }

    .fallback {
      display: none;
    }
  }

  @media (forced-colors: active) {
    .sample {
      color: CanvasText;
      background: Canvas;
      border-color: CanvasText;
    }

    .sample p {
      background: Canvas;
      border-color: CanvasText;
    }
  }
}

The playground also offers prefers-color-scheme and prefers-reduced-motion. Changing the website’s local theme does not necessarily change the system preference queried by media().

supports(): accepting syntax

Checking support and checking whether a condition matches are different tasks.

supports() returns a CSS support condition. Here display: grid leads to 32px when accepted. The playground’s negative option queries a deliberately nonexistent value so you can observe else without faking browser capabilities.

Choose a value based on Grid support

CSS if() · conditional padding

Native if() active.

Fallback active: 12px padding; this browser does not accept this example’s if() syntax.

supports(display: grid) is evaluated only when if() exists. A browser accepting both syntaxes uses 32px; without if(), it keeps 12px.
Choose a value based on Grid support
<div class="if-scene-supports">
  <article class="sample"><p>CSS if() · conditional padding</p></article>
  <p class="native">Native if() active.</p>
  <p class="fallback">Fallback active: 12px padding; this browser does not accept this example’s if() syntax.</p>
</div>
:root {
  color-scheme: light dark;
}

.if-scene-supports {
  display: grid;
  gap: 1rem;
  min-inline-size: 0;
  color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
  font: 1rem/1.6 system-ui, sans-serif;
  overflow-wrap: anywhere;

  > p {
    margin: 0;
  }

  .sample {
    --status: complete;
    box-sizing: border-box;
    min-inline-size: 0;
    padding: 12px;
    border: 2px solid #1e40af;
    border-radius: 16px;
    corner-shape: squircle;
    color: #172554;
    background: #dbeafe;

    p {
      margin: 0;
      padding: 0.5rem;
      background: #fff;
      border: 1px dashed #1e40af;
    }
  }

  .native {
    display: none;
  }

  fieldset {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    min-inline-size: 0;
    padding: min(1rem, 5vi);
    border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
  }

  legend {
    padding-inline: 0.5rem;
  }

  label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem;
    cursor: pointer;
  }

  input {
    accent-color: light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
  }

  input:focus-visible {
    outline: 2px solid light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
    outline-offset: 3px;
  }

  @supports (padding: if(
    supports(display: grid): 32px;
    else: 8px
  )) {
    .sample {
      padding: if(
        supports(display: grid): 32px;
        else: 8px
      );
    }

    .native {
      display: block;
    }

    .fallback {
      display: none;
    }
  }

  @media (forced-colors: active) {
    .sample {
      color: CanvasText;
      background: Canvas;
      border-color: CanvasText;
    }

    .sample p {
      background: Canvas;
      border-color: CanvasText;
    }
  }
}

supports() inside if() needs if() to exist first. It does not protect an unknown function for older browsers. Use @supports around the declaration for that protection.

Branch order and else

The first match wins; the last does not replace earlier matches.

If two conditions are true, the first one’s value is returned. There is no cascade between branches. On these cards, --status: complete and media(width >= 0px) are both true; reversing their order changes padding from 32px to 8px.

Same conditions, different order

style() first → 32px

media(width >= 0px) first → 8px

Native if() active.

Fallback active: 12px padding; this browser does not accept this example’s if() syntax.

With support, the first card receives 32px and the second 8px. The later condition no longer decides the value. Both keep 12px without support.
Same conditions, different order
<div class="if-scene-order">
  <article class="sample"><p>style() first → 32px</p></article>
  <article class="sample media-first"><p>media(width &gt;= 0px) first → 8px</p></article>
  <p class="native">Native if() active.</p>
  <p class="fallback">Fallback active: 12px padding; this browser does not accept this example’s if() syntax.</p>
</div>
:root {
  color-scheme: light dark;
}

.if-scene-order {
  display: grid;
  gap: 1rem;
  min-inline-size: 0;
  color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
  font: 1rem/1.6 system-ui, sans-serif;
  overflow-wrap: anywhere;

  > p {
    margin: 0;
  }

  .sample {
    --status: complete;
    box-sizing: border-box;
    min-inline-size: 0;
    padding: 12px;
    border: 2px solid #1e40af;
    border-radius: 16px;
    corner-shape: squircle;
    color: #172554;
    background: #dbeafe;

    p {
      margin: 0;
      padding: 0.5rem;
      background: #fff;
      border: 1px dashed #1e40af;
    }
  }

  .native {
    display: none;
  }

  fieldset {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    min-inline-size: 0;
    padding: min(1rem, 5vi);
    border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
  }

  legend {
    padding-inline: 0.5rem;
  }

  label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem;
    cursor: pointer;
  }

  input {
    accent-color: light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
  }

  input:focus-visible {
    outline: 2px solid light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
    outline-offset: 3px;
  }

  @supports (padding: if(style(--status: complete): 32px; media(width >= 0px): 8px; else: 12px)) {
    .sample {
      padding: if(style(--status: complete): 32px; media(width >= 0px): 8px; else: 12px);
    }

    .native {
      display: block;
    }

    .fallback {
      display: none;
    }
  }

  @supports (padding: if(media(width >= 0px): 8px; style(--status: complete): 32px; else: 12px)) {
    .sample.media-first {
      padding: if(media(width >= 0px): 8px; style(--status: complete): 32px; else: 12px);
    }
  }

  @media (forced-colors: active) {
    .sample {
      color: CanvasText;
      background: Canvas;
      border-color: CanvasText;
    }

    .sample p {
      background: Canvas;
      border-color: CanvasText;
    }
  }
}

else is an always-true condition: put it last to avoid unreachable branches. Without else, if no condition matches, if() produces a guaranteed-invalid value. This does not mean 0 or automatically recovering an earlier declaration.

Fallbacks and invalid values

Three cases: if() is missing, no branch matches, or the value does not fit the property.

The recipe sets padding: 12px outside @supports and adds if() only if the browser accepts that syntax for padding. The 8px else works inside the function; the 12px fallback protects browsers that cannot use it. These are different levels.

A base before progressive enhancement

CSS if()

Native if() active.

Fallback active: 12px padding; this browser does not accept this example’s if() syntax.

The card uses 32px if it understands if() and 12px otherwise. Content and borders remain present in either case.
A base before progressive enhancement
<div class="if-scene-fallback">
<article class="if-scene-fallback-card">
  <p>CSS if()</p>
</article>
  <p class="native">Native if() active.</p>
  <p class="fallback">Fallback active: 12px padding; this browser does not accept this example’s if() syntax.</p>
</div>
:root {
  color-scheme: light dark;
}

.if-scene-fallback-card {
  --status: complete;
  box-sizing: border-box;
  inline-size: 100%;
  padding: 12px;
  border: 2px solid #1e40af;
  border-radius: 16px;
  corner-shape: squircle;
  color: #172554;
  background: #dbeafe;
  font: 600 1rem/1.5 system-ui, sans-serif;
  overflow-wrap: anywhere;
}

.if-scene-fallback-card p {
  margin: 0;
  padding: 0.5rem;
  border: 1px dashed #1e40af;
  background: #fff;
}

@supports (padding: if(
  style(--status: complete): 32px;
  style(--status: pending): 20px;
  else: 8px
)) {
  .if-scene-fallback-card {
    padding: if(
      style(--status: complete): 32px;
      style(--status: pending): 20px;
      else: 8px
    );
  }
}

@media (forced-colors: active) {
  .if-scene-fallback-card {
    color: CanvasText;
    background: Canvas;
    border-color: CanvasText;
  }

  .if-scene-fallback-card p {
    background: Canvas;
    border-color: CanvasText;
  }
}

.if-scene-fallback {
  display: grid;
  gap: 1rem;
  color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
  font: 1rem/1.6 system-ui, sans-serif;
  overflow-wrap: anywhere;

  > p {
    margin: 0;
  }

  .native {
    display: none;
  }

  @supports (padding: if(
    style(--status: complete): 32px;
    style(--status: pending): 20px;
    else: 8px
  )) {
    .native {
      display: block;
    }

    .fallback {
      display: none;
    }
  }
}

A custom property can store tokens that later fail as padding. CSS.supports("--token", "if(...)") does not detect the function. Check a real property and a concrete expression. Even with support, the value reaching padding must have the right type; include else and avoid cyclic references.

No match: use the var() fallback

Here --status is idle and the only branch queries complete. --space becomes guaranteed-invalid, allowing var(--space, 8px) to provide an alternative. This does not turn every wrong value type into a var() fallback.

No match: use the var() fallback
<div class="if-no-match">CSS if()</div>
.if-no-match {
  padding: 12px;

  @supports (padding: if(style(--status: complete): 32px; else: 8px)) {
    --status: idle;
    --space: if(style(--status: complete): 32px);
    padding: var(--space, 8px);
  }
}

Common mistakes

Diagnosing the right level avoids confusing a false condition with missing support.

The function is not interpreted

Expected result
The property changes based on a condition.
Cause
Branches use commas, a colon is missing, or there is a space between if and the parenthesis.
Solution
Use if(condition: value; else: alternative), with no space in if(.

The second condition does not win

Expected result
A branch further down replaces the earlier result.
Cause
The first branch was already true, or an earlier else stopped the search.
Solution
Order conditions from specific to general and leave else last.

else does not rescue an older browser

Expected result
The browser uses else when it does not know if().
Cause
It cannot evaluate any part of a function it does not implement.
Solution
Declare a base outside and guard if() with @supports on a real property.

A narrow card does not activate media()

Expected result
The condition changes when the panel becomes narrow.
Cause
media(width ...) queries the page viewport, not the component box.
Solution
Resize the viewport; use @container for container size conditions.

Comparisons

Choose the tool based on what you need to change.

if() chooses a value inside one declaration; @media and @supports can apply sets of declarations. @container queries a container and can apply rules to its descendants; style() inside if() queries the element receiving the value. @function defines a custom CSS function; if() is already defined by the platform.

For a theme that only switches colors based on color-scheme, light-dark() may express the intent better. For whole component variants, a class or conditional rule can be clearer than repeating if() across many properties.

Support and compatibility

Catalog status, syntax detection and actual behavior.

Checked on October 15, 2026: Web Platform DX and MDN list if() as limited availability. BCD records Chrome/Edge from 137, with no stable support in Firefox or Safari. The Baseline card may update independently of this text.

@supports checks acceptance of the queried syntax; it does not certify every branch or every engine. The preview lets CSS resolve the condition. An unsupported browser keeps the base and its notice, without simulating if() in JavaScript.