diff --git a/index.js b/index.js index 7ab0699..db716bc 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ const PREFERS_COLOR_ONLY = /^\(\s*prefers-color-scheme\s*:\s*(dark|light)\s*\)$/ const PREFERS_COLOR = /\(\s*prefers-color-scheme\s*:\s*(dark|light)\s*\)/g -const LIGHT_DARK = /light-dark\(\s*(.+?)\s*,\s*(.+?)\s*\)/g +const LIGHT_DARK = + /light-dark\(\s*((?:[^(),]|\(.+\))+?)\s*,\s*((?:[^(),]|\(.+\))+?)\s*\)/gs const STRING = /"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/dg function escapeRegExp(string) { @@ -30,15 +31,15 @@ function addColorSchemeMedia(isDark, propValue, declaration, postcss) { declaration.parent.after(mediaQuery) } -function replaceLightDark(isDark, declaration, stringBoundaries) { - return declaration.value.replaceAll( +function replaceLightDark(isDark, declarationValue, stringBoundaries) { + return declarationValue.replaceAll( LIGHT_DARK, (match, lightColor, darkColor, offset) => { let isInsideString = stringBoundaries.some( boundary => offset > boundary[0] && offset < boundary[1] ) if (isInsideString) return match - return isDark ? darkColor : lightColor + return replaceLightDark(isDark, isDark ? darkColor : lightColor, []) } ) } @@ -154,9 +155,17 @@ module.exports = (opts = {}) => { match = STRING.exec(value) } - let lightValue = replaceLightDark(false, declaration, stringBoundaries) + let lightValue = replaceLightDark( + false, + declaration.value, + stringBoundaries + ) if (declaration.value === lightValue) return - let darkValue = replaceLightDark(true, declaration, stringBoundaries) + let darkValue = replaceLightDark( + true, + declaration.value, + stringBoundaries + ) addColorSchemeMedia(false, lightValue, declaration, postcss) addColorSchemeMedia(true, darkValue, declaration, postcss) diff --git a/index.test.js b/index.test.js index 31c0fbb..90f4128 100644 --- a/index.test.js +++ b/index.test.js @@ -407,6 +407,83 @@ html:where(.is-light) { ) }) +test('transforms nested light-dark()', () => { + run( + `html { + border: 1px solid light-dark(light-dark(white, red), light-dark(blue, light-dark(gray, rgb(255 122 127 / .2)))) +}`, + `@media (prefers-color-scheme:dark) { + html:where(:not(.is-light)) { + border: 1px solid rgb(255 122 127 / .2) + } +} +html:where(.is-dark) { + border: 1px solid rgb(255 122 127 / .2) +} +@media (prefers-color-scheme:light) { + html:where(:not(.is-dark)) { + border: 1px solid white + } +} +html:where(.is-light) { + border: 1px solid white +}` + ) +}) + +test('transforms light-dark() with various color formats', () => { + run( + `html { + border: 1px solid light-dark(rgb(0, 0, 0), var(--color)); + color: light-dark( hsla(120, 100%, 50%, 0.3) , hsl( + var(--red-hue) + var(--red-sat) + calc(var(--red-lit) - 20%) + )); +}`, + `@media (prefers-color-scheme:dark) { + html:where(:not(.is-light)) { + color: hsl( + var(--red-hue) + var(--red-sat) + calc(var(--red-lit) - 20%) + ) + } +} +html:where(.is-dark) { + color: hsl( + var(--red-hue) + var(--red-sat) + calc(var(--red-lit) - 20%) + ) +} +@media (prefers-color-scheme:light) { + html:where(:not(.is-dark)) { + color: hsla(120, 100%, 50%, 0.3) + } +} +html:where(.is-light) { + color: hsla(120, 100%, 50%, 0.3) +} +@media (prefers-color-scheme:dark) { + html:where(:not(.is-light)) { + border: 1px solid var(--color) + } +} +html:where(.is-dark) { + border: 1px solid var(--color) +} +@media (prefers-color-scheme:light) { + html:where(:not(.is-dark)) { + border: 1px solid rgb(0, 0, 0) + } +} +html:where(.is-light) { + border: 1px solid rgb(0, 0, 0) +}` + ) +}) + test('does not transform light-dark() inside strings', () => { run( `html {