@@ -18,7 +18,45 @@ export const applyCssTranspilation = async ({
1818 customMedia : true ,
1919 } ,
2020 } ) ;
21- return { content : String ( code ) , sourcemap : map } ;
21+ return {
22+ content : defineLightDarkSwitchIfNeeded ( String ( code ) ) ,
23+ sourcemap : map ,
24+ } ;
25+ } ;
26+
27+ // The two custom properties lightningcss lowers "light-dark()" into. The pair
28+ // works as a switch: exactly one of them computes to the guaranteed-invalid
29+ // "initial" at a time, so exactly one of the two var() fallbacks survives.
30+ const LIGHT_DARK_SWITCH_CSS = `
31+ :root {
32+ --lightningcss-light: initial;
33+ --lightningcss-dark: ;
34+ }
35+ @media (prefers-color-scheme: dark) {
36+ :root {
37+ --lightningcss-light: ;
38+ --lightningcss-dark: initial;
39+ }
40+ }
41+ ` ;
42+
43+ // lightningcss writes that switch into the rule declaring "color-scheme", and
44+ // only there: a stylesheet lowering light-dark() without one references two
45+ // properties nobody defines, both var() fall back, the two values concatenate
46+ // and the declaration is dropped as invalid — a color silently missing rather
47+ // than an approximation of it. Each module owning a stylesheet of its own (see
48+ // jsenv:import_meta_css), the sheet declaring color-scheme is almost never the
49+ // sheet using light-dark(), so the switch is written here instead. Appended
50+ // rather than prepended: nothing else declares these two, so cascade order does
51+ // not matter, and the sourcemap of what comes before stays exact.
52+ const defineLightDarkSwitchIfNeeded = ( css ) => {
53+ if ( ! css . includes ( "var(--lightningcss-light" ) ) {
54+ return css ;
55+ }
56+ if ( css . includes ( "--lightningcss-light:" ) ) {
57+ return css ;
58+ }
59+ return `${ css } ${ LIGHT_DARK_SWITCH_CSS } ` ;
2260} ;
2361
2462// runtimeCompat names on the left, lightningcss target names on the right
0 commit comments