CSS if() is one of those small-looking platform additions that can remove a surprising amount of UI plumbing. It gives stylesheets a conditional expression, so a component can choose a value based on a media feature, a supports() test, or a style query, without asking JavaScript to coordinate the decision.
For product teams, that matters less because it is novel CSS syntax and more because it moves a class of presentation decisions back to the rendering layer. Responsive tokens, compact variants, visual affordances, and progressive enhancements no longer all need a React effect, an event listener, or an extra modifier class.
TL;DR: Use CSS if() to select a CSS value from conditions the browser already understands. Start with isolated design tokens and progressive enhancements, provide a fallback declaration, and keep data or behaviour decisions in JavaScript.Table of contents
- Why conditional values belong in CSS
- The
if()mental model - Three useful conditions
- Practical component patterns
- Progressive enhancement and fallbacks
- Where CSS
if()should not be used - A sensible adoption plan
- FAQ
Why conditional values belong in CSS
Modern frontend applications often split one visual decision across several layers. CSS describes the result, while JavaScript observes viewport state or component configuration, translates that into a class, then triggers a render. That can be appropriate for behaviour, but it is an expensive path for a purely visual choice.
Consider a card that uses a larger gap on wide screens and a compact gap on narrow ones. A media query is already the right answer when every card follows the same rule. The complexity appears when that spacing is one value inside a composable token system, or when a component wants to respond to the style of the container it lives in.
Historically, teams have reached for one of these workarounds:
- duplicate declarations in multiple media or container-query blocks;
- add an
isCompactclass from a component framework; - use
matchMedia()and subscribe to changes; - calculate a custom property in JavaScript;
- accept a less precise design.
CSS container queries and style queries removed much of the need to inspect the viewport in JavaScript. CSS if() takes the next step: it lets a declaration select between values based on those conditions.
The important boundary is simple. If the decision changes what the interface does, application code should own it. If it changes how the same interface looks, let CSS own it whenever possible.
The if() mental model
At a high level, if() reads like a compact conditional expression:
.element {
property: if(condition: value; else: fallback-value);
}A real declaration contains a condition type. The condition can check a media feature, feature support, or a style query. The browser uses the first matching branch; else covers the remaining case.
.card {
padding: if(
media(width >= 48rem): 2rem;
else: 1rem
);
}This is not an imperative if statement. It does not run arbitrary code, set state, or alter the DOM. It only resolves a CSS value during style calculation. That limitation is a feature: it keeps responsive presentation close to the cascade rather than creating another application-state system.
The syntax is most useful when the alternatives belong in the same declaration. If an entire block of properties must change, a normal media query, container query, or selector remains clearer.
Three useful conditions
1. Media conditions for responsive values
The media form makes responsive values much easier to keep alongside a component token.
:root {
--page-gutter: if(
media(width >= 80rem): 3rem;
media(width >= 48rem): 2rem;
else: 1rem
);
}
.page-shell {
padding-inline: var(--page-gutter);
}This should not replace every media-query block. It is a win when a design value has a small number of responsive modes and the value is reused. The token tells the whole story in one place.
It also avoids the false precision of reading window.innerWidth in a component. Browser media features already account for the environment the CSS engine is styling.
2. supports() conditions for progressive enhancement
Feature detection is another natural use. A design can opt into a newer capability while preserving an established baseline.
.product-grid {
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
grid-template-columns: if(
supports(grid-template-columns: subgrid): subgrid;
else: repeat(auto-fit, minmax(16rem, 1fr))
);
}In practice, a separate fallback declaration before the enhanced one is often the clearest approach, especially while browser support is still developing. But supports() inside if() is useful when the result is a single calculated design token rather than a whole alternative rule.
.component {
--surface-shadow: if(
supports(color: color-mix(in srgb, black, white)): 0 1rem 2rem color-mix(in srgb, black 18%, transparent);
else: 0 1rem 2rem rgb(0 0 0 / 18%)
);
box-shadow: var(--surface-shadow);
}3. Style conditions for component variants
Style queries are particularly compelling for design systems. A component can react to a custom property supplied by its parent rather than depend on an application-specific class name.
.card-region {
container-type: inline-size;
--density: comfortable;
}
.card {
padding: if(
style(--density: compact): 0.75rem;
else: 1.25rem
);
gap: if(
style(--density: compact): 0.5rem;
else: 0.875rem
);
}A dashboard sidebar can set --density: compact; a main content region can keep the comfortable default. The card remains portable because it does not need to know which route, framework component, or breakpoint placed it there.
This is a better abstraction than an ever-growing set of modifiers such as .card--sidebar, .card--mobile, and .card--compressed. Name the design intent, not the page where the component happened to be used first.
Practical component patterns
Responsive typography without scattered overrides
A component-level type scale can be expressed as a token.
.article-header {
--title-size: if(
media(width >= 64rem): clamp(2.5rem, 5vw, 4.5rem);
else: clamp(2rem, 9vw, 3rem)
);
}
.article-header h1 {
font-size: var(--title-size);
line-height: 1.04;
}The heading rule stays stable. Designers and developers can adjust the responsive decision where the token is introduced, rather than hunt through distant overrides.
A context-aware action bar
Container queries remain the right mechanism for a component that has less horizontal space, regardless of viewport size. if() can choose a value within that component.
.action-bar {
container-type: inline-size;
display: flex;
align-items: center;
gap: if(
media(width < 28rem): 0.5rem;
else: 1rem
);
}
.action-bar__label {
font-size: if(
media(width < 28rem): 0.875rem;
else: 1rem
);
}For larger changes, use @container to adjust layout. For a few related values, conditional tokens reduce repetition. The goal is not to make every stylesheet clever; it is to make the component's responsive contract readable.
Theme-aware values with a safe default
Many systems combine user preferences, brand themes, and accessibility settings. Conditional CSS can keep a decorative enhancement from becoming a dependency.
.notice {
--notice-border: if(
media(prefers-contrast: more): currentColor;
else: color-mix(in oklab, currentColor 35%, transparent)
);
border: 2px solid var(--notice-border);
}Do not use this to hide accessibility work. Semantic markup, focus states, contrast testing, and motion reduction still require deliberate design. It is simply a tidy way to choose a visual value from a preference the browser already exposes.
Progressive enhancement and fallbacks
Because CSS if() is relatively new, deployment discipline matters. The browser should receive a fully acceptable baseline before it sees an enhancement.
The safest pattern is a conventional declaration followed by the conditional version:
.hero {
padding-inline: 1rem;
padding-inline: if(media(width >= 48rem): 2rem; else: 1rem);
}A browser that does not understand the latter declaration discards it and retains the first. A supporting browser uses the more expressive version. Test this in the browsers and versions your audience actually uses, not only in a local evergreen browser.
A few practical rules help:
- Use it for values, not control flow. Keep normal queries for multiple property changes.
- Make the fallback good, not merely non-broken. Progressive enhancement should not produce a second-class experience.
- Avoid a JavaScript polyfill by default. A visual convenience rarely justifies extra runtime code.
- Put conditions near the token they resolve. That is the readability benefit; do not bury them in an unrelated utility file.
- Test zoom, forced colors, reduced motion, and narrow containers. A compact expression can still encode a bad design choice.
Where CSS if() should not be used
CSS if() is not a replacement for state management. It cannot decide whether to fetch data, whether a user has permission to act, whether a form is valid, or whether a navigation should occur. It should also not be used to conceal content that must be present or absent for semantic reasons.
Be cautious when a conditional declaration becomes hard to scan. This is less maintainable:
/* Technically compact, practically opaque */
.widget {
margin: if(media(width > 90rem): 3rem; media(width > 60rem): 2rem; style(--density: compact): 0.5rem; else: 1rem);
}The conditions mix unrelated concepts and make precedence difficult to reason about. Split the concern into named custom properties or regular query blocks. CSS has a cascade, layers, and custom properties already; if() should complement those tools rather than become a puzzle language.
A sensible adoption plan
Start small, with a component that has duplicated responsive values today. Good candidates include page gutters, spacing scales, type sizes, shadows, border treatments, and density tokens.
Then follow a lightweight review checklist:
- Is this only a presentation decision?
- Is the fallback declaration valid and attractive?
- Would a standard
@mediaor@containerblock be easier to read? - Are the conditions based on a real design intent?
- Have target browsers been verified?
For React, Next.js, Vue, or any other framework, this is also an opportunity to delete code. Remove matchMedia() hooks that only choose a class for spacing. Remove state that exists solely to mirror a browser media feature. Fewer renders are welcome, but the larger gain is ownership: the CSS engine owns CSS decisions.
The modern web platform is steadily filling in the gaps that once drove teams to framework code for basic UI work. CSS if() will not transform every stylesheet. Used with restraint, though, it makes conditional design tokens a first-class part of the cascade, which is exactly where they belong.
FAQ
What is CSS if()?
CSS if() is a conditional value function. It allows a declaration to select among CSS values based on media, support, or style conditions.
Does CSS if() replace media queries?
No. Use media and container queries for rule blocks and layout changes. if() is best for choosing a value inside one declaration or custom property.
Can CSS if() replace JavaScript responsive hooks?
It can replace hooks that exist only to make visual responsive decisions. JavaScript is still necessary when a condition changes data, behaviour, or accessible interaction.
How should teams ship CSS if() safely?
Provide a conventional fallback declaration first, then place the conditional declaration after it. Verify support across the browsers your product supports.
Is CSS if() useful with design tokens?
Yes. It is especially useful for responsive, capability-aware, or density-aware custom properties, because the condition and the value live together.