Prompt and Context
A design system needs one padding, color, or flex-direction value to vary with media, style, or feature conditions without duplicating an entire rule set for every variant. The team proposes CSS if(), but older browsers still need to work. Give the adoption boundary, syntax strategy, and verification plan.
This tests CSS value resolution and the cascade, not whether someone can move JavaScript conditions into a stylesheet. MDN says if() returns the first true branch in order; the CSS Values 5 draft says an unmatched function produces an empty token stream and does not roll the declaration back like @media. A 2026 HTML/CSS interview guide still treats layout, cascade, and browser differences as core signals, so the answer must place the new syntax inside a compatibility and debugging plan.
What the Interviewer Evaluates
Separate the conditions: media() describes the environment, style() reads an ancestor style query, and supports() tests browser capability. A strong candidate writes a stable declaration first and uses a second declaration for enhancement; they do not assume a parser preserves the old value inside one declaration after seeing an unknown function.
The interviewer also looks for branch order, the always-true else, guaranteed-invalid outcomes, and how multiple declarations enter the cascade. The answer should include a browser matrix, DevTools inspection, and an acceptable visual fallback with the new feature disabled.
Questions to Clarify Before Answering
Where does the condition come from?
Ask whether it is viewport orientation, a container custom property, or browser support for a value. Use media() for viewport or preference, style() for component state, and supports() for capability. If several declarations change together, ordinary @media, @container, or @supports may be clearer.
What must the fallback preserve?
Clarify whether old browsers only need readable content or must preserve exact spacing, contrast, and interaction. For accessibility or layout safety, provide a stable declaration first and override it with if(); do not allow an unmatched branch to fall back to an initial property value.
Can the component be overridden through the cascade?
Identify which ancestor owns the custom property and whether a theme layer can override it. A style() query observes a style relationship; making a variable depend on itself can create a cyclic substitution. Define an owner and namespace so a component does not read the value it is currently computing.
30-Second Answer Framework
“I first identify the condition source, then choose media(), style(), or supports(). if() checks branches in order and else is the stable fallback; without a match the result can be guaranteed-invalid, so I keep a traditional declaration for older browsers. Conditions spanning several rules still belong in @media, @container, or @supports. I verify parsing, cascade, contrast, and layout in real browsers instead of treating an experimental function as universally production-ready.”
Step-by-Step Deep Dive
Step 1: Match each condition to its responsibility
Use media() for orientation, width, and user preference; style() for state exposed by an ancestor or container; and supports() for feature capability. if() reduces duplication when one property value changes. When several properties, pseudo-elements, or a subtree change, a rule-level query is easier to audit.
Step 2: Make evaluation order explicit
CSS Values 5 defines an ordered list of conditions and values. The browser tests the first branch and continues until one is true; else is always true and therefore belongs last. Example:
.card {
padding: 1rem;
padding: if(
style(--density: compact): 0.5rem;
media(width > 60rem): 1.25rem;
else: 1rem;
);
}The first match stops evaluation. Put more specific branches before general ones, or a broad branch can make a later branch unreachable.
Step 3: Understand unmatched branches and old-browser fallback
Without a matching condition or else, if() can produce a guaranteed-invalid or empty token stream. That may invalidate the declaration or expose the property’s initial behavior; it is not a promise to keep the previous declaration. For critical layout, write a static declaration first and an if() declaration second. A browser that does not parse if() skips the second line and keeps the first.
.toolbar {
flex-direction: row;
flex-direction: if(media(orientation: portrait): column; else: row);
}In a browser that parses if(), an explicit else still covers an unmatched condition. If the fallback is a group of rules, @supports (padding: if(...)) is easier to maintain than repeating a complex condition on every property.
Step 4: Handle style-query cascade and cycles
A style() query usually reads a custom property exposed by an ancestor or container. A theme can set --density: compact, and a component can derive its padding from that state. Do not define --size in terms of if(style(--size: ...)); the CSSWG describes this as a cyclic substitution, so the query evaluates false. Give state variables and derived properties different names and document their owner.
Step 5: Compare if() with rule-level queries
When only one part of color varies with support, if(supports(...)) keeps the value near the property. When several declarations change, @supports groups the rule. When layout depends on container width, @container or a rule-level media query is often clearer than a long inline function. Fewer lines are not the only goal; review and debugging cost matter.
Step 6: Build a browser and visual-regression matrix
Run the page in browsers that support and do not support if(). Inspect the final CSSOM value, cascade origin, contrast, focus order, and overflow. Cover an unmatched condition, multiple true conditions, an undefined variable, an invalid property value, and a parse failure. If the feature only improves decoration, keep it in an enhancement layer; if it changes accessibility, preserve an acceptable static baseline.
High-Quality Sample Answer
I would first ask where the condition comes from and what the fallback must preserve. Use media for viewport or preference, style for component state, and supports for capability. Use if() for one property value; use @media, @container, or @supports when a group of rules changes.
Evaluation follows source order. The first true branch wins and else is always true. Without a match and without else, the result can be guaranteed-invalid, so a critical property gets a stable declaration first and an if() override second. Older browsers skip the unknown declaration; supporting browsers use the explicit else.
I would separate theme state from derived values to avoid style-query cycles and put specific branches first. Tests cover support, final CSSOM values, cascade, contrast, overflow, and unmatched branches. If a rule-level query is clearer, I would use it rather than trading maintainability for a few fewer lines.
Common Mistakes
- Mistake → Omitting a static fallback → Why it fails → A browser without if() skips the entire declaration → Fix → Write a stable value first, then override with if().
- Mistake → Treating if() as rule-level @media → Why it fails → It returns one property value and cannot replace a multi-declaration layout branch → Fix → Use a rule-level query for several properties.
- Mistake → Omitting else → Why it fails → No match can produce a guaranteed-invalid value → Fix → Provide a default or retain a prior declaration.
- Mistake → Making a style query read the variable it is computing → Why it fails → Cyclic substitution makes the query fail → Fix → Separate state and derived properties.
- Mistake → Claiming compatibility because parsing succeeds → Why it fails → Experimental features can be partial or inconsistent → Fix → Verify real browsers with a support matrix and visual regression.
Follow-up Questions and Responses
Follow-up 1: When do you choose if() over @supports?
Use if(supports(...)) when one property value changes, such as a color format. Use @supports when multiple declarations, pseudo-elements, or layout rules change; it keeps the unsupported path together. They can be combined, but do not put page-wide logic into one function.
Follow-up 2: What if two conditions are true?
The first true branch in source order wins, and later branches do not participate. Put the more specific or higher-priority condition first and document the intended exclusivity; otherwise a new branch can be shadowed by an earlier general branch.
Follow-up 3: Can an unsupported browser preserve the old value automatically?
Use two declarations: a stable value first, then the if() value. An older parser skips the unknown second declaration. Do not put both values inside one declaration, and do not assume a supporting browser restores the previous declaration when no branch matches.
Follow-up 4: What if style() makes component state hard to trace?
Give state variables a namespace and one owner, then inspect computed values and cascade sources in DevTools. If state crosses many components or changes many properties, switch to an explicit class, data attribute, or rule-level query for observability.