fix: resolve single box-shadow from runtime CSS variables#295
fix: resolve single box-shadow from runtime CSS variables#295YevheniiKotyrlo wants to merge 1 commit intonativewind:mainfrom
Conversation
|
@YevheniiKotyrlo Also please rebase this branch |
ffd33dd to
c7e3eb3
Compare
|
Rebased on latest The fix now also applies All quality gates pass (typecheck, lint, build with no unstaged files, test — 980 passed, 3 pre-existing babel failures same on |
There was a problem hiding this comment.
The flat vs nested detection makes sense, and the tests cover the important cases (single, multi, transparent filtering, theme switching).
The !Array.isArray(args[0]) check assumes runtime variables always produce flat arrays for single shadows. Is there a case where a single shadow could come through as [[0, 4, 6, -1, "#000"]] (nested but length 1)? If so it'd hit the old flatMap path, which should still work but worth a quick sanity check. If you've verified that, we're good.
Also the branch needs a rebase.
c7e3eb3 to
0b172b3
Compare
|
Good question. I traced through the compiler and runtime to verify —
return groups.length === 1 ? groups[0] : groups;So a single shadow always resolves to a flat array I've also expanded the test suite significantly — 34 box-shadow tests + 4 text-shadow tests now, structured as compile-time / runtime parity to prove both paths produce identical results. This covers single shadows, multi-shadow, inset, color-first, negative offsets, transparent filtering, Force pushing the rebased branch now (based on latest |
Summary
Fix
box-shadowshorthand resolution when CSS variables are resolved at runtime (not inlined at compile time). Currently, a single shadow from a runtime variable producesboxShadow: []instead of the expected shadow object.Problem
When a CSS variable holding a single
box-shadowvalue is resolved at runtime, the shorthand handler receives a flat array of tokens like[0, 4, 6, -1, "#000"]. The handler incorrectly iterates each primitive individually — passing0,4,6, etc. to the pattern matcher one at a time. No single primitive matches any shadow pattern, so all returnundefinedand get filtered out.The runtime path is triggered when variables are:
:root+.darkfor theme switching)@property(variable has an@propertyinitial value but no class-level definition)inlineVariables: false)The compile-time path (single-definition variables that get inlined) is not affected — lightningcss parses the full shadow value correctly.
Reproduction
Expected:
boxShadow: [{ offsetX: 0, offsetY: 4, blurRadius: 6, spreadDistance: -1, color: "#000" }]Actual:
boxShadow: []Solution
Detect whether the resolved args array is flat (single shadow) or nested (multiple shadows) before entering the multi-shadow handler:
[0, 4, 6, -1, "#000"]→ first element is a primitive → pass the entire array to the pattern handler as a single shadow[[0, 4, 6, -1, "#000"], [0, 1, 2, 0, "#333"]]→ first element is an array → use existingflatMaplogic for multiple shadowsThe existing multi-shadow path (nested arrays from comma-separated
box-shadowvalues) is unchanged. The flat array path also appliesomitTransparentShadowsandnormalizeInsetValuefor correct handling.Why
[[0, 4, 6, -1, "#000"]](nested length-1) cannot happenreduceParseUnparsedindeclarations.ts(line 1075) always unwraps single groups:A single shadow always produces a flat array. Multiple shadows produce a nested array. There is no path through the compiler or runtime variable resolver that wraps a single shadow in an extra array layer.
Verification
yarn typecheck— passyarn lint— passyarn build— pass (ESM + CJS + DTS)yarn test— 1014 passed, 3 failed (pre-existing babel plugin tests onmain), 21 skipped (pre-existing)Test architecture (38 tests: 34 box-shadow + 4 text-shadow)
Tests are structured with compile/runtime parity — each scenario is tested through both the compile-time and runtime variable resolution paths to prove they produce identical results.
Static CSS (10 tests) — single shadow (basic, color-first, negative offsets, no spread, no color), inset shadow (basic, color-first, no color, no spread), mixed inset + outset
Compile-time CSS variables (6 tests) — single nested var, deep nested vars, theme switching (multi-definition), inset via var, inset no-spread via var, transparent via var
Runtime variables (15 tests) — same scenarios as above with
{ inlineVariables: false }, plus: multi-shadow from separate vars, multi-shadow from single var, multi-shadow with transparent filtering@propertydefaults (4 tests) — transparent defaults filtered, currentcolor platform object, Tailwind ring pattern (3 vars, 2 transparent) via both compile-time and runtimeText-shadow regression (4 tests) — compile-time and runtime parity, proving text-shadow is unaffected (different resolver architecture — no
flatMap)Related
normalizeInsetValueon the flat array path