diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a7793e09915..702e1e2af3ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - _Experimental_: Add `user-valid` and `user-invalid` variants ([#12370](https://github.com/tailwindlabs/tailwindcss/pull/12370)) +### Changed + +- Don't include theme variables that aren't used in compiled CSS ([#16211](https://github.com/tailwindlabs/tailwindcss/pull/16211), [#16676](https://github.com/tailwindlabs/tailwindcss/pull/16676)) + ### Fixed - Remove invalid `!important` on CSS variable declarations ([#16668](https://github.com/tailwindlabs/tailwindcss/pull/16668)) @@ -44,9 +48,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Revert change to no longer include theme variables that aren't used in compiled CSS ([#16403](https://github.com/tailwindlabs/tailwindcss/pull/16403)) - -### Fixed - - Upgrade: Don't migrate `blur` to `blur-sm` when used with Next.js `` ([#16405](https://github.com/tailwindlabs/tailwindcss/pull/16405)) ## [4.0.5] - 2025-02-08 diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts index 6f48e9f9c77c..2eee0413dc4f 100644 --- a/integrations/cli/index.test.ts +++ b/integrations/cli/index.test.ts @@ -1289,9 +1289,7 @@ test( expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` " --- ./dist/out.css --- - :root, :host { - --color-blue-500: blue; - } + " `) diff --git a/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap b/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap index d401ace41bed..dbf73d755e12 100644 --- a/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap +++ b/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap @@ -4,363 +4,11 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` "@layer theme { :root, :host { --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --color-red-50: oklch(.971 .013 17.38); - --color-red-100: oklch(.936 .032 17.717); - --color-red-200: oklch(.885 .062 18.334); - --color-red-300: oklch(.808 .114 19.571); - --color-red-400: oklch(.704 .191 22.216); - --color-red-500: oklch(.637 .237 25.331); - --color-red-600: oklch(.577 .245 27.325); - --color-red-700: oklch(.505 .213 27.518); - --color-red-800: oklch(.444 .177 26.899); - --color-red-900: oklch(.396 .141 25.723); - --color-red-950: oklch(.258 .092 26.042); - --color-orange-50: oklch(.98 .016 73.684); - --color-orange-100: oklch(.954 .038 75.164); - --color-orange-200: oklch(.901 .076 70.697); - --color-orange-300: oklch(.837 .128 66.29); - --color-orange-400: oklch(.75 .183 55.934); - --color-orange-500: oklch(.705 .213 47.604); - --color-orange-600: oklch(.646 .222 41.116); - --color-orange-700: oklch(.553 .195 38.402); - --color-orange-800: oklch(.47 .157 37.304); - --color-orange-900: oklch(.408 .123 38.172); - --color-orange-950: oklch(.266 .079 36.259); - --color-amber-50: oklch(.987 .022 95.277); - --color-amber-100: oklch(.962 .059 95.617); - --color-amber-200: oklch(.924 .12 95.746); - --color-amber-300: oklch(.879 .169 91.605); - --color-amber-400: oklch(.828 .189 84.429); - --color-amber-500: oklch(.769 .188 70.08); - --color-amber-600: oklch(.666 .179 58.318); - --color-amber-700: oklch(.555 .163 48.998); - --color-amber-800: oklch(.473 .137 46.201); - --color-amber-900: oklch(.414 .112 45.904); - --color-amber-950: oklch(.279 .077 45.635); - --color-yellow-50: oklch(.987 .026 102.212); - --color-yellow-100: oklch(.973 .071 103.193); - --color-yellow-200: oklch(.945 .129 101.54); - --color-yellow-300: oklch(.905 .182 98.111); - --color-yellow-400: oklch(.852 .199 91.936); - --color-yellow-500: oklch(.795 .184 86.047); - --color-yellow-600: oklch(.681 .162 75.834); - --color-yellow-700: oklch(.554 .135 66.442); - --color-yellow-800: oklch(.476 .114 61.907); - --color-yellow-900: oklch(.421 .095 57.708); - --color-yellow-950: oklch(.286 .066 53.813); - --color-lime-50: oklch(.986 .031 120.757); - --color-lime-100: oklch(.967 .067 122.328); - --color-lime-200: oklch(.938 .127 124.321); - --color-lime-300: oklch(.897 .196 126.665); - --color-lime-400: oklch(.841 .238 128.85); - --color-lime-500: oklch(.768 .233 130.85); - --color-lime-600: oklch(.648 .2 131.684); - --color-lime-700: oklch(.532 .157 131.589); - --color-lime-800: oklch(.453 .124 130.933); - --color-lime-900: oklch(.405 .101 131.063); - --color-lime-950: oklch(.274 .072 132.109); - --color-green-50: oklch(.982 .018 155.826); - --color-green-100: oklch(.962 .044 156.743); - --color-green-200: oklch(.925 .084 155.995); - --color-green-300: oklch(.871 .15 154.449); - --color-green-400: oklch(.792 .209 151.711); - --color-green-500: oklch(.723 .219 149.579); - --color-green-600: oklch(.627 .194 149.214); - --color-green-700: oklch(.527 .154 150.069); - --color-green-800: oklch(.448 .119 151.328); - --color-green-900: oklch(.393 .095 152.535); - --color-green-950: oklch(.266 .065 152.934); - --color-emerald-50: oklch(.979 .021 166.113); - --color-emerald-100: oklch(.95 .052 163.051); - --color-emerald-200: oklch(.905 .093 164.15); - --color-emerald-300: oklch(.845 .143 164.978); - --color-emerald-400: oklch(.765 .177 163.223); - --color-emerald-500: oklch(.696 .17 162.48); - --color-emerald-600: oklch(.596 .145 163.225); - --color-emerald-700: oklch(.508 .118 165.612); - --color-emerald-800: oklch(.432 .095 166.913); - --color-emerald-900: oklch(.378 .077 168.94); - --color-emerald-950: oklch(.262 .051 172.552); - --color-teal-50: oklch(.984 .014 180.72); - --color-teal-100: oklch(.953 .051 180.801); - --color-teal-200: oklch(.91 .096 180.426); - --color-teal-300: oklch(.855 .138 181.071); - --color-teal-400: oklch(.777 .152 181.912); - --color-teal-500: oklch(.704 .14 182.503); - --color-teal-600: oklch(.6 .118 184.704); - --color-teal-700: oklch(.511 .096 186.391); - --color-teal-800: oklch(.437 .078 188.216); - --color-teal-900: oklch(.386 .063 188.416); - --color-teal-950: oklch(.277 .046 192.524); - --color-cyan-50: oklch(.984 .019 200.873); - --color-cyan-100: oklch(.956 .045 203.388); - --color-cyan-200: oklch(.917 .08 205.041); - --color-cyan-300: oklch(.865 .127 207.078); - --color-cyan-400: oklch(.789 .154 211.53); - --color-cyan-500: oklch(.715 .143 215.221); - --color-cyan-600: oklch(.609 .126 221.723); - --color-cyan-700: oklch(.52 .105 223.128); - --color-cyan-800: oklch(.45 .085 224.283); - --color-cyan-900: oklch(.398 .07 227.392); - --color-cyan-950: oklch(.302 .056 229.695); - --color-sky-50: oklch(.977 .013 236.62); - --color-sky-100: oklch(.951 .026 236.824); - --color-sky-200: oklch(.901 .058 230.902); - --color-sky-300: oklch(.828 .111 230.318); - --color-sky-400: oklch(.746 .16 232.661); - --color-sky-500: oklch(.685 .169 237.323); - --color-sky-600: oklch(.588 .158 241.966); - --color-sky-700: oklch(.5 .134 242.749); - --color-sky-800: oklch(.443 .11 240.79); - --color-sky-900: oklch(.391 .09 240.876); - --color-sky-950: oklch(.293 .066 243.157); - --color-blue-50: oklch(.97 .014 254.604); - --color-blue-100: oklch(.932 .032 255.585); - --color-blue-200: oklch(.882 .059 254.128); - --color-blue-300: oklch(.809 .105 251.813); - --color-blue-400: oklch(.707 .165 254.624); - --color-blue-500: oklch(.623 .214 259.815); - --color-blue-600: oklch(.546 .245 262.881); - --color-blue-700: oklch(.488 .243 264.376); - --color-blue-800: oklch(.424 .199 265.638); - --color-blue-900: oklch(.379 .146 265.522); - --color-blue-950: oklch(.282 .091 267.935); - --color-indigo-50: oklch(.962 .018 272.314); - --color-indigo-100: oklch(.93 .034 272.788); - --color-indigo-200: oklch(.87 .065 274.039); - --color-indigo-300: oklch(.785 .115 274.713); - --color-indigo-400: oklch(.673 .182 276.935); - --color-indigo-500: oklch(.585 .233 277.117); - --color-indigo-600: oklch(.511 .262 276.966); - --color-indigo-700: oklch(.457 .24 277.023); - --color-indigo-800: oklch(.398 .195 277.366); - --color-indigo-900: oklch(.359 .144 278.697); - --color-indigo-950: oklch(.257 .09 281.288); - --color-violet-50: oklch(.969 .016 293.756); - --color-violet-100: oklch(.943 .029 294.588); - --color-violet-200: oklch(.894 .057 293.283); - --color-violet-300: oklch(.811 .111 293.571); - --color-violet-400: oklch(.702 .183 293.541); - --color-violet-500: oklch(.606 .25 292.717); - --color-violet-600: oklch(.541 .281 293.009); - --color-violet-700: oklch(.491 .27 292.581); - --color-violet-800: oklch(.432 .232 292.759); - --color-violet-900: oklch(.38 .189 293.745); - --color-violet-950: oklch(.283 .141 291.089); - --color-purple-50: oklch(.977 .014 308.299); - --color-purple-100: oklch(.946 .033 307.174); - --color-purple-200: oklch(.902 .063 306.703); - --color-purple-300: oklch(.827 .119 306.383); - --color-purple-400: oklch(.714 .203 305.504); - --color-purple-500: oklch(.627 .265 303.9); - --color-purple-600: oklch(.558 .288 302.321); - --color-purple-700: oklch(.496 .265 301.924); - --color-purple-800: oklch(.438 .218 303.724); - --color-purple-900: oklch(.381 .176 304.987); - --color-purple-950: oklch(.291 .149 302.717); - --color-fuchsia-50: oklch(.977 .017 320.058); - --color-fuchsia-100: oklch(.952 .037 318.852); - --color-fuchsia-200: oklch(.903 .076 319.62); - --color-fuchsia-300: oklch(.833 .145 321.434); - --color-fuchsia-400: oklch(.74 .238 322.16); - --color-fuchsia-500: oklch(.667 .295 322.15); - --color-fuchsia-600: oklch(.591 .293 322.896); - --color-fuchsia-700: oklch(.518 .253 323.949); - --color-fuchsia-800: oklch(.452 .211 324.591); - --color-fuchsia-900: oklch(.401 .17 325.612); - --color-fuchsia-950: oklch(.293 .136 325.661); - --color-pink-50: oklch(.971 .014 343.198); - --color-pink-100: oklch(.948 .028 342.258); - --color-pink-200: oklch(.899 .061 343.231); - --color-pink-300: oklch(.823 .12 346.018); - --color-pink-400: oklch(.718 .202 349.761); - --color-pink-500: oklch(.656 .241 354.308); - --color-pink-600: oklch(.592 .249 .584); - --color-pink-700: oklch(.525 .223 3.958); - --color-pink-800: oklch(.459 .187 3.815); - --color-pink-900: oklch(.408 .153 2.432); - --color-pink-950: oklch(.284 .109 3.907); - --color-rose-50: oklch(.969 .015 12.422); - --color-rose-100: oklch(.941 .03 12.58); - --color-rose-200: oklch(.892 .058 10.001); - --color-rose-300: oklch(.81 .117 11.638); - --color-rose-400: oklch(.712 .194 13.428); - --color-rose-500: oklch(.645 .246 16.439); - --color-rose-600: oklch(.586 .253 17.585); - --color-rose-700: oklch(.514 .222 16.935); - --color-rose-800: oklch(.455 .188 13.697); - --color-rose-900: oklch(.41 .159 10.272); - --color-rose-950: oklch(.271 .105 12.094); - --color-slate-50: oklch(.984 .003 247.858); - --color-slate-100: oklch(.968 .007 247.896); - --color-slate-200: oklch(.929 .013 255.508); - --color-slate-300: oklch(.869 .022 252.894); - --color-slate-400: oklch(.704 .04 256.788); - --color-slate-500: oklch(.554 .046 257.417); - --color-slate-600: oklch(.446 .043 257.281); - --color-slate-700: oklch(.372 .044 257.287); - --color-slate-800: oklch(.279 .041 260.031); - --color-slate-900: oklch(.208 .042 265.755); - --color-slate-950: oklch(.129 .042 264.695); - --color-gray-50: oklch(.985 .002 247.839); - --color-gray-100: oklch(.967 .003 264.542); - --color-gray-200: oklch(.928 .006 264.531); - --color-gray-300: oklch(.872 .01 258.338); - --color-gray-400: oklch(.707 .022 261.325); - --color-gray-500: oklch(.551 .027 264.364); - --color-gray-600: oklch(.446 .03 256.802); - --color-gray-700: oklch(.373 .034 259.733); - --color-gray-800: oklch(.278 .033 256.848); - --color-gray-900: oklch(.21 .034 264.665); - --color-gray-950: oklch(.13 .028 261.692); - --color-zinc-50: oklch(.985 0 0); - --color-zinc-100: oklch(.967 .001 286.375); - --color-zinc-200: oklch(.92 .004 286.32); - --color-zinc-300: oklch(.871 .006 286.286); - --color-zinc-400: oklch(.705 .015 286.067); - --color-zinc-500: oklch(.552 .016 285.938); - --color-zinc-600: oklch(.442 .017 285.786); - --color-zinc-700: oklch(.37 .013 285.805); - --color-zinc-800: oklch(.274 .006 286.033); - --color-zinc-900: oklch(.21 .006 285.885); - --color-zinc-950: oklch(.141 .005 285.823); - --color-neutral-50: oklch(.985 0 0); - --color-neutral-100: oklch(.97 0 0); - --color-neutral-200: oklch(.922 0 0); - --color-neutral-300: oklch(.87 0 0); - --color-neutral-400: oklch(.708 0 0); - --color-neutral-500: oklch(.556 0 0); - --color-neutral-600: oklch(.439 0 0); - --color-neutral-700: oklch(.371 0 0); - --color-neutral-800: oklch(.269 0 0); - --color-neutral-900: oklch(.205 0 0); - --color-neutral-950: oklch(.145 0 0); - --color-stone-50: oklch(.985 .001 106.423); - --color-stone-100: oklch(.97 .001 106.424); - --color-stone-200: oklch(.923 .003 48.717); - --color-stone-300: oklch(.869 .005 56.366); - --color-stone-400: oklch(.709 .01 56.259); - --color-stone-500: oklch(.553 .013 58.071); - --color-stone-600: oklch(.444 .011 73.639); - --color-stone-700: oklch(.374 .01 67.558); - --color-stone-800: oklch(.268 .007 34.298); - --color-stone-900: oklch(.216 .006 56.043); - --color-stone-950: oklch(.147 .004 49.25); --color-black: #000; - --color-white: #fff; - --spacing: .25rem; - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - --container-3xs: 16rem; - --container-2xs: 18rem; - --container-xs: 20rem; - --container-sm: 24rem; - --container-md: 28rem; - --container-lg: 32rem; - --container-xl: 36rem; - --container-2xl: 42rem; - --container-3xl: 48rem; - --container-4xl: 56rem; - --container-5xl: 64rem; - --container-6xl: 72rem; - --container-7xl: 80rem; - --text-xs: .75rem; - --text-xs--line-height: calc(1 / .75); - --text-sm: .875rem; - --text-sm--line-height: calc(1.25 / .875); - --text-base: 1rem; - --text-base--line-height: calc(1.5 / 1); - --text-lg: 1.125rem; - --text-lg--line-height: calc(1.75 / 1.125); - --text-xl: 1.25rem; - --text-xl--line-height: calc(1.75 / 1.25); --text-2xl: 1.5rem; --text-2xl--line-height: calc(2 / 1.5); - --text-3xl: 1.875rem; - --text-3xl--line-height: calc(2.25 / 1.875); - --text-4xl: 2.25rem; - --text-4xl--line-height: calc(2.5 / 2.25); - --text-5xl: 3rem; - --text-5xl--line-height: 1; - --text-6xl: 3.75rem; - --text-6xl--line-height: 1; - --text-7xl: 4.5rem; - --text-7xl--line-height: 1; - --text-8xl: 6rem; - --text-8xl--line-height: 1; - --text-9xl: 8rem; - --text-9xl--line-height: 1; - --font-weight-thin: 100; - --font-weight-extralight: 200; - --font-weight-light: 300; - --font-weight-normal: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; --font-weight-bold: 700; - --font-weight-extrabold: 800; - --font-weight-black: 900; - --tracking-tighter: -.05em; - --tracking-tight: -.025em; - --tracking-normal: 0em; - --tracking-wide: .025em; - --tracking-wider: .05em; - --tracking-widest: .1em; - --leading-tight: 1.25; - --leading-snug: 1.375; - --leading-normal: 1.5; - --leading-relaxed: 1.625; - --leading-loose: 2; - --radius-xs: .125rem; - --radius-sm: .25rem; - --radius-md: .375rem; - --radius-lg: .5rem; - --radius-xl: .75rem; - --radius-2xl: 1rem; - --radius-3xl: 1.5rem; - --radius-4xl: 2rem; - --shadow-2xs: 0 1px #0000000d; - --shadow-xs: 0 1px 2px 0 #0000000d; - --shadow-sm: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --shadow-md: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --shadow-lg: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - --shadow-xl: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a; - --shadow-2xl: 0 25px 50px -12px #00000040; - --inset-shadow-2xs: inset 0 1px #0000000d; - --inset-shadow-xs: inset 0 1px 1px #0000000d; - --inset-shadow-sm: inset 0 2px 4px #0000000d; - --drop-shadow-xs: 0 1px 1px #0000000d; - --drop-shadow-sm: 0 1px 2px #00000026; - --drop-shadow-md: 0 3px 3px #0000001f; - --drop-shadow-lg: 0 4px 4px #00000026; - --drop-shadow-xl: 0 9px 7px #0000001a; - --drop-shadow-2xl: 0 25px 25px #00000026; - --ease-in: cubic-bezier(.4, 0, 1, 1); - --ease-out: cubic-bezier(0, 0, .2, 1); - --ease-in-out: cubic-bezier(.4, 0, .2, 1); - --animate-spin: spin 1s linear infinite; - --animate-ping: ping 1s cubic-bezier(0, 0, .2, 1) infinite; - --animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; - --animate-bounce: bounce 1s infinite; - --blur-xs: 4px; - --blur-sm: 8px; - --blur-md: 12px; - --blur-lg: 16px; - --blur-xl: 24px; - --blur-2xl: 40px; - --blur-3xl: 64px; - --perspective-dramatic: 100px; - --perspective-near: 300px; - --perspective-normal: 500px; - --perspective-midrange: 800px; - --perspective-distant: 1200px; - --aspect-video: 16 / 9; - --default-transition-duration: .15s; - --default-transition-timing-function: cubic-bezier(.4, 0, .2, 1); --default-font-family: var(--font-sans); --default-font-feature-settings: var(--font-sans--font-feature-settings); --default-font-variation-settings: var(--font-sans--font-variation-settings); @@ -630,37 +278,6 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` } } -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -@keyframes ping { - 75%, 100% { - opacity: 0; - transform: scale(2); - } -} - -@keyframes pulse { - 50% { - opacity: .5; - } -} - -@keyframes bounce { - 0%, 100% { - animation-timing-function: cubic-bezier(.8, 0, 1, 1); - transform: translateY(-25%); - } - - 50% { - animation-timing-function: cubic-bezier(0, 0, .2, 1); - transform: none; - } -} - @property --tw-font-weight { syntax: "*"; inherits: false diff --git a/packages/@tailwindcss-postcss/src/index.test.ts b/packages/@tailwindcss-postcss/src/index.test.ts index 97eee3c1c5f0..3a7e1b8b896c 100644 --- a/packages/@tailwindcss-postcss/src/index.test.ts +++ b/packages/@tailwindcss-postcss/src/index.test.ts @@ -330,11 +330,7 @@ test('runs `Once` plugins in the right order', async () => { ) expect(result.css.trim()).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .custom-css { + ".custom-css { color: red; }" `) @@ -347,11 +343,7 @@ test('runs `Once` plugins in the right order', async () => { }" `) expect(after).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .custom-css { + ".custom-css { color: red; }" `) diff --git a/packages/tailwindcss/src/__snapshots__/index.test.ts.snap b/packages/tailwindcss/src/__snapshots__/index.test.ts.snap index 9ed291c3908a..5e88fe462d53 100644 --- a/packages/tailwindcss/src/__snapshots__/index.test.ts.snap +++ b/packages/tailwindcss/src/__snapshots__/index.test.ts.snap @@ -2,370 +2,8 @@ exports[`compiling CSS > \`@tailwind utilities\` is replaced by utilities using the default theme 1`] = ` ":root, :host { - --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; - --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --color-red-50: oklch(.971 .013 17.38); - --color-red-100: oklch(.936 .032 17.717); - --color-red-200: oklch(.885 .062 18.334); - --color-red-300: oklch(.808 .114 19.571); - --color-red-400: oklch(.704 .191 22.216); --color-red-500: oklch(.637 .237 25.331); - --color-red-600: oklch(.577 .245 27.325); - --color-red-700: oklch(.505 .213 27.518); - --color-red-800: oklch(.444 .177 26.899); - --color-red-900: oklch(.396 .141 25.723); - --color-red-950: oklch(.258 .092 26.042); - --color-orange-50: oklch(.98 .016 73.684); - --color-orange-100: oklch(.954 .038 75.164); - --color-orange-200: oklch(.901 .076 70.697); - --color-orange-300: oklch(.837 .128 66.29); - --color-orange-400: oklch(.75 .183 55.934); - --color-orange-500: oklch(.705 .213 47.604); - --color-orange-600: oklch(.646 .222 41.116); - --color-orange-700: oklch(.553 .195 38.402); - --color-orange-800: oklch(.47 .157 37.304); - --color-orange-900: oklch(.408 .123 38.172); - --color-orange-950: oklch(.266 .079 36.259); - --color-amber-50: oklch(.987 .022 95.277); - --color-amber-100: oklch(.962 .059 95.617); - --color-amber-200: oklch(.924 .12 95.746); - --color-amber-300: oklch(.879 .169 91.605); - --color-amber-400: oklch(.828 .189 84.429); - --color-amber-500: oklch(.769 .188 70.08); - --color-amber-600: oklch(.666 .179 58.318); - --color-amber-700: oklch(.555 .163 48.998); - --color-amber-800: oklch(.473 .137 46.201); - --color-amber-900: oklch(.414 .112 45.904); - --color-amber-950: oklch(.279 .077 45.635); - --color-yellow-50: oklch(.987 .026 102.212); - --color-yellow-100: oklch(.973 .071 103.193); - --color-yellow-200: oklch(.945 .129 101.54); - --color-yellow-300: oklch(.905 .182 98.111); - --color-yellow-400: oklch(.852 .199 91.936); - --color-yellow-500: oklch(.795 .184 86.047); - --color-yellow-600: oklch(.681 .162 75.834); - --color-yellow-700: oklch(.554 .135 66.442); - --color-yellow-800: oklch(.476 .114 61.907); - --color-yellow-900: oklch(.421 .095 57.708); - --color-yellow-950: oklch(.286 .066 53.813); - --color-lime-50: oklch(.986 .031 120.757); - --color-lime-100: oklch(.967 .067 122.328); - --color-lime-200: oklch(.938 .127 124.321); - --color-lime-300: oklch(.897 .196 126.665); - --color-lime-400: oklch(.841 .238 128.85); - --color-lime-500: oklch(.768 .233 130.85); - --color-lime-600: oklch(.648 .2 131.684); - --color-lime-700: oklch(.532 .157 131.589); - --color-lime-800: oklch(.453 .124 130.933); - --color-lime-900: oklch(.405 .101 131.063); - --color-lime-950: oklch(.274 .072 132.109); - --color-green-50: oklch(.982 .018 155.826); - --color-green-100: oklch(.962 .044 156.743); - --color-green-200: oklch(.925 .084 155.995); - --color-green-300: oklch(.871 .15 154.449); - --color-green-400: oklch(.792 .209 151.711); - --color-green-500: oklch(.723 .219 149.579); - --color-green-600: oklch(.627 .194 149.214); - --color-green-700: oklch(.527 .154 150.069); - --color-green-800: oklch(.448 .119 151.328); - --color-green-900: oklch(.393 .095 152.535); - --color-green-950: oklch(.266 .065 152.934); - --color-emerald-50: oklch(.979 .021 166.113); - --color-emerald-100: oklch(.95 .052 163.051); - --color-emerald-200: oklch(.905 .093 164.15); - --color-emerald-300: oklch(.845 .143 164.978); - --color-emerald-400: oklch(.765 .177 163.223); - --color-emerald-500: oklch(.696 .17 162.48); - --color-emerald-600: oklch(.596 .145 163.225); - --color-emerald-700: oklch(.508 .118 165.612); - --color-emerald-800: oklch(.432 .095 166.913); - --color-emerald-900: oklch(.378 .077 168.94); - --color-emerald-950: oklch(.262 .051 172.552); - --color-teal-50: oklch(.984 .014 180.72); - --color-teal-100: oklch(.953 .051 180.801); - --color-teal-200: oklch(.91 .096 180.426); - --color-teal-300: oklch(.855 .138 181.071); - --color-teal-400: oklch(.777 .152 181.912); - --color-teal-500: oklch(.704 .14 182.503); - --color-teal-600: oklch(.6 .118 184.704); - --color-teal-700: oklch(.511 .096 186.391); - --color-teal-800: oklch(.437 .078 188.216); - --color-teal-900: oklch(.386 .063 188.416); - --color-teal-950: oklch(.277 .046 192.524); - --color-cyan-50: oklch(.984 .019 200.873); - --color-cyan-100: oklch(.956 .045 203.388); - --color-cyan-200: oklch(.917 .08 205.041); - --color-cyan-300: oklch(.865 .127 207.078); - --color-cyan-400: oklch(.789 .154 211.53); - --color-cyan-500: oklch(.715 .143 215.221); - --color-cyan-600: oklch(.609 .126 221.723); - --color-cyan-700: oklch(.52 .105 223.128); - --color-cyan-800: oklch(.45 .085 224.283); - --color-cyan-900: oklch(.398 .07 227.392); - --color-cyan-950: oklch(.302 .056 229.695); - --color-sky-50: oklch(.977 .013 236.62); - --color-sky-100: oklch(.951 .026 236.824); - --color-sky-200: oklch(.901 .058 230.902); - --color-sky-300: oklch(.828 .111 230.318); - --color-sky-400: oklch(.746 .16 232.661); - --color-sky-500: oklch(.685 .169 237.323); - --color-sky-600: oklch(.588 .158 241.966); - --color-sky-700: oklch(.5 .134 242.749); - --color-sky-800: oklch(.443 .11 240.79); - --color-sky-900: oklch(.391 .09 240.876); - --color-sky-950: oklch(.293 .066 243.157); - --color-blue-50: oklch(.97 .014 254.604); - --color-blue-100: oklch(.932 .032 255.585); - --color-blue-200: oklch(.882 .059 254.128); - --color-blue-300: oklch(.809 .105 251.813); - --color-blue-400: oklch(.707 .165 254.624); - --color-blue-500: oklch(.623 .214 259.815); - --color-blue-600: oklch(.546 .245 262.881); - --color-blue-700: oklch(.488 .243 264.376); - --color-blue-800: oklch(.424 .199 265.638); - --color-blue-900: oklch(.379 .146 265.522); - --color-blue-950: oklch(.282 .091 267.935); - --color-indigo-50: oklch(.962 .018 272.314); - --color-indigo-100: oklch(.93 .034 272.788); - --color-indigo-200: oklch(.87 .065 274.039); - --color-indigo-300: oklch(.785 .115 274.713); - --color-indigo-400: oklch(.673 .182 276.935); - --color-indigo-500: oklch(.585 .233 277.117); - --color-indigo-600: oklch(.511 .262 276.966); - --color-indigo-700: oklch(.457 .24 277.023); - --color-indigo-800: oklch(.398 .195 277.366); - --color-indigo-900: oklch(.359 .144 278.697); - --color-indigo-950: oklch(.257 .09 281.288); - --color-violet-50: oklch(.969 .016 293.756); - --color-violet-100: oklch(.943 .029 294.588); - --color-violet-200: oklch(.894 .057 293.283); - --color-violet-300: oklch(.811 .111 293.571); - --color-violet-400: oklch(.702 .183 293.541); - --color-violet-500: oklch(.606 .25 292.717); - --color-violet-600: oklch(.541 .281 293.009); - --color-violet-700: oklch(.491 .27 292.581); - --color-violet-800: oklch(.432 .232 292.759); - --color-violet-900: oklch(.38 .189 293.745); - --color-violet-950: oklch(.283 .141 291.089); - --color-purple-50: oklch(.977 .014 308.299); - --color-purple-100: oklch(.946 .033 307.174); - --color-purple-200: oklch(.902 .063 306.703); - --color-purple-300: oklch(.827 .119 306.383); - --color-purple-400: oklch(.714 .203 305.504); - --color-purple-500: oklch(.627 .265 303.9); - --color-purple-600: oklch(.558 .288 302.321); - --color-purple-700: oklch(.496 .265 301.924); - --color-purple-800: oklch(.438 .218 303.724); - --color-purple-900: oklch(.381 .176 304.987); - --color-purple-950: oklch(.291 .149 302.717); - --color-fuchsia-50: oklch(.977 .017 320.058); - --color-fuchsia-100: oklch(.952 .037 318.852); - --color-fuchsia-200: oklch(.903 .076 319.62); - --color-fuchsia-300: oklch(.833 .145 321.434); - --color-fuchsia-400: oklch(.74 .238 322.16); - --color-fuchsia-500: oklch(.667 .295 322.15); - --color-fuchsia-600: oklch(.591 .293 322.896); - --color-fuchsia-700: oklch(.518 .253 323.949); - --color-fuchsia-800: oklch(.452 .211 324.591); - --color-fuchsia-900: oklch(.401 .17 325.612); - --color-fuchsia-950: oklch(.293 .136 325.661); - --color-pink-50: oklch(.971 .014 343.198); - --color-pink-100: oklch(.948 .028 342.258); - --color-pink-200: oklch(.899 .061 343.231); - --color-pink-300: oklch(.823 .12 346.018); - --color-pink-400: oklch(.718 .202 349.761); - --color-pink-500: oklch(.656 .241 354.308); - --color-pink-600: oklch(.592 .249 .584); - --color-pink-700: oklch(.525 .223 3.958); - --color-pink-800: oklch(.459 .187 3.815); - --color-pink-900: oklch(.408 .153 2.432); - --color-pink-950: oklch(.284 .109 3.907); - --color-rose-50: oklch(.969 .015 12.422); - --color-rose-100: oklch(.941 .03 12.58); - --color-rose-200: oklch(.892 .058 10.001); - --color-rose-300: oklch(.81 .117 11.638); - --color-rose-400: oklch(.712 .194 13.428); - --color-rose-500: oklch(.645 .246 16.439); - --color-rose-600: oklch(.586 .253 17.585); - --color-rose-700: oklch(.514 .222 16.935); - --color-rose-800: oklch(.455 .188 13.697); - --color-rose-900: oklch(.41 .159 10.272); - --color-rose-950: oklch(.271 .105 12.094); - --color-slate-50: oklch(.984 .003 247.858); - --color-slate-100: oklch(.968 .007 247.896); - --color-slate-200: oklch(.929 .013 255.508); - --color-slate-300: oklch(.869 .022 252.894); - --color-slate-400: oklch(.704 .04 256.788); - --color-slate-500: oklch(.554 .046 257.417); - --color-slate-600: oklch(.446 .043 257.281); - --color-slate-700: oklch(.372 .044 257.287); - --color-slate-800: oklch(.279 .041 260.031); - --color-slate-900: oklch(.208 .042 265.755); - --color-slate-950: oklch(.129 .042 264.695); - --color-gray-50: oklch(.985 .002 247.839); - --color-gray-100: oklch(.967 .003 264.542); - --color-gray-200: oklch(.928 .006 264.531); - --color-gray-300: oklch(.872 .01 258.338); - --color-gray-400: oklch(.707 .022 261.325); - --color-gray-500: oklch(.551 .027 264.364); - --color-gray-600: oklch(.446 .03 256.802); - --color-gray-700: oklch(.373 .034 259.733); - --color-gray-800: oklch(.278 .033 256.848); - --color-gray-900: oklch(.21 .034 264.665); - --color-gray-950: oklch(.13 .028 261.692); - --color-zinc-50: oklch(.985 0 0); - --color-zinc-100: oklch(.967 .001 286.375); - --color-zinc-200: oklch(.92 .004 286.32); - --color-zinc-300: oklch(.871 .006 286.286); - --color-zinc-400: oklch(.705 .015 286.067); - --color-zinc-500: oklch(.552 .016 285.938); - --color-zinc-600: oklch(.442 .017 285.786); - --color-zinc-700: oklch(.37 .013 285.805); - --color-zinc-800: oklch(.274 .006 286.033); - --color-zinc-900: oklch(.21 .006 285.885); - --color-zinc-950: oklch(.141 .005 285.823); - --color-neutral-50: oklch(.985 0 0); - --color-neutral-100: oklch(.97 0 0); - --color-neutral-200: oklch(.922 0 0); - --color-neutral-300: oklch(.87 0 0); - --color-neutral-400: oklch(.708 0 0); - --color-neutral-500: oklch(.556 0 0); - --color-neutral-600: oklch(.439 0 0); - --color-neutral-700: oklch(.371 0 0); - --color-neutral-800: oklch(.269 0 0); - --color-neutral-900: oklch(.205 0 0); - --color-neutral-950: oklch(.145 0 0); - --color-stone-50: oklch(.985 .001 106.423); - --color-stone-100: oklch(.97 .001 106.424); - --color-stone-200: oklch(.923 .003 48.717); - --color-stone-300: oklch(.869 .005 56.366); - --color-stone-400: oklch(.709 .01 56.259); - --color-stone-500: oklch(.553 .013 58.071); - --color-stone-600: oklch(.444 .011 73.639); - --color-stone-700: oklch(.374 .01 67.558); - --color-stone-800: oklch(.268 .007 34.298); - --color-stone-900: oklch(.216 .006 56.043); - --color-stone-950: oklch(.147 .004 49.25); - --color-black: #000; - --color-white: #fff; --spacing: .25rem; - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - --container-3xs: 16rem; - --container-2xs: 18rem; - --container-xs: 20rem; - --container-sm: 24rem; - --container-md: 28rem; - --container-lg: 32rem; - --container-xl: 36rem; - --container-2xl: 42rem; - --container-3xl: 48rem; - --container-4xl: 56rem; - --container-5xl: 64rem; - --container-6xl: 72rem; - --container-7xl: 80rem; - --text-xs: .75rem; - --text-xs--line-height: calc(1 / .75); - --text-sm: .875rem; - --text-sm--line-height: calc(1.25 / .875); - --text-base: 1rem; - --text-base--line-height: calc(1.5 / 1); - --text-lg: 1.125rem; - --text-lg--line-height: calc(1.75 / 1.125); - --text-xl: 1.25rem; - --text-xl--line-height: calc(1.75 / 1.25); - --text-2xl: 1.5rem; - --text-2xl--line-height: calc(2 / 1.5); - --text-3xl: 1.875rem; - --text-3xl--line-height: calc(2.25 / 1.875); - --text-4xl: 2.25rem; - --text-4xl--line-height: calc(2.5 / 2.25); - --text-5xl: 3rem; - --text-5xl--line-height: 1; - --text-6xl: 3.75rem; - --text-6xl--line-height: 1; - --text-7xl: 4.5rem; - --text-7xl--line-height: 1; - --text-8xl: 6rem; - --text-8xl--line-height: 1; - --text-9xl: 8rem; - --text-9xl--line-height: 1; - --font-weight-thin: 100; - --font-weight-extralight: 200; - --font-weight-light: 300; - --font-weight-normal: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - --font-weight-extrabold: 800; - --font-weight-black: 900; - --tracking-tighter: -.05em; - --tracking-tight: -.025em; - --tracking-normal: 0em; - --tracking-wide: .025em; - --tracking-wider: .05em; - --tracking-widest: .1em; - --leading-tight: 1.25; - --leading-snug: 1.375; - --leading-normal: 1.5; - --leading-relaxed: 1.625; - --leading-loose: 2; - --radius-xs: .125rem; - --radius-sm: .25rem; - --radius-md: .375rem; - --radius-lg: .5rem; - --radius-xl: .75rem; - --radius-2xl: 1rem; - --radius-3xl: 1.5rem; - --radius-4xl: 2rem; - --shadow-2xs: 0 1px #0000000d; - --shadow-xs: 0 1px 2px 0 #0000000d; - --shadow-sm: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --shadow-md: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --shadow-lg: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - --shadow-xl: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a; - --shadow-2xl: 0 25px 50px -12px #00000040; - --inset-shadow-2xs: inset 0 1px #0000000d; - --inset-shadow-xs: inset 0 1px 1px #0000000d; - --inset-shadow-sm: inset 0 2px 4px #0000000d; - --drop-shadow-xs: 0 1px 1px #0000000d; - --drop-shadow-sm: 0 1px 2px #00000026; - --drop-shadow-md: 0 3px 3px #0000001f; - --drop-shadow-lg: 0 4px 4px #00000026; - --drop-shadow-xl: 0 9px 7px #0000001a; - --drop-shadow-2xl: 0 25px 25px #00000026; - --ease-in: cubic-bezier(.4, 0, 1, 1); - --ease-out: cubic-bezier(0, 0, .2, 1); - --ease-in-out: cubic-bezier(.4, 0, .2, 1); - --animate-spin: spin 1s linear infinite; - --animate-ping: ping 1s cubic-bezier(0, 0, .2, 1) infinite; - --animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; - --animate-bounce: bounce 1s infinite; - --blur-xs: 4px; - --blur-sm: 8px; - --blur-md: 12px; - --blur-lg: 16px; - --blur-xl: 24px; - --blur-2xl: 40px; - --blur-3xl: 64px; - --perspective-dramatic: 100px; - --perspective-near: 300px; - --perspective-normal: 500px; - --perspective-midrange: 800px; - --perspective-distant: 1200px; - --aspect-video: 16 / 9; - --default-transition-duration: .15s; - --default-transition-timing-function: cubic-bezier(.4, 0, .2, 1); - --default-font-family: var(--font-sans); - --default-font-feature-settings: var(--font-sans--font-feature-settings); - --default-font-variation-settings: var(--font-sans--font-variation-settings); - --default-mono-font-family: var(--font-mono); - --default-mono-font-feature-settings: var(--font-mono--font-feature-settings); - --default-mono-font-variation-settings: var(--font-mono--font-variation-settings); } .w-4 { @@ -387,37 +25,6 @@ exports[`compiling CSS > \`@tailwind utilities\` is replaced by utilities using } } -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -@keyframes ping { - 75%, 100% { - opacity: 0; - transform: scale(2); - } -} - -@keyframes pulse { - 50% { - opacity: .5; - } -} - -@keyframes bounce { - 0%, 100% { - animation-timing-function: cubic-bezier(.8, 0, 1, 1); - transform: translateY(-25%); - } - - 50% { - animation-timing-function: cubic-bezier(0, 0, .2, 1); - transform: none; - } -} - @property --tw-shadow { syntax: "*"; inherits: false; diff --git a/packages/tailwindcss/src/__snapshots__/utilities.test.ts.snap b/packages/tailwindcss/src/__snapshots__/utilities.test.ts.snap index 0fe26f6582b5..9d35f62cc069 100644 --- a/packages/tailwindcss/src/__snapshots__/utilities.test.ts.snap +++ b/packages/tailwindcss/src/__snapshots__/utilities.test.ts.snap @@ -2,9 +2,6 @@ exports[`border-* 1`] = ` ":root, :host { - --radius-none: 0px; - --radius-full: 9999px; - --radius-sm: .125rem; --color-red-500: #ef4444; } @@ -127,9 +124,6 @@ exports[`border-* 1`] = ` exports[`border-b-* 1`] = ` ":root, :host { - --radius-none: 0px; - --radius-full: 9999px; - --radius-sm: .125rem; --color-red-500: #ef4444; } @@ -252,9 +246,6 @@ exports[`border-b-* 1`] = ` exports[`border-e-* 1`] = ` ":root, :host { - --radius-none: 0px; - --radius-full: 9999px; - --radius-sm: .125rem; --color-red-500: #ef4444; } @@ -377,9 +368,6 @@ exports[`border-e-* 1`] = ` exports[`border-l-* 1`] = ` ":root, :host { - --radius-none: 0px; - --radius-full: 9999px; - --radius-sm: .125rem; --color-red-500: #ef4444; } @@ -502,9 +490,6 @@ exports[`border-l-* 1`] = ` exports[`border-r-* 1`] = ` ":root, :host { - --radius-none: 0px; - --radius-full: 9999px; - --radius-sm: .125rem; --color-red-500: #ef4444; } @@ -627,9 +612,6 @@ exports[`border-r-* 1`] = ` exports[`border-s-* 1`] = ` ":root, :host { - --radius-none: 0px; - --radius-full: 9999px; - --radius-sm: .125rem; --color-red-500: #ef4444; } @@ -752,9 +734,6 @@ exports[`border-s-* 1`] = ` exports[`border-t-* 1`] = ` ":root, :host { - --radius-none: 0px; - --radius-full: 9999px; - --radius-sm: .125rem; --color-red-500: #ef4444; } @@ -877,9 +856,6 @@ exports[`border-t-* 1`] = ` exports[`border-x-* 1`] = ` ":root, :host { - --radius-none: 0px; - --radius-full: 9999px; - --radius-sm: .125rem; --color-red-500: #ef4444; } @@ -1002,9 +978,6 @@ exports[`border-x-* 1`] = ` exports[`border-y-* 1`] = ` ":root, :host { - --radius-none: 0px; - --radius-full: 9999px; - --radius-sm: .125rem; --color-red-500: #ef4444; } diff --git a/packages/tailwindcss/src/ast.ts b/packages/tailwindcss/src/ast.ts index edf0e4e235bc..36194d9f41fc 100644 --- a/packages/tailwindcss/src/ast.ts +++ b/packages/tailwindcss/src/ast.ts @@ -1,8 +1,8 @@ import { parseAtRule } from './css-parser' import type { DesignSystem } from './design-system' -import { enableRemoveUnusedThemeVariables } from './feature-flags' -import { ThemeOptions } from './theme' +import { Theme, ThemeOptions } from './theme' import { DefaultMap } from './utils/default-map' +import { extractUsedVariables } from './utils/variables' const AT_SIGN = 0x40 @@ -256,7 +256,11 @@ export function walkDepth( // Optimize the AST for printing where all the special nodes that require custom // handling are handled such that the printing is a 1-to-1 transformation. -export function optimizeAst(ast: AstNode[], designSystem: DesignSystem) { +export function optimizeAst( + ast: AstNode[], + designSystem: DesignSystem, + firstThemeRule: StyleRule | null, +) { let atRoots: AstNode[] = [] let seenAtProperties = new Set() let cssThemeVariables = new DefaultMap< @@ -266,6 +270,8 @@ export function optimizeAst(ast: AstNode[], designSystem: DesignSystem) { let keyframes = new Set() let usedKeyframeNames = new Set() + let variableDependencies = new DefaultMap>(() => new Set()) + function transform( node: AstNode, parent: Extract['nodes'], @@ -285,7 +291,15 @@ export function optimizeAst(ast: AstNode[], designSystem: DesignSystem) { // Track used CSS variables if (node.value.includes('var(')) { - designSystem.trackUsedVariables(node.value) + // Declaring another variable does not count as usage. Instead, we mark + // the relationship + if (context.theme && node.property[0] === '-' && node.property[1] === '-') { + for (let variable of extractUsedVariables(node.value)) { + variableDependencies.get(variable).add(node.property) + } + } else { + designSystem.trackUsedVariables(node.value) + } } // Track used animation names @@ -376,13 +390,32 @@ export function optimizeAst(ast: AstNode[], designSystem: DesignSystem) { // Context else if (node.kind === 'context') { - // Remove reference imports from printing if (node.context.reference) { - return - } + if (firstThemeRule) { + let path = findNode(node.nodes, (node) => node === firstThemeRule) + if (path) { + let newPathToFirstThemeRule = path + .filter((node) => node.kind === 'at-rule') + .map((node) => ({ ...node, nodes: [] })) + .reverse() as AtRule[] + + let child = firstThemeRule as AstNode + for (let node of newPathToFirstThemeRule) { + node.nodes = [child] + child = node + } - for (let child of node.nodes) { - transform(child, parent, { ...context, ...node.context }, depth) + let newParent: AstNode[] = [] + transform(child, newParent, { ...context, ...node.context, reference: false }, depth) + parent.push(...newParent) + } + } + + return + } else { + for (let child of node.nodes) { + transform(child, parent, { ...context, ...node.context }, depth) + } } } @@ -403,49 +436,67 @@ export function optimizeAst(ast: AstNode[], designSystem: DesignSystem) { } // Remove unused theme variables - if (enableRemoveUnusedThemeVariables) { - next: for (let [parent, declarations] of cssThemeVariables) { - for (let declaration of declarations) { - let options = designSystem.theme.getOptions(declaration.property) - - if (options & (ThemeOptions.STATIC | ThemeOptions.USED)) { - if (declaration.property.startsWith('--animate-')) { - let parts = declaration.value!.split(/\s+/) - for (let part of parts) usedKeyframeNames.add(part) - } - - continue + next: for (let [parent, declarations] of cssThemeVariables) { + for (let declaration of declarations) { + // Find out if a variable is either used directly or if any of the + // variables referencing it is used. + let variableUsed = isVariableUsed( + declaration.property, + designSystem.theme, + variableDependencies, + ) + if (variableUsed) { + if (declaration.property.startsWith('--animate-')) { + let parts = declaration.value!.split(/\s+/) + for (let part of parts) usedKeyframeNames.add(part) } - // Remove the declaration (from its parent) - let idx = parent.indexOf(declaration) - parent.splice(idx, 1) - - // If the parent is now empty, remove it from the AST - if (parent.length === 0) { - for (let [idx, node] of newAst.entries()) { - // Assumption, but right now the `@theme` must be top-level, so we - // don't need to traverse the entire AST to find the parent. - // - // Checking for `rule`, because at this stage the `@theme` is already - // converted to a normal style rule `:root, :host` - if (node.kind === 'rule' && node.nodes === parent) { - newAst.splice(idx, 1) - break - } - } + continue + } - continue next - } + // Remove the declaration (from its parent) + let idx = parent.indexOf(declaration) + parent.splice(idx, 1) + + // If the parent is now empty, remove it and any `@layer` rules above it + // from the AST + if (parent.length === 0) { + let path = findNode(newAst, (node) => node.kind === 'rule' && node.nodes === parent) + + if (!path || path.length === 0) continue next + + // Add the root of the AST so we can delete from the top-level + path.unshift({ + kind: 'at-root', + nodes: newAst, + }) + + // Remove nodes from the parent as long as the parent is empty + // otherwise and consist of only @layer rules + do { + let nodeToRemove = path.pop() + if (!nodeToRemove) break + + let removeFrom = path[path.length - 1] + if (!removeFrom) break + if (removeFrom.kind !== 'at-root' && removeFrom.kind !== 'at-rule') break + + let idx = removeFrom.nodes.indexOf(nodeToRemove) + if (idx === -1) break + + removeFrom.nodes.splice(idx, 1) + } while (true) + + continue next } } + } - // Remove unused keyframes - for (let keyframe of keyframes) { - if (!usedKeyframeNames.has(keyframe.params)) { - let idx = atRoots.indexOf(keyframe) - atRoots.splice(idx, 1) - } + // Remove unused keyframes + for (let keyframe of keyframes) { + if (!usedKeyframeNames.has(keyframe.params)) { + let idx = atRoots.indexOf(keyframe) + atRoots.splice(idx, 1) } } @@ -522,3 +573,44 @@ export function toCss(ast: AstNode[]) { return css } + +function findNode(ast: AstNode[], fn: (node: AstNode) => boolean): AstNode[] | null { + let foundPath: AstNode[] = [] + walk(ast, (node, { path }) => { + if (fn(node)) { + foundPath = [...path] + return WalkAction.Stop + } + }) + return foundPath +} + +// Find out if a variable is either used directly or if any of the variables that depend on it are +// used +function isVariableUsed( + variable: string, + theme: Theme, + variableDependencies: Map>, + alreadySeenVariables: Set = new Set(), +): boolean { + // Break recursions when visiting a variable twice + if (alreadySeenVariables.has(variable)) { + return true + } else { + alreadySeenVariables.add(variable) + } + + let options = theme.getOptions(variable) + if (options & (ThemeOptions.STATIC | ThemeOptions.USED)) { + return true + } else { + let dependencies = variableDependencies.get(variable) ?? [] + for (let dependency of dependencies) { + if (isVariableUsed(dependency, theme, variableDependencies, alreadySeenVariables)) { + return true + } + } + } + + return false +} diff --git a/packages/tailwindcss/src/at-import.test.ts b/packages/tailwindcss/src/at-import.test.ts index 07922e83a44a..81ab16eda1fa 100644 --- a/packages/tailwindcss/src/at-import.test.ts +++ b/packages/tailwindcss/src/at-import.test.ts @@ -580,7 +580,11 @@ test('resolves @reference as `@import "…" reference`', async () => { { loadStylesheet, candidates: ['text-red-500'] }, ), ).resolves.toMatchInlineSnapshot(` - ".text-red-500 { + ":root, :host { + --color-red-500: red; + } + + .text-red-500 { color: var(--color-red-500); } " diff --git a/packages/tailwindcss/src/compat/config.test.ts b/packages/tailwindcss/src/compat/config.test.ts index eb18cd734f21..55b91ec394e0 100644 --- a/packages/tailwindcss/src/compat/config.test.ts +++ b/packages/tailwindcss/src/compat/config.test.ts @@ -322,11 +322,7 @@ describe('theme callbacks', () => { expect(compiler.build(['leading-base', 'leading-md', 'leading-xl', 'prose'])) .toMatchInlineSnapshot(` - ":root, :host { - --text-base: 100rem; - --text-md--line-height: 101rem; - } - .prose { + ".prose { [class~=lead-base] { font-size: 100rem; line-height: 201rem; @@ -562,12 +558,7 @@ describe('default font family compatibility', () => { }) expect(compiler.build(['font-sans'])).toMatchInlineSnapshot(` - ":root, :host { - --default-font-family: Potato Sans; - --default-font-feature-settings: normal; - --default-font-variation-settings: normal; - } - .font-sans { + ".font-sans { font-family: Potato Sans; } " @@ -601,12 +592,7 @@ describe('default font family compatibility', () => { }) expect(compiler.build(['font-sans'])).toMatchInlineSnapshot(` - ":root, :host { - --default-font-family: Potato Sans; - --default-font-feature-settings: "cv06"; - --default-font-variation-settings: normal; - } - .font-sans { + ".font-sans { font-family: Potato Sans; font-feature-settings: "cv06"; } @@ -641,12 +627,7 @@ describe('default font family compatibility', () => { }) expect(compiler.build(['font-sans'])).toMatchInlineSnapshot(` - ":root, :host { - --default-font-family: Potato Sans; - --default-font-feature-settings: normal; - --default-font-variation-settings: "XHGT" 0.7; - } - .font-sans { + ".font-sans { font-family: Potato Sans; font-variation-settings: "XHGT" 0.7; } @@ -684,12 +665,7 @@ describe('default font family compatibility', () => { }) expect(compiler.build(['font-sans'])).toMatchInlineSnapshot(` - ":root, :host { - --default-font-family: Potato Sans; - --default-font-feature-settings: "cv06"; - --default-font-variation-settings: "XHGT" 0.7; - } - .font-sans { + ".font-sans { font-family: Potato Sans; font-feature-settings: "cv06"; font-variation-settings: "XHGT" 0.7; @@ -729,9 +705,6 @@ describe('default font family compatibility', () => { expect(compiler.build(['font-sans'])).toMatchInlineSnapshot(` ":root, :host { - --default-font-family: var(--font-family-sans); - --default-font-feature-settings: var(--font-family-sans--font-feature-settings); - --default-font-variation-settings: var(--font-family-sans--font-variation-settings); --font-sans: Sandwich Sans; } .font-sans { @@ -768,12 +741,7 @@ describe('default font family compatibility', () => { }) expect(compiler.build(['font-sans'])).toMatchInlineSnapshot(` - ":root, :host { - --default-font-family: Inter, system-ui, sans-serif; - --default-font-feature-settings: normal; - --default-font-variation-settings: normal; - } - .font-sans { + ".font-sans { font-family: Inter, system-ui, sans-serif; } " @@ -806,14 +774,7 @@ describe('default font family compatibility', () => { }), }) - expect(compiler.build(['font-sans'])).toMatchInlineSnapshot(` - ":root, :host { - --default-font-family: var(--font-family-sans); - --default-font-feature-settings: var(--font-family-sans--font-feature-settings); - --default-font-variation-settings: var(--font-family-sans--font-variation-settings); - } - " - `) + expect(compiler.build(['font-sans'])).toMatchInlineSnapshot(`""`) }) test('overriding `fontFamily.mono` sets `--default-mono-font-family`', async () => { @@ -841,12 +802,7 @@ describe('default font family compatibility', () => { }) expect(compiler.build(['font-mono'])).toMatchInlineSnapshot(` - ":root, :host { - --default-mono-font-family: Potato Mono; - --default-mono-font-feature-settings: normal; - --default-mono-font-variation-settings: normal; - } - .font-mono { + ".font-mono { font-family: Potato Mono; } " @@ -880,12 +836,7 @@ describe('default font family compatibility', () => { }) expect(compiler.build(['font-mono'])).toMatchInlineSnapshot(` - ":root, :host { - --default-mono-font-family: Potato Mono; - --default-mono-font-feature-settings: "cv06"; - --default-mono-font-variation-settings: normal; - } - .font-mono { + ".font-mono { font-family: Potato Mono; font-feature-settings: "cv06"; } @@ -920,12 +871,7 @@ describe('default font family compatibility', () => { }) expect(compiler.build(['font-mono'])).toMatchInlineSnapshot(` - ":root, :host { - --default-mono-font-family: Potato Mono; - --default-mono-font-feature-settings: normal; - --default-mono-font-variation-settings: "XHGT" 0.7; - } - .font-mono { + ".font-mono { font-family: Potato Mono; font-variation-settings: "XHGT" 0.7; } @@ -963,12 +909,7 @@ describe('default font family compatibility', () => { }) expect(compiler.build(['font-mono'])).toMatchInlineSnapshot(` - ":root, :host { - --default-mono-font-family: Potato Mono; - --default-mono-font-feature-settings: "cv06"; - --default-mono-font-variation-settings: "XHGT" 0.7; - } - .font-mono { + ".font-mono { font-family: Potato Mono; font-feature-settings: "cv06"; font-variation-settings: "XHGT" 0.7; @@ -1008,9 +949,6 @@ describe('default font family compatibility', () => { expect(compiler.build(['font-mono'])).toMatchInlineSnapshot(` ":root, :host { - --default-mono-font-family: var(--font-mono); - --default-mono-font-feature-settings: var(--font-mono--font-feature-settings); - --default-mono-font-variation-settings: var(--font-mono--font-variation-settings); --font-mono: Sandwich Mono; } .font-mono { @@ -1046,14 +984,7 @@ describe('default font family compatibility', () => { }), }) - expect(compiler.build(['font-mono'])).toMatchInlineSnapshot(` - ":root, :host { - --default-mono-font-family: var(--font-family-mono); - --default-mono-font-feature-settings: var(--font-family-mono--font-feature-settings); - --default-mono-font-variation-settings: var(--font-family-mono--font-variation-settings); - } - " - `) + expect(compiler.build(['font-mono'])).toMatchInlineSnapshot(`""`) }) }) @@ -1179,13 +1110,7 @@ test('merges css breakpoints with js config screens', async () => { expect(compiler.build(['sm:flex', 'md:flex', 'lg:flex', 'min-sm:max-md:underline'])) .toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 50rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .sm\\:flex { + ".sm\\:flex { @media (width >= 44rem) { display: flex; } @@ -1333,12 +1258,7 @@ test('Prefixes configured in CSS take precedence over those defined in JS config ) expect(compiler.build(['wat:custom'])).toMatchInlineSnapshot(` - ":root, :host { - --wat-color-red: #f00; - --wat-color-green: #0f0; - --wat-breakpoint-sm: 640px; - } - .wat\\:custom { + ".wat\\:custom { color: red; } " diff --git a/packages/tailwindcss/src/compat/container-config.test.ts b/packages/tailwindcss/src/compat/container-config.test.ts index e2b9a0ca591f..b5618de84135 100644 --- a/packages/tailwindcss/src/compat/container-config.test.ts +++ b/packages/tailwindcss/src/compat/container-config.test.ts @@ -31,14 +31,7 @@ test('creates a custom utility to extend the built-in container', async () => { }) expect(compiler.build(['container'])).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .container { + ".container { width: 100%; @media (width >= 40rem) { max-width: 40rem; @@ -96,14 +89,7 @@ test('allows padding to be defined at custom breakpoints', async () => { }) expect(compiler.build(['container'])).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .container { + ".container { width: 100%; @media (width >= 40rem) { max-width: 40rem; @@ -164,14 +150,7 @@ test('allows breakpoints to be overwritten', async () => { }) expect(compiler.build(['container'])).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .container { + ".container { width: 100%; @media (width >= 40rem) { max-width: 40rem; @@ -237,14 +216,7 @@ test('padding applies to custom `container` screens', async () => { }) expect(compiler.build(['container'])).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .container { + ".container { width: 100%; @media (width >= 40rem) { max-width: 40rem; @@ -307,14 +279,7 @@ test("an empty `screen` config will undo all custom media screens and won't appl }) expect(compiler.build(['container'])).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .container { + ".container { width: 100%; @media (width >= 40rem) { max-width: 40rem; @@ -380,20 +345,7 @@ test('legacy container component does not interfere with new --container variabl expect(compiler.build(['max-w-sm'])).toMatchInlineSnapshot(` ":root, :host { - --container-3xs: 16rem; - --container-2xs: 18rem; - --container-xs: 20rem; --container-sm: 24rem; - --container-md: 28rem; - --container-lg: 32rem; - --container-xl: 36rem; - --container-2xl: 42rem; - --container-3xl: 48rem; - --container-4xl: 56rem; - --container-5xl: 64rem; - --container-6xl: 72rem; - --container-7xl: 80rem; - --container-prose: 65ch; } .max-w-sm { max-width: var(--container-sm); @@ -438,14 +390,7 @@ test('combines custom padding and screen overwrites', async () => { }) expect(compiler.build(['container', '!container'])).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .\\!container { + ".\\!container { width: 100% !important; @media (width >= 40rem) { max-width: 40rem !important; @@ -557,14 +502,7 @@ test('filters out complex breakpoints', async () => { }) expect(compiler.build(['container'])).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .container { + ".container { width: 100%; @media (width >= 40rem) { max-width: 40rem; diff --git a/packages/tailwindcss/src/compat/plugin-api.test.ts b/packages/tailwindcss/src/compat/plugin-api.test.ts index ff9a016e391a..14d0849a4eec 100644 --- a/packages/tailwindcss/src/compat/plugin-api.test.ts +++ b/packages/tailwindcss/src/compat/plugin-api.test.ts @@ -293,9 +293,6 @@ describe('theme', async () => { .variable { color: color-mix(in oklab, #ef4444 var(--opacity), transparent); } - :root, :host { - --color-red-500: #ef4444; - } " `) }) @@ -379,9 +376,6 @@ describe('theme', async () => { .js-variable { color: color-mix(in oklab, rgb(255 0 0 / 1) var(--opacity), transparent); } - :root, :host { - --color-custom-css: rgba(255 0 0 / ); - } " `) }) @@ -1422,12 +1416,6 @@ describe('theme', async () => { .my-width-2\\.5 { width: 0.625rem; } - :root, :host { - --width-1: 0.25rem; - --width-1\\/2: 60%; - --width-1\\.5: 0.375rem; - --width-2_5: 0.625rem; - } " `) }) @@ -1479,12 +1467,6 @@ describe('theme', async () => { .my-width-2\\.5 { width: 0.625rem; } - :root, :host { - --width-1: 0.25rem; - --width-1\\/2: 60%; - --width-1\\.5: 0.375rem; - --width-2_5: 0.625rem; - } " `) }) @@ -3200,10 +3182,7 @@ describe('addUtilities()', () => { ) expect(compiled.build(['foo', 'md:foo', 'not-hover:md:foo']).trim()).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 768px; - } - .foo { + ".foo { :where(.foo > :first-child) { color: red; } @@ -4080,10 +4059,7 @@ describe('matchUtilities()', () => { expect(compiled.build(['foo-red', 'md:foo-red', 'not-hover:md:foo-red']).trim()) .toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 768px; - } - .foo-red { + ".foo-red { :where(.foo-red > :first-child) { color: red; } diff --git a/packages/tailwindcss/src/compat/screens-config.test.ts b/packages/tailwindcss/src/compat/screens-config.test.ts index e8f96ee55fd3..ac73d8216976 100644 --- a/packages/tailwindcss/src/compat/screens-config.test.ts +++ b/packages/tailwindcss/src/compat/screens-config.test.ts @@ -46,13 +46,7 @@ test('CSS `--breakpoint-*` merge with JS config `screens`', async () => { 'print:items-end', ]), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 50rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .max-w-screen-sm { + ".max-w-screen-sm { max-width: 44rem; } .sm\\:flex { @@ -140,10 +134,7 @@ test('JS config `screens` extend CSS `--breakpoint-*`', async () => { 'print:items-end', ]), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 50rem; - } - .min-xs\\:flex { + ".min-xs\\:flex { @media (width >= 30rem) { display: flex; } @@ -316,14 +307,7 @@ test('JS config `screens` overwrite CSS `--breakpoint-*`', async () => { 'print:items-end', ]), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - } - .mini\\:flex { + ".mini\\:flex { @media (width >= 40rem) { display: flex; } @@ -584,10 +568,7 @@ describe('complex screen configs', () => { 'print:items-end', ]), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 48rem; - } - .min-sm\\:flex { + ".min-sm\\:flex { @media (width >= 40rem) { display: flex; } diff --git a/packages/tailwindcss/src/css-functions.test.ts b/packages/tailwindcss/src/css-functions.test.ts index d90311b99547..371531c41236 100644 --- a/packages/tailwindcss/src/css-functions.test.ts +++ b/packages/tailwindcss/src/css-functions.test.ts @@ -94,11 +94,7 @@ describe('--spacing(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --spacing: .25rem; - } - - .foo { + ".foo { margin: 1rem; }" `) @@ -157,11 +153,7 @@ describe('--theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -197,11 +189,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -218,11 +206,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -239,11 +223,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -260,11 +240,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -281,11 +257,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -302,11 +274,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -323,11 +291,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: oklab(62.7955% .22486 .12584 / .75); }" `) @@ -344,11 +308,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: oklab(62.7955% .22486 .12584 / .75); }" `) @@ -365,11 +325,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: oklab(62.7955% .22486 .12584 / .75); }" `) @@ -386,11 +342,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: color-mix(in oklab, red var(--opacity), transparent); }" `) @@ -408,11 +360,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: color-mix(in oklab, red var(--opacity, 50%), transparent); }" `) @@ -429,11 +377,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --spacing-12: 3rem; - } - - .space-on-the-left { + ".space-on-the-left { margin-left: 3rem; }" `) @@ -450,11 +394,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --spacing-2_5: .625rem; - } - - .space-on-the-left { + ".space-on-the-left { margin-left: .625rem; }" `) @@ -471,11 +411,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --spacing-2_5: .625rem; - } - - .space-on-the-left { + ".space-on-the-left { margin-left: calc(100vh - .625rem); }" `) @@ -492,11 +428,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --radius-lg: .5rem; - } - - .radius { + ".radius { border-radius: .5rem; }" `) @@ -514,11 +446,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --blur: 8px; - } - - .default-blur { + ".default-blur { filter: blur(8px); }" `) @@ -537,12 +465,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --text-xs: 1337.75rem; - --text-xs--line-height: 1337rem; - } - - .text { + ".text { font-size: 1337.75rem; line-height: 1337rem; }" @@ -627,11 +550,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: oklab(62.7955% .22486 .12584 / .25); }" `) @@ -685,12 +604,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - --color-foo: red; - } - - .red { + ".red { color: red; }" `) @@ -708,12 +622,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - --color-foo: oklab(62.7955% .22486 .12584 / .5); - } - - .red { + ".red { color: oklab(62.7955% .22486 .12584 / .25); }" `) @@ -732,11 +641,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -753,11 +658,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: oklab(62.7955% .22486 .12584 / .5); }" `) @@ -774,11 +675,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -797,11 +694,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --blur: 8px; - } - - .blur { + ".blur { filter: blur(8px); }" `) @@ -860,11 +753,6 @@ describe('theme(…)', () => { .sm\\:\\[--color\\:theme\\(colors\\.red\\[500\\]\\)\\] { --color: red; } - } - - :root, :host { - --breakpoint-sm: 40rem; - --color-red-500: red; }" `) }) @@ -922,12 +810,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - } - - @media (width >= 48rem) and (width <= 64rem) { + "@media (width >= 48rem) and (width <= 64rem) { .red { color: red; } @@ -949,12 +832,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - } - - @media (width >= 48rem) and (width < 64rem) { + "@media (width >= 48rem) and (width < 64rem) { .red { color: red; } @@ -977,11 +855,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 48rem; - } - - @media (width >= 48rem) { + "@media (width >= 48rem) { .red { color: red; } @@ -1002,11 +876,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-md: 48rem; - } - - @container (width > 48rem) { + "@container (width > 48rem) { .red { color: red; } @@ -1027,11 +897,7 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ":root, :host { - --font-size-xs: .75rem; - } - - @supports (text-stroke: 0.75rem) { + "@supports (text-stroke: 0.75rem) { .red { color: red; } @@ -1188,11 +1054,7 @@ test('replaces CSS theme() function with values inside imported stylesheets', as }, ), ).toMatchInlineSnapshot(` - ":root, :host { - --color-red-500: red; - } - - .red { + ".red { color: red; }" `) @@ -1213,11 +1075,7 @@ test('resolves paths ending with a 1', async () => { [], ), ).toMatchInlineSnapshot(` - ":root, :host { - --spacing-1: .25rem; - } - - .foo { + ".foo { margin: .25rem; }" `) diff --git a/packages/tailwindcss/src/design-system.ts b/packages/tailwindcss/src/design-system.ts index cfa02beaf366..5d3abc9ffa9e 100644 --- a/packages/tailwindcss/src/design-system.ts +++ b/packages/tailwindcss/src/design-system.ts @@ -7,7 +7,7 @@ import { getClassOrder } from './sort' import type { Theme, ThemeKey } from './theme' import { Utilities, createUtilities, withAlpha } from './utilities' import { DefaultMap } from './utils/default-map' -import * as ValueParser from './value-parser' +import { extractUsedVariables } from './utils/variables' import { Variants, createVariants } from './variants' export type DesignSystem = { @@ -69,19 +69,9 @@ export function buildDesignSystem(theme: Theme): DesignSystem { }) let trackUsedVariables = new DefaultMap((raw) => { - ValueParser.walk(ValueParser.parse(raw), (node) => { - if (node.kind !== 'function' || node.value !== 'var') return - - ValueParser.walk(node.nodes, (child) => { - if (child.kind !== 'word' || child.value[0] !== '-' || child.value[1] !== '-') return - - theme.markUsedVariable(child.value) - }) - - return ValueParser.ValueWalkAction.Skip - }) - - return true + for (let variable of extractUsedVariables(raw)) { + theme.markUsedVariable(variable) + } }) let designSystem: DesignSystem = { @@ -104,7 +94,7 @@ export function buildDesignSystem(theme: Theme): DesignSystem { }, }) - astNodes = optimizeAst(astNodes, designSystem) + astNodes = optimizeAst(astNodes, designSystem, null) if (astNodes.length === 0 || wasInvalid) { result.push(null) diff --git a/packages/tailwindcss/src/feature-flags.ts b/packages/tailwindcss/src/feature-flags.ts index e6db9b7a6261..2cdf403e3abb 100644 --- a/packages/tailwindcss/src/feature-flags.ts +++ b/packages/tailwindcss/src/feature-flags.ts @@ -1,3 +1 @@ -export const enableRemoveUnusedThemeVariables = false - export const enableUserValid = process.env.FEATURES_ENV !== 'stable' diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index aa68796073f0..67b6c3c53770 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -27,7 +27,6 @@ describe('compiling CSS', () => { ).toMatchInlineSnapshot(` ":root, :host { --color-black: #000; - --breakpoint-md: 768px; } @layer utilities { @@ -173,7 +172,6 @@ describe('compiling CSS', () => { --spacing-1\\.5: 1.5px; --spacing-2_5: 2.5px; --spacing-3\\.5: 3.5px; - --spacing-3_5: 3.5px; --spacing-foo\\/bar: 3rem; } @@ -294,7 +292,6 @@ describe('@apply', () => { --color-blue-500: #3b82f6; --color-green-200: #bbf7d0; --color-green-500: #22c55e; - --breakpoint-md: 768px; --animate-spin: spin 1s linear infinite; } @@ -1149,7 +1146,6 @@ describe('Parsing theme values from CSS', () => { ).toMatchInlineSnapshot(` ":root, :host { --color-red: red; - --animate-foo: foo 1s infinite; --text-lg: 20px; } @@ -1159,12 +1155,6 @@ describe('Parsing theme values from CSS', () => { .accent-red { accent-color: var(--color-red); - } - - @keyframes foo { - to { - opacity: 1; - } }" `) }) @@ -1251,12 +1241,6 @@ describe('Parsing theme values from CSS', () => { .accent-green { accent-color: var(--color-green); - } - - @keyframes spin { - to { - transform: rotate(360deg); - } }" `) }) @@ -1304,12 +1288,6 @@ describe('Parsing theme values from CSS', () => { .accent-green { accent-color: var(--color-green); - } - - @keyframes spin { - to { - transform: rotate(360deg); - } }" `) }) @@ -1403,7 +1381,6 @@ describe('Parsing theme values from CSS', () => { ), ).toMatchInlineSnapshot(` ":root, :host { - --inset-shadow-sm: inset 0 2px 4px #0000000d; --inset-md: 50px; } @@ -1586,12 +1563,6 @@ describe('Parsing theme values from CSS', () => { animation: var(--animate-foobar); } - @keyframes foo { - to { - opacity: 1; - } - } - @keyframes foobar { to { opacity: 0; @@ -1628,7 +1599,6 @@ describe('Parsing theme values from CSS', () => { ).toMatchInlineSnapshot(` ":root, :host { --animate-foo: used 1s infinite; - --animate-bar: unused 1s infinite; } .animate-foo { @@ -1639,12 +1609,6 @@ describe('Parsing theme values from CSS', () => { to { opacity: 1; } - } - - @keyframes unused { - to { - opacity: 0; - } }" `) }) @@ -1675,12 +1639,7 @@ describe('Parsing theme values from CSS', () => { ['animate-foo'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --animate-foo: used 1s infinite; - --animate-bar: unused 1s infinite; - } - - .animate-foo { + ".animate-foo { animation: 1s infinite used; } @@ -1688,12 +1647,6 @@ describe('Parsing theme values from CSS', () => { to { opacity: 1; } - } - - @keyframes unused { - to { - opacity: 0; - } }" `) }) @@ -1782,12 +1735,6 @@ describe('Parsing theme values from CSS', () => { to { opacity: 1; } - } - - @keyframes unused { - to { - opacity: 0; - } }" `) }) @@ -2092,13 +2039,7 @@ describe('Parsing theme values from CSS', () => { ['bg-tomato', 'bg-potato', 'bg-primary'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --color-tomato: #e10c04; - --color-potato: #ac855b; - --color-primary: var(--primary); - } - - .bg-potato { + ".bg-potato { background-color: #ac855b; } @@ -2137,13 +2078,7 @@ describe('Parsing theme values from CSS', () => { }, ), ).toMatchInlineSnapshot(` - ":root, :host { - --color-tomato: #e10c04; - --color-potato: #ac855b; - --color-primary: var(--primary); - } - - .bg-tomato { + ".bg-tomato { background-color: #e10c04; }" `) @@ -2176,6 +2111,30 @@ describe('Parsing theme values from CSS', () => { `) }) + test('when no theme values are emitted, empty layers can be removed', async () => { + expect( + await compileCss( + css` + @layer theme1 { + @layer theme2 { + @theme { + --color-tomato: #e10c04; + --color-potato: #ac855b; + } + } + } + + @tailwind utilities; + `, + ['underline'], + ), + ).toMatchInlineSnapshot(` + ".underline { + text-decoration-line: underline; + }" + `) + }) + test('wrapping `@theme` with `@media theme(inline)` behaves like `@theme inline` to support `@import` statements', async () => { expect( await compileCss( @@ -2193,13 +2152,7 @@ describe('Parsing theme values from CSS', () => { ['bg-tomato', 'bg-potato', 'bg-primary'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --color-tomato: #e10c04; - --color-potato: #ac855b; - --color-primary: var(--primary); - } - - .bg-potato { + ".bg-potato { background-color: #ac855b; } @@ -2312,11 +2265,7 @@ describe('Parsing theme values from CSS', () => { ['bg-potato'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --color-potato: #efb46b; - } - - .bg-potato { + ".bg-potato { background-color: #efb46b; }" `) @@ -2487,6 +2436,54 @@ describe('Parsing theme values from CSS', () => { }" `) }) + + test('only emits theme variables that are used outside of being defined by another variable', async () => { + let { build } = await compile( + css` + @theme { + --var-a: var(--var-b); + --var-b: var(--var-c); + --var-c: var(--var-d); + --var-d: red; + + --var-four: green; + --var-three: var(--var-four); + --var-two: var(--var-three); + --var-one: var(--var-two); + + --var-eins: var(--var-zwei); + --var-zwei: var(--var-drei); + --var-drei: var(--var-vier); + --var-vier: blue; + } + + @utility get-var-* { + color: --value(--var-\*); + } + @tailwind utilities; + `, + {}, + ) + + expect(optimizeCss(build(['get-var-b', 'get-var-two'])).trim()).toMatchInlineSnapshot(` + ":root, :host { + --var-b: var(--var-c); + --var-c: var(--var-d); + --var-d: red; + --var-four: green; + --var-three: var(--var-four); + --var-two: var(--var-three); + } + + .get-var-b { + color: var(--var-b); + } + + .get-var-two { + color: var(--var-two); + }" + `) + }) }) describe('plugins', () => { @@ -3661,7 +3658,7 @@ it("should error when `layer(…)` is used, but it's not the first param", async ) }) -describe('`@import "…" reference`', () => { +describe('`@reference "…" reference`', () => { test('recursively removes styles', async () => { let loadStylesheet = async (id: string, base: string) => { if (id === './foo/baz.css') { @@ -3692,7 +3689,7 @@ describe('`@import "…" reference`', () => { await expect( compileCss( ` - @import './foo/bar.css' reference; + @reference './foo/bar.css'; .bar { @apply md:hocus:foo; @@ -3732,7 +3729,7 @@ describe('`@import "…" reference`', () => { let { build } = await compile( css` - @import './foo/bar.css' reference; + @reference './foo/bar.css'; `, { loadStylesheet }, ) @@ -3740,40 +3737,6 @@ describe('`@import "…" reference`', () => { expect(build(['text-underline', 'border']).trim()).toMatchInlineSnapshot(`""`) }) - test('removes styles when the import resolver was handled outside of Tailwind CSS', async () => { - await expect( - compileCss( - ` - @media reference { - @layer theme { - @theme { - --breakpoint-md: 48rem; - } - .foo { - color: red; - } - } - @utility foo { - color: red; - } - @custom-variant hocus (&:hover, &:focus); - } - - .bar { - @apply md:hocus:foo; - } - `, - [], - ), - ).resolves.toMatchInlineSnapshot(` - "@media (width >= 48rem) { - .bar:hover, .bar:focus { - color: red; - } - }" - `) - }) - test('removes all @keyframes, even those contributed by JavasScript plugins', async () => { await expect( compileCss( @@ -3848,8 +3811,177 @@ describe('`@import "…" reference`', () => { }, ), ).resolves.toMatchInlineSnapshot(` - ".bar { + "@layer theme { + :root, :host { + --animate-spin: spin 1s linear infinite; + } + } + + .bar { animation: var(--animate-spin); + } + + @keyframes spin { + to { + transform: rotate(360deg); + } + }" + `) + }) + + test('emits theme variables and keyframes defined inside @reference-ed files', async () => { + let loadStylesheet = async (id: string, base: string) => { + switch (id) { + case './one.css': { + return { + content: css` + @import './two.css' layer(two); + `, + base: '/root', + } + } + case './two.css': { + return { + content: css` + @import './three.css' layer(three); + `, + base: '/root', + } + } + case './three.css': { + return { + content: css` + .foo { + color: red; + } + @theme { + --color-red: red; + --animate-wiggle: wiggle 1s ease-in-out infinite; + @keyframes wiggle { + 0%, + 100% { + transform: rotate(-3deg); + } + 50% { + transform: rotate(3deg); + } + } + } + `, + base: '/root', + } + } + } + } + + await expect( + compileCss( + ` + @reference './one.css'; + .bar { + @apply text-red animate-wiggle; + } + `, + [], + { loadStylesheet }, + ), + ).resolves.toMatchInlineSnapshot(` + "@layer two { + @layer three { + :root, :host { + --color-red: red; + --animate-wiggle: wiggle 1s ease-in-out infinite; + } + } + } + + .bar { + animation: var(--animate-wiggle); + color: var(--color-red); + } + + @keyframes wiggle { + 0%, 100% { + transform: rotate(-3deg); + } + + 50% { + transform: rotate(3deg); + } + }" + `) + }) + + test('supports `@import "…" reference` syntax', async () => { + let loadStylesheet = async () => { + return { + content: css` + .foo { + color: red; + } + @utility foo { + color: red; + } + @theme { + --breakpoint-md: 768px; + } + @custom-variant hocus (&:hover, &:focus); + `, + base: '/root/foo', + } + } + + await expect( + compileCss( + ` + @import './foo/bar.css' reference; + + .bar { + @apply md:hocus:foo; + } + `, + [], + { loadStylesheet }, + ), + ).resolves.toMatchInlineSnapshot(` + "@media (width >= 768px) { + .bar:hover, .bar:focus { + color: red; + } + }" + `) + }) + + test('removes styles when the import resolver was handled outside of Tailwind CSS', async () => { + await expect( + compileCss( + ` + @media reference { + @layer theme { + @theme { + --breakpoint-md: 48rem; + } + .foo { + color: red; + } + } + @utility foo { + color: red; + } + @custom-variant hocus (&:hover, &:focus); + } + + .bar { + @apply md:hocus:foo; + } + `, + [], + ), + ).resolves.toMatchInlineSnapshot(` + "@media (width >= 48rem) { + .bar:hover, .bar:focus { + color: red; + } }" `) }) diff --git a/packages/tailwindcss/src/index.ts b/packages/tailwindcss/src/index.ts index b7e7886c28b1..1fc32c6076ee 100644 --- a/packages/tailwindcss/src/index.ts +++ b/packages/tailwindcss/src/index.ts @@ -448,10 +448,6 @@ async function parseCss( if (node.name === '@theme') { let [themeOptions, themePrefix] = parseThemeOptions(node.params) - if (context.reference) { - themeOptions |= ThemeOptions.REFERENCE - } - if (themePrefix) { if (!IS_VALID_PREFIX.test(themePrefix)) { throw new Error( @@ -494,7 +490,7 @@ async function parseCss( // Keep a reference to the first `@theme` rule to update with the full // theme later, and delete any other `@theme` rules. - if (!firstThemeRule && !(themeOptions & ThemeOptions.REFERENCE)) { + if (!firstThemeRule) { firstThemeRule = styleRule(':root, :host', []) replaceWith([firstThemeRule]) } else { @@ -607,6 +603,7 @@ async function parseCss( root, utilitiesNode, features, + firstThemeRule, } } @@ -619,7 +616,10 @@ export async function compileAst( features: Features build(candidates: string[]): AstNode[] }> { - let { designSystem, ast, globs, root, utilitiesNode, features } = await parseCss(input, opts) + let { designSystem, ast, globs, root, utilitiesNode, features, firstThemeRule } = await parseCss( + input, + opts, + ) if (process.env.NODE_ENV !== 'test') { ast.unshift(comment(`! tailwindcss v${version} | MIT License | https://tailwindcss.com `)) @@ -647,7 +647,7 @@ export async function compileAst( } if (!utilitiesNode) { - compiled ??= optimizeAst(ast, designSystem) + compiled ??= optimizeAst(ast, designSystem, firstThemeRule) return compiled } @@ -669,7 +669,7 @@ export async function compileAst( // If no new candidates were added, we can return the original CSS. This // currently assumes that we only add new candidates and never remove any. if (!didChange) { - compiled ??= optimizeAst(ast, designSystem) + compiled ??= optimizeAst(ast, designSystem, firstThemeRule) return compiled } @@ -681,7 +681,7 @@ export async function compileAst( // CSS. This currently assumes that we only add new ast nodes and never // remove any. if (previousAstNodeCount === newNodes.length) { - compiled ??= optimizeAst(ast, designSystem) + compiled ??= optimizeAst(ast, designSystem, firstThemeRule) return compiled } @@ -689,7 +689,7 @@ export async function compileAst( utilitiesNode.nodes = newNodes - compiled = optimizeAst(ast, designSystem) + compiled = optimizeAst(ast, designSystem, firstThemeRule) return compiled }, } diff --git a/packages/tailwindcss/src/prefix.test.ts b/packages/tailwindcss/src/prefix.test.ts index 8fb0eec6a48f..020575ce2f87 100644 --- a/packages/tailwindcss/src/prefix.test.ts +++ b/packages/tailwindcss/src/prefix.test.ts @@ -108,8 +108,6 @@ test('CSS variables output by the theme are prefixed', async () => { expect(compiler.build(['tw:text-red'])).toMatchInlineSnapshot(` ":root, :host { --tw-color-red: #f00; - --tw-color-green: #0f0; - --tw-breakpoint-sm: 640px; } .tw\\:text-red { color: var(--tw-color-red); @@ -131,12 +129,7 @@ test('CSS theme functions do not use the prefix', async () => { expect(compiler.build(['tw:[color:theme(--color-red)]', 'tw:text-[theme(--color-red)]'])) .toMatchInlineSnapshot(` - ":root, :host { - --tw-color-red: #f00; - --tw-color-green: #0f0; - --tw-breakpoint-sm: 640px; - } - .tw\\:\\[color\\:theme\\(--color-red\\)\\] { + ".tw\\:\\[color\\:theme\\(--color-red\\)\\] { color: #f00; } .tw\\:text-\\[theme\\(--color-red\\)\\] { @@ -193,12 +186,7 @@ test('JS theme functions do not use the prefix', async () => { ) expect(compiler.build(['tw:my-custom'])).toMatchInlineSnapshot(` - ":root, :host { - --tw-color-red: #f00; - --tw-color-green: #0f0; - --tw-breakpoint-sm: 640px; - } - .tw\\:my-custom { + ".tw\\:my-custom { color: #f00; } " @@ -337,12 +325,7 @@ test('a prefix can be configured via @import prefix(…)', async () => { }, }) - expect(compiler.build(['underline', 'hover:line-through', 'custom'])).toMatchInlineSnapshot(` - ":root, :host { - --tw-color-potato: #7a4724; - } - " - `) + expect(compiler.build(['underline', 'hover:line-through', 'custom'])).toMatchInlineSnapshot(`""`) }) test('a prefix must be letters only', async () => { diff --git a/packages/tailwindcss/src/utilities.test.ts b/packages/tailwindcss/src/utilities.test.ts index 90e12bf832ad..79ac4700655e 100644 --- a/packages/tailwindcss/src/utilities.test.ts +++ b/packages/tailwindcss/src/utilities.test.ts @@ -147,7 +147,6 @@ test('inset', async () => { ).toMatchInlineSnapshot(` ":root, :host { --spacing-4: 1rem; - --inset-shadow-sm: inset 0 1px 1px #0000000d; --inset-shadowned: 1940px; } @@ -3162,10 +3161,6 @@ describe('container', () => { ).toMatchInlineSnapshot(` ":root, :host { --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; } .container { @@ -3305,10 +3300,6 @@ describe('container', () => { ).toMatchInlineSnapshot(` ":root, :host { --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; } .container { @@ -4045,11 +4036,7 @@ test('translate-x', async () => { ['translate-x-full', '-translate-x-full', 'translate-x-px', '-translate-x-[var(--value)]'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --spacing: .25rem; - } - - .-translate-x-\\[var\\(--value\\)\\] { + ".-translate-x-\\[var\\(--value\\)\\] { --tw-translate-x: calc(var(--value) * -1); translate: var(--tw-translate-x) var(--tw-translate-y); } @@ -4173,11 +4160,7 @@ test('translate-y', async () => { ['translate-y-full', '-translate-y-full', 'translate-y-px', '-translate-y-[var(--value)]'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --spacing: .25rem; - } - - .-translate-y-\\[var\\(--value\\)\\] { + ".-translate-y-\\[var\\(--value\\)\\] { --tw-translate-y: calc(var(--value) * -1); translate: var(--tw-translate-x) var(--tw-translate-y); } @@ -7734,11 +7717,7 @@ test('divide-x with custom default border width', async () => { ['divide-x'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --default-border-width: 2px; - } - - :where(.divide-x > :not(:last-child)) { + ":where(.divide-x > :not(:last-child)) { --tw-divide-x-reverse: 0; border-inline-style: var(--tw-border-style); border-inline-start-width: calc(2px * var(--tw-divide-x-reverse)); @@ -7840,11 +7819,7 @@ test('divide-y with custom default border width', async () => { ['divide-y'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --default-border-width: 2px; - } - - :where(.divide-y > :not(:last-child)) { + ":where(.divide-y > :not(:last-child)) { --tw-divide-y-reverse: 0; border-bottom-style: var(--tw-border-style); border-top-style: var(--tw-border-style); @@ -9930,11 +9905,7 @@ test('border with custom default border width', async () => { ['border'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --default-border-width: 2px; - } - - .border { + ".border { border-style: var(--tw-border-style); border-width: 2px; } @@ -13878,7 +13849,6 @@ test('transition', async () => { ":root, :host { --default-transition-timing-function: ease; --default-transition-duration: .1s; - --transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; --transition-property-opacity: opacity; } @@ -13944,12 +13914,7 @@ test('transition', async () => { ['transition', 'transition-all', 'transition-colors'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --default-transition-timing-function: ease; - --default-transition-duration: .1s; - } - - .transition { + ".transition { transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter; transition-timing-function: var(--tw-ease, ease); transition-duration: var(--tw-duration, .1s); @@ -14752,11 +14717,7 @@ test('outline', async () => { ['outline'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --default-outline-width: 2px; - } - - .outline { + ".outline { outline-style: var(--tw-outline-style); outline-width: 2px; } @@ -15261,8 +15222,6 @@ test('shadow', async () => { ).toMatchInlineSnapshot(` ":root, :host { --color-red-500: #ef4444; - --shadow-sm: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --shadow-xl: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a; } .shadow-\\[10px_10px\\] { @@ -15485,8 +15444,6 @@ test('inset-shadow', async () => { ).toMatchInlineSnapshot(` ":root, :host { --color-red-500: #ef4444; - --inset-shadow: inset 0 2px 4px #0000000d; - --inset-shadow-sm: inset 0 1px 1px #0000000d; } .inset-shadow { @@ -15891,11 +15848,7 @@ test('ring', async () => { ['ring'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --default-ring-width: 2px; - } - - .ring { + ".ring { --tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); } @@ -16564,7 +16517,6 @@ describe('spacing utilities', () => { expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(` ":root, :host { --spacing-sm: 8px; - --container-sm: 256px; } .w-sm { @@ -16638,13 +16590,7 @@ describe('custom utilities', () => { let compiled = build([]) // `foo` is not used yet: - expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(` - "@layer utilities; - - :root, :host { - --example-foo: 123px; - }" - `) + expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(`"@layer utilities;"`) // `foo` is used, and the CSS variable is emitted: compiled = build(['foo']) @@ -17828,12 +17774,7 @@ describe('custom utilities', () => { ` expect(await compileCss(input, ['flex'])).toMatchInlineSnapshot(` - ":root, :host { - --example-foo: red; - --color-red-500: red; - } - - .flex { + ".flex { display: flex; }" `) @@ -17926,11 +17867,7 @@ describe('custom utilities', () => { ` expect(await compileCss(input, ['tab-github'])).toMatchInlineSnapshot(` - ":root, :host { - --tab-size-github: 8; - } - - .tab-github { + ".tab-github { tab-size: 8; }" `) diff --git a/packages/tailwindcss/src/utils/variables.ts b/packages/tailwindcss/src/utils/variables.ts new file mode 100644 index 000000000000..d19743413e55 --- /dev/null +++ b/packages/tailwindcss/src/utils/variables.ts @@ -0,0 +1,17 @@ +import * as ValueParser from '../value-parser' + +export function extractUsedVariables(raw: string): string[] { + let variables: string[] = [] + ValueParser.walk(ValueParser.parse(raw), (node) => { + if (node.kind !== 'function' || node.value !== 'var') return + + ValueParser.walk(node.nodes, (child) => { + if (child.kind !== 'word' || child.value[0] !== '-' || child.value[1] !== '-') return + + variables.push(child.value) + }) + + return ValueParser.ValueWalkAction.Skip + }) + return variables +} diff --git a/packages/tailwindcss/src/variants.test.ts b/packages/tailwindcss/src/variants.test.ts index 554be3c4d1b6..e215835aa101 100644 --- a/packages/tailwindcss/src/variants.test.ts +++ b/packages/tailwindcss/src/variants.test.ts @@ -775,15 +775,7 @@ test('default breakpoints', async () => { ['sm:flex', 'md:flex', 'lg:flex', 'xl:flex', '2xl:flex'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 640px; - --breakpoint-md: 768px; - --breakpoint-lg: 1024px; - --breakpoint-xl: 1280px; - --breakpoint-2xl: 1536px; - } - - @media (width >= 640px) { + "@media (width >= 640px) { .sm\\:flex { display: flex; } @@ -843,11 +835,7 @@ test('custom breakpoint', async () => { ['10xl:flex'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-10xl: 5000px; - } - - @media (width >= 5000px) { + "@media (width >= 5000px) { .\\31 0xl\\:flex { display: flex; } @@ -870,13 +858,7 @@ test('max-*', async () => { ['max-lg:flex', 'max-sm:flex', 'max-md:flex'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 640px; - --breakpoint-lg: 1024px; - --breakpoint-md: 768px; - } - - @media (width < 1024px) { + "@media (width < 1024px) { .max-lg\\:flex { display: flex; } @@ -925,13 +907,7 @@ test('min-*', async () => { ['min-lg:flex', 'min-sm:flex', 'min-md:flex'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 640px; - --breakpoint-lg: 1024px; - --breakpoint-md: 768px; - } - - @media (width >= 640px) { + "@media (width >= 640px) { .min-sm\\:flex { display: flex; } @@ -982,15 +958,7 @@ test('sorting stacked min-* and max-* variants', async () => { ['min-sm:max-lg:flex', 'min-sm:max-xl:flex', 'min-md:max-lg:flex', 'min-xs:max-sm:flex'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 640px; - --breakpoint-lg: 1024px; - --breakpoint-md: 768px; - --breakpoint-xl: 1280px; - --breakpoint-xs: 280px; - } - - @media (width >= 280px) { + "@media (width >= 280px) { @media (width < 640px) { .min-xs\\:max-sm\\:flex { display: flex; @@ -1037,13 +1005,7 @@ test('stacked min-* and max-* variants should come after unprefixed variants', a ['sm:flex', 'min-sm:max-lg:flex', 'md:flex', 'min-md:max-lg:flex'], ), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 640px; - --breakpoint-lg: 1024px; - --breakpoint-md: 768px; - } - - @media (width >= 640px) { + "@media (width >= 640px) { .sm\\:flex { display: flex; } @@ -1099,13 +1061,7 @@ test('min, max and unprefixed breakpoints', async () => { ], ), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 640px; - --breakpoint-lg: 1024px; - --breakpoint-md: 768px; - } - - @media (width < 1024px) { + "@media (width < 1024px) { .max-lg\\:flex { display: flex; } @@ -1484,11 +1440,7 @@ test('not', async () => { ], ), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 640px; - } - - .not-first\\:flex:not(:first-child), .not-last\\:flex:not(:last-child), .not-only\\:flex:not(:only-child), .not-odd\\:flex:not(:nth-child(odd)), .not-even\\:flex:not(:nth-child(2n)), .not-first-of-type\\:flex:not(:first-of-type), .not-last-of-type\\:flex:not(:last-of-type), .not-only-of-type\\:flex:not(:only-of-type), .not-visited\\:flex:not(:visited), .not-target\\:flex:not(:target) { + ".not-first\\:flex:not(:first-child), .not-last\\:flex:not(:last-child), .not-only\\:flex:not(:only-child), .not-odd\\:flex:not(:nth-child(odd)), .not-even\\:flex:not(:nth-child(2n)), .not-first-of-type\\:flex:not(:first-of-type), .not-last-of-type\\:flex:not(:last-of-type), .not-only-of-type\\:flex:not(:only-of-type), .not-visited\\:flex:not(:visited), .not-target\\:flex:not(:target) { display: flex; } @@ -2030,11 +1982,7 @@ test('container queries', async () => { ], ), ).toMatchInlineSnapshot(` - ":root, :host { - --container-lg: 1024px; - } - - @container name (width < 1024px) { + "@container name (width < 1024px) { .\\@max-lg\\/name\\:flex { display: flex; } @@ -2186,15 +2134,7 @@ test('variant order', async () => { ], ), ).toMatchInlineSnapshot(` - ":root, :host { - --breakpoint-sm: 640px; - --breakpoint-md: 768px; - --breakpoint-lg: 1024px; - --breakpoint-xl: 1280px; - --breakpoint-2xl: 1536px; - } - - @media (hover: hover) { + "@media (hover: hover) { .group-hover\\:flex:is(:where(.group):hover *), .peer-hover\\:flex:is(:where(.peer):hover ~ *) { display: flex; }