if() Conditional Value
Edit the CSS if() conditional value function (shipped 2025) with compile-time BRANCH-GRAMMAR validation. if() picks a value from a list of guarded branches: if( <condition> : <value> [ ; โฆ ]* ), where each condition is media(), supports(), or style(), and the final branch may be a bare else. The strict tier validates the if() wrapper (via the kit's ParseFunction), splits the body on TOP-LEVEL semicolons (a local paren-aware splitter โ the kit ships none), splits each branch on its FIRST top-level colon (so a colon inside style(--x: 1) is not a split), checks the condition kind, enforces else-as-last-branch, and requires a non-empty value. Condition bodies are validated leniently (non-empty + balanced parens); the runtime parser does fuller structural work. if(foo(x): 1) โ never (unknown kind); if(else: a; media(x): b) โ never (else not last).
Branch by condition
Controlled value + onChange. The CSS if() function picks a value from a list of guarded branches โ each branch is a condition : value separated by semicolons, where the condition is media(), supports(), style(), or a trailing else. Add branches and the editor emits a valid if() string.
if(media(width >= 800px): 2rem; else: 1rem)Build the branch list
Each row is one branch: a condition-kind select (media / supports / style), a condition-body input, and the value. Add or remove branches; the final row may switch to a trailing else. The produced if() string updates live.
if(media(width >= 800px): 2rem; supports(width: 1cqi): 1.5rem; else: 1rem)Heads up: the CSS if() function is cutting-edge โ it shipped in 2025 and browser support is still rolling out. Non-supporting browsers ignore the declaration and fall back.
Three usage tiers
From useState-and-go to compile-time branch-grammar validation.
Pass any string
useState<string>. No compile-time validation; the runtime parser splits the branches and classifies each condition โ including condition bodies the strict tier only checks for balance.
if(media(width >= 600px): grid; else: block)const [value, setValue] = useState<string>("if(media(width >= 600px): grid; else: block)")
Literal-shaped hints
State typed as IfFunctionString โ an if(โฆ)-shaped string. It is also the onChange return type, so downstream consumers stay in the suggestion union.
if(supports(display: grid): grid; else: block)const [value, setValue] = useState<IfFunctionString>("if(supports(display: grid): grid; else: block)")
Branch grammar typed at compile time
cssIf() validates the if() wrapper, splits branches on top-level semicolons, splits each branch on its first top-level colon, and checks the condition kind, the else-last rule, and a non-empty value โ resolving any violation to never before you run the code.
if(media(width >= 800px): red; supports(color: oklch(0 0 0)): green; else: blue)cssIf("if(media(width >= 800px): red; else: blue)") // โ // @ts-expect-error unknown condition kind cssIf("if(foo(x): 1)") // @ts-expect-error else not last cssIf("if(else: a; media(width >= 1px): b)")
Note: condition bodies (the media-query / supports-condition / style-query grammar) are validated leniently at the type level โ non-empty + balanced parens. The runtime parser does fuller structural work.
API
Public surface โ component props, runtime helpers, and the type exports.
ยง IfFunction / IfFunctionPanel
<IfFunction
value: IfFunctionString | (string & {})
onChange: (next: IfFunctionString) => void
className?: string
aria-label?: string
/>IfFunction is popover-wrapped; IfFunctionPanel renders the same editor inline. Both are controlled.
| Prop | Type | Description |
|---|---|---|
| value | IfFunctionString | (string & {}) | Current CSS if() value. Required. An empty / unparseable value renders an empty branch list. |
| onChange | (next: IfFunctionString) => void | Fires when the branch list changes. Emits the canonical `if( b1; b2; โฆ )` string. |
ยง Sub-components
<BranchRow branch onChange onRemove allowElse? index? />
One editable branch: a condition-kind select, a condition-body input (hidden for `else`, which shows `(always)`), a value input, and a remove button. `allowElse` gates the `else` option (final row only).
<ConditionKindSelect label value onChange allowElse? />
A labelled native select over media / supports / style (+ else when allowElse).
<AddBranchButton onAdd />
Appends a fresh media branch via onAdd.
<IfPreview value />
Applies the built if() string to a sample element's color, with a cutting-edge browser-support caption and a graceful fallback.
ยง Runtime helpers
cssIf<S>(value: S & IfFunctionLiteral<S>): S
Call-site validator for if(). Mirrors color() / easing() / cssCalc().
parseIf(src): IfBranch[] | null
String โ typed branches, or null on a bad wrapper / no branches / a branch without a top-level colon / an empty value / an unknown kind / an unbalanced body / else-not-last. Trailing and interior empty branches are tolerated.
formatIf(branches): string
Canonical re-serialization โ `if( b1; b2; โฆ )` (kind(condition): value, joined by `; `).
branchToCss(branch): string
One branch โ `else: value` or `kind(condition): value`.
branchCount(src): number
Runtime mirror of BranchCountOf โ the branch count (invalid โ 0).
defaultBranch(kind?): IfBranch
Seed a fresh branch (defaults to a media branch with a valid condition + value).
ยง Types
- IfFunctionLiteral<S>
- Strict validator โ S if S is a valid if() value (wrapper, branch split, condition kind, else-last, non-empty value), else never. Condition bodies validated leniently.
- IfFunctionString
- Suggestion union โ an if(โฆ)-shaped string. The onChange return type.
- ConditionString / ConditionKind
- A single condition suggestion union (media()/supports()/style()/else) and the kind discriminant.
- BranchesOf<S> / BranchCountOf<S>
- Tuple of the raw per-branch strings; and its length.
- ConditionKindsOf<S>
- Tuple of each branch's condition kind.
- IfBranch
- Per-branch record { kind; condition; value }. Exported for advanced use.
- IfFunctionState
- { branches: IfBranch[] } โ the internal editor state; `kind` is the per-branch discriminant.
ยง Strict-tier scope
- The
if( โฆ )wrapper is validated via the kit'sParseFunction(name must beif). - The body splits on top-level semicolons (a local paren-aware char-walk; the kit ships no semicolon splitter) into โฅ1 branch. A trailing
;is tolerated. - Each branch splits on its first top-level colon โ so a colon inside
style(--x: 1)or a value'surl(a:b)is not the split point. - The condition kind must be
media()/supports()/style()or a literalelseโelsepermitted only as the last branch. The value must be non-empty. - Condition bodies are weak-validated (non-empty + balanced parens). The internal media-query / supports-condition / style-query grammar is deferred to the runtime parser (and the media-query builder component).
calc()/var()inside a body or value is opaque text โ accepted as long as parens balance and the slot is non-empty.
Drop it in
One command via the shadcn CLI.
$ pnpm dlx shadcn@latest add https://turtiesocks.github.io/ridiculous/r/if-function.json