mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
28 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d684733d80
|
Only expose used CSS variables (#16211)
This PR only exposes used CSS variables.
My initial approach was to track the used variables, this was a bit
messy because it meant that we had to walk part of the AST(s) in
multiple places. We also had to be careful because sometimes if a
variable exists in an AST, that doesn't mean that it's actually used.
E.g.:
```css
h1 {
color: var(--color-red-500); /* Definitely used, so let's keep it */
}
@utility foo {
color: var(--color-blue-500); /* Hmm, used? */
}
```
In this last case, the `--color-blue-500` is part of the CSS AST, but as
long as `foo` the utility is not used, it won't end up in your actual
CSS file, therefore the variable is **not** used.
Alternatively, if the `foo` utility is used with an invalid variant
(e.g.: `group-[>.foo]:foo`, then the `@utility foo` code will still run
internally because variants are applied on top of the utility. This
means that it looks like `var(--color-blue-500)` is being used.
Another annoying side effect was that because variables are
conditionally generated, that the `@theme` -> `:root, :host` conversion
had to happen for every build, instead of once in the `compile(…)` step.
---
To prevent all the messy rules and additional booking while walking of
ASTs I thought about a different approach. We are only interested in
variables that are actually used. The only way we know for sure, is
right before the `toCss(…)` step. Any step before that could still throw
away AST nodes.
However, we do have an `optimizeAst` step right before printing to
simplify and optimize the AST. So the idea was to keep all the CSS
variables in the AST, and only in the `optimizeAst` step we perform a
kind of mark-and-sweep algorithm where we can first check which
variables are _actually_ used (these are the ones that are left in the
AST), and later we removed the ones that weren't part of known used
list.
Moving the logic to this step feels a natural spot for this to happen,
because we are in fact optimizing the AST. We were already walking the
AST, so we can just handle these cases while we are walking without
additional walks. Last but not least, this also means that there is only
a single spot where need to track and remove variables.
Now, there is a different part to this story. If you use a variable in
JS land for example, we also want to make sure that we keep the CSS
variable in the CSS. To do this, we can mark variables as being used in
the internal `Theme`.
The Oxide scanner will also emit used variables that it can find such as
`var(--color-red-500)` and will emit `--color-red-500` as a "candidate".
We can then proactively mark this one as used even though it may not be
used anyway in the actual AST.
---
### Always including all variables
Some users might make heavy use of JavaScript and string interpolation
where they _need_ all the variables to be present. Similar to the
`inline` and `reference` theme options, this also exposes a new `static`
option. This ensures that all the CSS variables will always be generated
regardless of whether it's used or not.
One handy feature is that you have granular control over this:
```css
/* These will always be generated */
@theme static {
--color-primary: red;
--color-secondary: blue;
}
/* Only generated when used */
@theme {
--color-maybe: pink;
}
```
### Performance considerations:
Now that we are tracking which variables are being used, it means that
we will produce a smaller CSS file, but we are also doing more work (the
mark-and-sweep part). That said, ran some benchmarks and the changes
look like this:
Running it on Catalyst:
<img width="1086" alt="image"
src="https://github.com/user-attachments/assets/ec2124f0-2e64-4a11-aa5e-5f7ae6605962"
/>
_(probably within margin of error)_
Running it on Tailwind UI:
<img width="1113" alt="image"
src="https://github.com/user-attachments/assets/6bea2328-d790-4f33-a0ae-72654c688edb"
/>
### Test plan
- Tests have been updated with the removed CSS variables
- Added a dedicated integration test to show that Oxide can find
variables and mark them as used (so they are included)
- Ran the code on Catalyst, and verified that all the removed variables
are in fact not used anywhere in the codebase.
The diff on Catalyst looks like this:
<details>
```diff
diff --git a/templates/catalyst/out.css b/templates/catalyst/out.css
index f2b364ea..240d1d90 100644
--- a/templates/catalyst/out.css
+++ b/templates/catalyst/out.css
@@ -29,218 +29,111 @@
@layer theme {
:root, :host {
--font-sans: Inter, sans-serif;
- --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(0.971 0.013 17.38);
- --color-red-100: oklch(0.936 0.032 17.717);
--color-red-200: oklch(0.885 0.062 18.334);
--color-red-300: oklch(0.808 0.114 19.571);
--color-red-400: oklch(0.704 0.191 22.216);
--color-red-500: oklch(0.637 0.237 25.331);
--color-red-600: oklch(0.577 0.245 27.325);
--color-red-700: oklch(0.505 0.213 27.518);
- --color-red-800: oklch(0.444 0.177 26.899);
--color-red-900: oklch(0.396 0.141 25.723);
- --color-red-950: oklch(0.258 0.092 26.042);
- --color-orange-50: oklch(0.98 0.016 73.684);
- --color-orange-100: oklch(0.954 0.038 75.164);
--color-orange-200: oklch(0.901 0.076 70.697);
--color-orange-300: oklch(0.837 0.128 66.29);
--color-orange-400: oklch(0.75 0.183 55.934);
--color-orange-500: oklch(0.705 0.213 47.604);
--color-orange-600: oklch(0.646 0.222 41.116);
--color-orange-700: oklch(0.553 0.195 38.402);
- --color-orange-800: oklch(0.47 0.157 37.304);
--color-orange-900: oklch(0.408 0.123 38.172);
- --color-orange-950: oklch(0.266 0.079 36.259);
- --color-amber-50: oklch(0.987 0.022 95.277);
- --color-amber-100: oklch(0.962 0.059 95.617);
- --color-amber-200: oklch(0.924 0.12 95.746);
- --color-amber-300: oklch(0.879 0.169 91.605);
--color-amber-400: oklch(0.828 0.189 84.429);
--color-amber-500: oklch(0.769 0.188 70.08);
--color-amber-600: oklch(0.666 0.179 58.318);
--color-amber-700: oklch(0.555 0.163 48.998);
- --color-amber-800: oklch(0.473 0.137 46.201);
- --color-amber-900: oklch(0.414 0.112 45.904);
--color-amber-950: oklch(0.279 0.077 45.635);
- --color-yellow-50: oklch(0.987 0.026 102.212);
- --color-yellow-100: oklch(0.973 0.071 103.193);
- --color-yellow-200: oklch(0.945 0.129 101.54);
--color-yellow-300: oklch(0.905 0.182 98.111);
--color-yellow-400: oklch(0.852 0.199 91.936);
- --color-yellow-500: oklch(0.795 0.184 86.047);
--color-yellow-600: oklch(0.681 0.162 75.834);
--color-yellow-700: oklch(0.554 0.135 66.442);
- --color-yellow-800: oklch(0.476 0.114 61.907);
- --color-yellow-900: oklch(0.421 0.095 57.708);
--color-yellow-950: oklch(0.286 0.066 53.813);
- --color-lime-50: oklch(0.986 0.031 120.757);
- --color-lime-100: oklch(0.967 0.067 122.328);
- --color-lime-200: oklch(0.938 0.127 124.321);
--color-lime-300: oklch(0.897 0.196 126.665);
--color-lime-400: oklch(0.841 0.238 128.85);
- --color-lime-500: oklch(0.768 0.233 130.85);
--color-lime-600: oklch(0.648 0.2 131.684);
--color-lime-700: oklch(0.532 0.157 131.589);
- --color-lime-800: oklch(0.453 0.124 130.933);
- --color-lime-900: oklch(0.405 0.101 131.063);
--color-lime-950: oklch(0.274 0.072 132.109);
- --color-green-50: oklch(0.982 0.018 155.826);
- --color-green-100: oklch(0.962 0.044 156.743);
- --color-green-200: oklch(0.925 0.084 155.995);
- --color-green-300: oklch(0.871 0.15 154.449);
--color-green-400: oklch(0.792 0.209 151.711);
--color-green-500: oklch(0.723 0.219 149.579);
--color-green-600: oklch(0.627 0.194 149.214);
--color-green-700: oklch(0.527 0.154 150.069);
- --color-green-800: oklch(0.448 0.119 151.328);
--color-green-900: oklch(0.393 0.095 152.535);
- --color-green-950: oklch(0.266 0.065 152.934);
- --color-emerald-50: oklch(0.979 0.021 166.113);
- --color-emerald-100: oklch(0.95 0.052 163.051);
- --color-emerald-200: oklch(0.905 0.093 164.15);
- --color-emerald-300: oklch(0.845 0.143 164.978);
--color-emerald-400: oklch(0.765 0.177 163.223);
--color-emerald-500: oklch(0.696 0.17 162.48);
--color-emerald-600: oklch(0.596 0.145 163.225);
--color-emerald-700: oklch(0.508 0.118 165.612);
- --color-emerald-800: oklch(0.432 0.095 166.913);
--color-emerald-900: oklch(0.378 0.077 168.94);
- --color-emerald-950: oklch(0.262 0.051 172.552);
- --color-teal-50: oklch(0.984 0.014 180.72);
- --color-teal-100: oklch(0.953 0.051 180.801);
- --color-teal-200: oklch(0.91 0.096 180.426);
--color-teal-300: oklch(0.855 0.138 181.071);
--color-teal-400: oklch(0.777 0.152 181.912);
--color-teal-500: oklch(0.704 0.14 182.503);
--color-teal-600: oklch(0.6 0.118 184.704);
--color-teal-700: oklch(0.511 0.096 186.391);
- --color-teal-800: oklch(0.437 0.078 188.216);
--color-teal-900: oklch(0.386 0.063 188.416);
- --color-teal-950: oklch(0.277 0.046 192.524);
- --color-cyan-50: oklch(0.984 0.019 200.873);
- --color-cyan-100: oklch(0.956 0.045 203.388);
- --color-cyan-200: oklch(0.917 0.08 205.041);
--color-cyan-300: oklch(0.865 0.127 207.078);
--color-cyan-400: oklch(0.789 0.154 211.53);
--color-cyan-500: oklch(0.715 0.143 215.221);
- --color-cyan-600: oklch(0.609 0.126 221.723);
--color-cyan-700: oklch(0.52 0.105 223.128);
- --color-cyan-800: oklch(0.45 0.085 224.283);
- --color-cyan-900: oklch(0.398 0.07 227.392);
--color-cyan-950: oklch(0.302 0.056 229.695);
- --color-sky-50: oklch(0.977 0.013 236.62);
- --color-sky-100: oklch(0.951 0.026 236.824);
- --color-sky-200: oklch(0.901 0.058 230.902);
--color-sky-300: oklch(0.828 0.111 230.318);
- --color-sky-400: oklch(0.746 0.16 232.661);
--color-sky-500: oklch(0.685 0.169 237.323);
--color-sky-600: oklch(0.588 0.158 241.966);
--color-sky-700: oklch(0.5 0.134 242.749);
- --color-sky-800: oklch(0.443 0.11 240.79);
--color-sky-900: oklch(0.391 0.09 240.876);
- --color-sky-950: oklch(0.293 0.066 243.157);
- --color-blue-50: oklch(0.97 0.014 254.604);
- --color-blue-100: oklch(0.932 0.032 255.585);
- --color-blue-200: oklch(0.882 0.059 254.128);
--color-blue-300: oklch(0.809 0.105 251.813);
--color-blue-400: oklch(0.707 0.165 254.624);
--color-blue-500: oklch(0.623 0.214 259.815);
--color-blue-600: oklch(0.546 0.245 262.881);
--color-blue-700: oklch(0.488 0.243 264.376);
- --color-blue-800: oklch(0.424 0.199 265.638);
--color-blue-900: oklch(0.379 0.146 265.522);
- --color-blue-950: oklch(0.282 0.091 267.935);
- --color-indigo-50: oklch(0.962 0.018 272.314);
- --color-indigo-100: oklch(0.93 0.034 272.788);
--color-indigo-200: oklch(0.87 0.065 274.039);
--color-indigo-300: oklch(0.785 0.115 274.713);
--color-indigo-400: oklch(0.673 0.182 276.935);
--color-indigo-500: oklch(0.585 0.233 277.117);
--color-indigo-600: oklch(0.511 0.262 276.966);
--color-indigo-700: oklch(0.457 0.24 277.023);
- --color-indigo-800: oklch(0.398 0.195 277.366);
--color-indigo-900: oklch(0.359 0.144 278.697);
- --color-indigo-950: oklch(0.257 0.09 281.288);
- --color-violet-50: oklch(0.969 0.016 293.756);
- --color-violet-100: oklch(0.943 0.029 294.588);
--color-violet-200: oklch(0.894 0.057 293.283);
--color-violet-300: oklch(0.811 0.111 293.571);
--color-violet-400: oklch(0.702 0.183 293.541);
--color-violet-500: oklch(0.606 0.25 292.717);
--color-violet-600: oklch(0.541 0.281 293.009);
--color-violet-700: oklch(0.491 0.27 292.581);
- --color-violet-800: oklch(0.432 0.232 292.759);
--color-violet-900: oklch(0.38 0.189 293.745);
- --color-violet-950: oklch(0.283 0.141 291.089);
- --color-purple-50: oklch(0.977 0.014 308.299);
- --color-purple-100: oklch(0.946 0.033 307.174);
--color-purple-200: oklch(0.902 0.063 306.703);
--color-purple-300: oklch(0.827 0.119 306.383);
--color-purple-400: oklch(0.714 0.203 305.504);
--color-purple-500: oklch(0.627 0.265 303.9);
--color-purple-600: oklch(0.558 0.288 302.321);
--color-purple-700: oklch(0.496 0.265 301.924);
- --color-purple-800: oklch(0.438 0.218 303.724);
--color-purple-900: oklch(0.381 0.176 304.987);
- --color-purple-950: oklch(0.291 0.149 302.717);
- --color-fuchsia-50: oklch(0.977 0.017 320.058);
- --color-fuchsia-100: oklch(0.952 0.037 318.852);
--color-fuchsia-200: oklch(0.903 0.076 319.62);
--color-fuchsia-300: oklch(0.833 0.145 321.434);
--color-fuchsia-400: oklch(0.74 0.238 322.16);
--color-fuchsia-500: oklch(0.667 0.295 322.15);
--color-fuchsia-600: oklch(0.591 0.293 322.896);
--color-fuchsia-700: oklch(0.518 0.253 323.949);
- --color-fuchsia-800: oklch(0.452 0.211 324.591);
--color-fuchsia-900: oklch(0.401 0.17 325.612);
- --color-fuchsia-950: oklch(0.293 0.136 325.661);
- --color-pink-50: oklch(0.971 0.014 343.198);
- --color-pink-100: oklch(0.948 0.028 342.258);
--color-pink-200: oklch(0.899 0.061 343.231);
--color-pink-300: oklch(0.823 0.12 346.018);
--color-pink-400: oklch(0.718 0.202 349.761);
--color-pink-500: oklch(0.656 0.241 354.308);
--color-pink-600: oklch(0.592 0.249 0.584);
--color-pink-700: oklch(0.525 0.223 3.958);
- --color-pink-800: oklch(0.459 0.187 3.815);
--color-pink-900: oklch(0.408 0.153 2.432);
- --color-pink-950: oklch(0.284 0.109 3.907);
- --color-rose-50: oklch(0.969 0.015 12.422);
- --color-rose-100: oklch(0.941 0.03 12.58);
--color-rose-200: oklch(0.892 0.058 10.001);
--color-rose-300: oklch(0.81 0.117 11.638);
--color-rose-400: oklch(0.712 0.194 13.428);
--color-rose-500: oklch(0.645 0.246 16.439);
--color-rose-600: oklch(0.586 0.253 17.585);
--color-rose-700: oklch(0.514 0.222 16.935);
- --color-rose-800: oklch(0.455 0.188 13.697);
--color-rose-900: oklch(0.41 0.159 10.272);
- --color-rose-950: oklch(0.271 0.105 12.094);
- --color-slate-50: oklch(0.984 0.003 247.858);
- --color-slate-100: oklch(0.968 0.007 247.896);
- --color-slate-200: oklch(0.929 0.013 255.508);
- --color-slate-300: oklch(0.869 0.022 252.894);
- --color-slate-400: oklch(0.704 0.04 256.788);
- --color-slate-500: oklch(0.554 0.046 257.417);
- --color-slate-600: oklch(0.446 0.043 257.281);
- --color-slate-700: oklch(0.372 0.044 257.287);
- --color-slate-800: oklch(0.279 0.041 260.031);
- --color-slate-900: oklch(0.208 0.042 265.755);
- --color-slate-950: oklch(0.129 0.042 264.695);
- --color-gray-50: oklch(0.985 0.002 247.839);
- --color-gray-100: oklch(0.967 0.003 264.542);
- --color-gray-200: oklch(0.928 0.006 264.531);
- --color-gray-300: oklch(0.872 0.01 258.338);
- --color-gray-400: oklch(0.707 0.022 261.325);
- --color-gray-500: oklch(0.551 0.027 264.364);
- --color-gray-600: oklch(0.446 0.03 256.802);
- --color-gray-700: oklch(0.373 0.034 259.733);
- --color-gray-800: oklch(0.278 0.033 256.848);
- --color-gray-900: oklch(0.21 0.034 264.665);
- --color-gray-950: oklch(0.13 0.028 261.692);
--color-zinc-50: oklch(0.985 0 0);
--color-zinc-100: oklch(0.967 0.001 286.375);
--color-zinc-200: oklch(0.92 0.004 286.32);
@@ -252,38 +145,9 @@
--color-zinc-800: oklch(0.274 0.006 286.033);
--color-zinc-900: oklch(0.21 0.006 285.885);
--color-zinc-950: oklch(0.141 0.005 285.823);
- --color-neutral-50: oklch(0.985 0 0);
- --color-neutral-100: oklch(0.97 0 0);
- --color-neutral-200: oklch(0.922 0 0);
- --color-neutral-300: oklch(0.87 0 0);
- --color-neutral-400: oklch(0.708 0 0);
- --color-neutral-500: oklch(0.556 0 0);
- --color-neutral-600: oklch(0.439 0 0);
- --color-neutral-700: oklch(0.371 0 0);
- --color-neutral-800: oklch(0.269 0 0);
- --color-neutral-900: oklch(0.205 0 0);
- --color-neutral-950: oklch(0.145 0 0);
- --color-stone-50: oklch(0.985 0.001 106.423);
- --color-stone-100: oklch(0.97 0.001 106.424);
- --color-stone-200: oklch(0.923 0.003 48.717);
- --color-stone-300: oklch(0.869 0.005 56.366);
- --color-stone-400: oklch(0.709 0.01 56.259);
- --color-stone-500: oklch(0.553 0.013 58.071);
- --color-stone-600: oklch(0.444 0.011 73.639);
- --color-stone-700: oklch(0.374 0.01 67.558);
- --color-stone-800: oklch(0.268 0.007 34.298);
- --color-stone-900: oklch(0.216 0.006 56.043);
- --color-stone-950: oklch(0.147 0.004 49.25);
--color-black: #000;
--color-white: #fff;
--spacing: 0.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;
@@ -302,92 +166,23 @@
--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: -0.05em;
- --tracking-tight: -0.025em;
- --tracking-normal: 0em;
- --tracking-wide: 0.025em;
- --tracking-wider: 0.05em;
- --tracking-widest: 0.1em;
- --leading-tight: 1.25;
- --leading-snug: 1.375;
- --leading-normal: 1.5;
- --leading-relaxed: 1.625;
- --leading-loose: 2;
- --radius-xs: 0.125rem;
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
--radius-2xl: 1rem;
--radius-3xl: 1.5rem;
- --radius-4xl: 2rem;
- --shadow-2xs: 0 1px rgb(0 0 0 / 0.05);
- --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
- --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
- --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1),
- 0 2px 4px -2px rgb(0 0 0 / 0.1);
- --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1),
- 0 4px 6px -4px rgb(0 0 0 / 0.1);
- --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1),
- 0 8px 10px -6px rgb(0 0 0 / 0.1);
- --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
- --inset-shadow-2xs: inset 0 1px rgb(0 0 0 / 0.05);
- --inset-shadow-xs: inset 0 1px 1px rgb(0 0 0 / 0.05);
- --inset-shadow-sm: inset 0 2px 4px rgb(0 0 0 / 0.05);
- --drop-shadow-xs: 0 1px 1px rgb(0 0 0 / 0.05);
- --drop-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.15);
- --drop-shadow-md: 0 3px 3px rgb(0 0 0 / 0.12);
- --drop-shadow-lg: 0 4px 4px rgb(0 0 0 / 0.15);
- --drop-shadow-xl: 0 9px 7px rgb(0 0 0 / 0.1);
- --drop-shadow-2xl: 0 25px 25px rgb(0 0 0 / 0.15);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
- --animate-spin: spin 1s linear infinite;
- --animate-ping: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
- --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 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: 150ms;
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
--default-font-family: var(--font-sans);
```
</details>
If you have `ripgrep` installed, you can use this command to verify that
these variables are indeed not used anywhere:
<details>
```shell
rg "\-\-font-serif\b"
rg "\-\-color-red-50\b"
rg "\-\-color-red-100\b"
rg "\-\-color-red-800\b"
rg "\-\-color-red-950\b"
rg "\-\-color-orange-50\b"
rg "\-\-color-orange-100\b"
rg "\-\-color-orange-800\b"
rg "\-\-color-orange-950\b"
rg "\-\-color-amber-50\b"
rg "\-\-color-amber-100\b"
rg "\-\-color-amber-200\b"
rg "\-\-color-amber-300\b"
rg "\-\-color-amber-800\b"
rg "\-\-color-amber-900\b"
rg "\-\-color-yellow-50\b"
rg "\-\-color-yellow-100\b"
rg "\-\-color-yellow-200\b"
rg "\-\-color-yellow-500\b"
rg "\-\-color-yellow-800\b"
rg "\-\-color-yellow-900\b"
rg "\-\-color-lime-50\b"
rg "\-\-color-lime-100\b"
rg "\-\-color-lime-200\b"
rg "\-\-color-lime-500\b"
rg "\-\-color-lime-800\b"
rg "\-\-color-lime-900\b"
rg "\-\-color-green-50\b"
rg "\-\-color-green-100\b"
rg "\-\-color-green-200\b"
rg "\-\-color-green-300\b"
rg "\-\-color-green-800\b"
rg "\-\-color-green-950\b"
rg "\-\-color-emerald-50\b"
rg "\-\-color-emerald-100\b"
rg "\-\-color-emerald-200\b"
rg "\-\-color-emerald-300\b"
rg "\-\-color-emerald-800\b"
rg "\-\-color-emerald-950\b"
rg "\-\-color-teal-50\b"
rg "\-\-color-teal-100\b"
rg "\-\-color-teal-200\b"
rg "\-\-color-teal-800\b"
rg "\-\-color-teal-950\b"
rg "\-\-color-cyan-50\b"
rg "\-\-color-cyan-100\b"
rg "\-\-color-cyan-200\b"
rg "\-\-color-cyan-600\b"
rg "\-\-color-cyan-800\b"
rg "\-\-color-cyan-900\b"
rg "\-\-color-sky-50\b"
rg "\-\-color-sky-100\b"
rg "\-\-color-sky-200\b"
rg "\-\-color-sky-400\b"
rg "\-\-color-sky-800\b"
rg "\-\-color-sky-950\b"
rg "\-\-color-blue-50\b"
rg "\-\-color-blue-100\b"
rg "\-\-color-blue-200\b"
rg "\-\-color-blue-800\b"
rg "\-\-color-blue-950\b"
rg "\-\-color-indigo-50\b"
rg "\-\-color-indigo-100\b"
rg "\-\-color-indigo-800\b"
rg "\-\-color-indigo-950\b"
rg "\-\-color-violet-50\b"
rg "\-\-color-violet-100\b"
rg "\-\-color-violet-800\b"
rg "\-\-color-violet-950\b"
rg "\-\-color-purple-50\b"
rg "\-\-color-purple-100\b"
rg "\-\-color-purple-800\b"
rg "\-\-color-purple-950\b"
rg "\-\-color-fuchsia-50\b"
rg "\-\-color-fuchsia-100\b"
rg "\-\-color-fuchsia-800\b"
rg "\-\-color-fuchsia-950\b"
rg "\-\-color-pink-50\b"
rg "\-\-color-pink-100\b"
rg "\-\-color-pink-800\b"
rg "\-\-color-pink-950\b"
rg "\-\-color-rose-50\b"
rg "\-\-color-rose-100\b"
rg "\-\-color-rose-800\b"
rg "\-\-color-rose-950\b"
rg "\-\-color-slate-50\b"
rg "\-\-color-slate-100\b"
rg "\-\-color-slate-200\b"
rg "\-\-color-slate-300\b"
rg "\-\-color-slate-400\b"
rg "\-\-color-slate-500\b"
rg "\-\-color-slate-600\b"
rg "\-\-color-slate-700\b"
rg "\-\-color-slate-800\b"
rg "\-\-color-slate-900\b"
rg "\-\-color-slate-950\b"
rg "\-\-color-gray-50\b"
rg "\-\-color-gray-100\b"
rg "\-\-color-gray-200\b"
rg "\-\-color-gray-300\b"
rg "\-\-color-gray-400\b"
rg "\-\-color-gray-500\b"
rg "\-\-color-gray-600\b"
rg "\-\-color-gray-700\b"
rg "\-\-color-gray-800\b"
rg "\-\-color-gray-900\b"
rg "\-\-color-gray-950\b"
rg "\-\-color-neutral-50\b"
rg "\-\-color-neutral-100\b"
rg "\-\-color-neutral-200\b"
rg "\-\-color-neutral-300\b"
rg "\-\-color-neutral-400\b"
rg "\-\-color-neutral-500\b"
rg "\-\-color-neutral-600\b"
rg "\-\-color-neutral-700\b"
rg "\-\-color-neutral-800\b"
rg "\-\-color-neutral-900\b"
rg "\-\-color-neutral-950\b"
rg "\-\-color-stone-50\b"
rg "\-\-color-stone-100\b"
rg "\-\-color-stone-200\b"
rg "\-\-color-stone-300\b"
rg "\-\-color-stone-400\b"
rg "\-\-color-stone-500\b"
rg "\-\-color-stone-600\b"
rg "\-\-color-stone-700\b"
rg "\-\-color-stone-800\b"
rg "\-\-color-stone-900\b"
rg "\-\-color-stone-950\b"
rg "\-\-breakpoint-sm\b"
rg "\-\-breakpoint-md\b"
rg "\-\-breakpoint-lg\b"
rg "\-\-breakpoint-xl\b"
rg "\-\-breakpoint-2xl\b"
rg "\-\-container-3xs\b"
rg "\-\-container-2xs\b"
rg "\-\-text-lg--line-height\b"
rg "\-\-text-xl--line-height\b"
rg "\-\-text-2xl--line-height\b"
rg "\-\-text-3xl\b"
rg "\-\-text-3xl--line-height\b"
rg "\-\-text-4xl\b"
rg "\-\-text-4xl--line-height\b"
rg "\-\-text-5xl\b"
rg "\-\-text-5xl--line-height\b"
rg "\-\-text-6xl\b"
rg "\-\-text-6xl--line-height\b"
rg "\-\-text-7xl\b"
rg "\-\-text-7xl--line-height\b"
rg "\-\-text-8xl\b"
rg "\-\-text-8xl--line-height\b"
rg "\-\-text-9xl\b"
rg "\-\-text-9xl--line-height\b"
rg "\-\-font-weight-thin\b"
rg "\-\-font-weight-extralight\b"
rg "\-\-font-weight-light\b"
rg "\-\-font-weight-extrabold\b"
rg "\-\-font-weight-black\b"
rg "\-\-tracking-tighter\b"
rg "\-\-tracking-tight\b"
rg "\-\-tracking-normal\b"
rg "\-\-tracking-wide\b"
rg "\-\-tracking-wider\b"
rg "\-\-tracking-widest\b"
rg "\-\-leading-tight\b"
rg "\-\-leading-snug\b"
rg "\-\-leading-normal\b"
rg "\-\-leading-relaxed\b"
rg "\-\-leading-loose\b"
rg "\-\-radius-xs\b"
rg "\-\-radius-4xl\b"
rg "\-\-shadow-2xs\b"
rg "\-\-shadow-xs\b"
rg "\-\-shadow-sm\b"
rg "\-\-shadow-md\b"
rg "\-\-shadow-lg\b"
rg "\-\-shadow-xl\b"
rg "\-\-shadow-2xl\b"
rg "\-\-inset-shadow-2xs\b"
rg "\-\-inset-shadow-xs\b"
rg "\-\-inset-shadow-sm\b"
rg "\-\-drop-shadow-xs\b"
rg "\-\-drop-shadow-sm\b"
rg "\-\-drop-shadow-md\b"
rg "\-\-drop-shadow-lg\b"
rg "\-\-drop-shadow-xl\b"
rg "\-\-drop-shadow-2xl\b"
rg "\-\-animate-spin\b"
rg "\-\-animate-ping\b"
rg "\-\-animate-pulse\b"
rg "\-\-animate-bounce\b"
rg "\-\-blur-xs\b"
rg "\-\-blur-sm\b"
rg "\-\-blur-lg\b"
rg "\-\-blur-2xl\b"
rg "\-\-blur-3xl\b"
rg "\-\-perspective-dramatic\b"
rg "\-\-perspective-near\b"
rg "\-\-perspective-normal\b"
rg "\-\-perspective-midrange\b"
rg "\-\-perspective-distant\b"
rg "\-\-aspect-video\b"
```
</details>
The only exception I noticed is that we have this:
```css
src/typography.utilities.css
10: @media (width >= theme(--breakpoint-sm)) {
```
But this is not a variable, but it's replaced at build time with the
actual value, so this is not a real issue.
Testing on other templates:
<img width="2968" alt="image"
src="https://github.com/user-attachments/assets/cabf121d-4cb9-468f-9cf5-ceb02609dc7d"
/>
Fixes: https://github.com/tailwindlabs/tailwindcss/issues/16145
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
|
||
|
|
9bfeb337e2
|
Fix resolve_globs crash when at root directory (#15988)
Fixes a crash found in Dockerfiles when a build takes place at the root directory. It's not a good practice to keep your application logic in `/`, but it probably shouldn't cause a crash either. I found this particularly difficult to write tests for because it would involve either running a glob on my real filesystem starting from `/` or mocking the calls to `fs` which as far as I can tell isn't supported in the codebase and would be out of scope to try to do here. Fixes #15987 |
||
|
|
b492187c49
|
Fix Oxide scanner bugs (#15974)
Fixes #15632 Fixes #15740 This PR fixes a number of Oxide scanner bugs reported over various channels, specifically: - When using the Svelte class shorthand split over various lines, we weren't extracting class names properly: ```svelte <div class:underline={isUnderline}> </div> ``` - We now extract classes when using the class shortcut in Angular: ```html <div [class.underline]=\"bool\"></div> ``` - We now validate parentheses within arbitrary candidates so that we don't consume invalid arbitrary candidates anymore which allows us to parse the following case properly: ```js const classes = [wrapper("bg-red-500")] ``` ## Test plan Added unit tests --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
e02a29fa94
|
Don’t look at ignore files outside initialized repos (#15941)
Right now, when Oxide is scanning for files, it considers ignore files in the "root" directory it is scanning as well as all parent directories. We honor .gitignore files even when not in a git repo as an optimization in case a project has been created, contains a .gitignore, but no repo has actually been initialized. However, this has an unintended side effect of including ignore files _ouside of a repo_ when there is one. This means that if you have a .gitignore file in your home folder it'll get applied even when you're inside a git repo which is not what you'd expect. This PR addresses this by checking to see the folder being scanned is inside a repo and turns on a flag that ensures .gitignore files from the repo are the only ones used (global ignore files configured in git still work tho). This still needs lots of tests to make sure things work as expected. Fixes #15876 --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com> |
||
|
|
9075db0c10
|
Apply Clippy suggestions (#15549)
While working on other PRs, I noticed that Clippy had some suggestions (warnings). This PR fixes those warnings. |
||
|
|
00ccbdc937
|
Don’t detect arbitrary properties when preceded by an escape (#15456)
This is a targeted bug fix uncovered by the v4 docs. Given this code: ```html <!-- [!code word:group-has-\\[a\\]\\:block] --> ``` We'd pick up `[a\\]\\:block]` as a candidate which would then make it far enough to get output to CSS and throw an error. This makes sure we don't try to start an arbitrary property if the preceding character is a `\` cc @RobinMalfait this look okay? --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com> |
||
|
|
bcf70990a7
|
Improve debug logs (#15303)
This PR improves the debug logs for the `@tailwindcss/postcss` integration. It uses custom instrumentation to provide a nested but detailed overview of where time is spent during the build process. The updated logs look like this: ``` [0.15ms] [@tailwindcss/postcss] src/app/geistsans_9fc57718.module.css [0.14ms] ↳ Quick bail check [0.02ms] [@tailwindcss/postcss] src/app/geistsans_9fc57718.module.css [0.01ms] ↳ Quick bail check [0.03ms] [@tailwindcss/postcss] src/app/geistmono_b9f59162.module.css [0.02ms] ↳ Quick bail check [0.12ms] [@tailwindcss/postcss] src/app/geistmono_b9f59162.module.css [0.11ms] ↳ Quick bail check [42.09ms] [@tailwindcss/postcss] src/app/globals.css [ 0.01ms] ↳ Quick bail check [12.12ms] ↳ Setup compiler [ 0.11ms] ↳ PostCSS AST -> Tailwind CSS AST [11.99ms] ↳ Create compiler [ 0.07ms] ↳ Register full rebuild paths [ 0.06ms] ↳ Setup scanner [ 7.51ms] ↳ Scan for candidates [ 5.86ms] ↳ Register dependency messages [ 5.88ms] ↳ Build utilities [ 8.34ms] ↳ Optimization [ 0.23ms] ↳ AST -> CSS [ 4.20ms] ↳ Lightning CSS [ 3.89ms] ↳ CSS -> PostCSS AST [ 1.97ms] ↳ Update PostCSS AST ``` |
||
|
|
408fa99849
|
Use AST transformations in @tailwindcss/postcss (#15297)
This PR improves the `@tailwindcss/postcss` integration by using direct AST transformations between our own AST and PostCSS's AST. This allows us to skip a step where we convert our AST into a string, then parse it back into a PostCSS AST. The only downside is that we still have to print the AST into a string if we want to optimize the CSS using Lightning CSS. Luckily this only happens in production (`NODE_ENV=production`). This also introduces a new private `compileAst` API, that allows us to accept an AST as the input. This allows us to skip the PostCSS AST -> string -> parse into our own AST step. To summarize: Instead of: - Input: `PostCSS AST` -> `.toString()` -> `CSS.parse(…)` -> `Tailwind CSS AST` - Output: `Tailwind CSS AST` -> `toCSS(ast)` -> `postcss.parse(…)` -> `PostCSS AST` We will now do this instead: - Input: `PostCSS AST` -> `transform(…)` -> `Tailwind CSS AST` - Output: `Tailwind CSS AST` -> `transform(…)` -> `PostCSS AST` --- Running this on Catalyst, the time spent in the `@tailwindcss/postcss` looks like this: - Before: median time per run: 19.407687 ms - After: median time per run: 11.8796455 ms This is tested on Catalyst which roughly generates ~208kb worth of CSS in dev mode. While it's not a lot, skipping the stringification and parsing seems to improve this step by ~40%. Note: these times exclude scanning the actual candidates and only time the work needed for parsing/stringifying the CSS from and into ASTs. The actual numbers are a bit higher because of the Oxide scanner reading files from disk. But since that part is going to be there no matter what, it's not fair to include it in this benchmark. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
6af483547e
|
Improve performance of scanning source files (#15270)
This PR improves scanning files by scanning chunks of the files in
parallel. Each chunk is separated by new lines since we can't use
whitespace in classes anyway.
This also means that we can use the power of your CPU to scan files
faster. The extractor itself also has less state to worry about on these
smaller chunks.
On a dedicated benchmark machine: Mac Mini, M1, 16 GB RAM
```shellsession
❯ hyperfine --warmup 15 --runs 50 \
-n NEW 'bun --bun /Users/ben/github.com/tailwindlabs/tailwindcss/packages/@tailwindcss-cli/src/index.ts -i ./tailwind.css -o out.css' \
-n CURRENT 'bun --bun /Users/ben/github.com/tailwindlabs/tailwindcss--next/packages/@tailwindcss-cli/src/index.ts -i ./tailwind.css -o out.css'
Benchmark 1: NEW
Time (mean ± σ): 337.2 ms ± 2.9 ms [User: 1376.6 ms, System: 80.9 ms]
Range (min … max): 331.0 ms … 345.3 ms 50 runs
Benchmark 2: CURRENT
Time (mean ± σ): 730.3 ms ± 3.8 ms [User: 978.9 ms, System: 78.7 ms]
Range (min … max): 722.0 ms … 741.8 ms 50 runs
Summary
NEW ran
2.17 ± 0.02 times faster than CURRENT
```
On a more powerful machine, MacBook Pro M1 Max, 64 GB RAM, the results
look even more promising:
```shellsession
❯ hyperfine --warmup 15 --runs 50 \
-n NEW 'bun --bun /Users/robin/github.com/tailwindlabs/tailwindcss/packages/@tailwindcss-cli/src/index.ts -i ./tailwind.css -o out.css' \
-n CURRENT 'bun --bun /Users/robin/github.com/tailwindlabs/tailwindcss--next/packages/@tailwindcss-cli/src/index.ts -i ./tailwind.css -o out.css'
Benchmark 1: NEW
Time (mean ± σ): 307.8 ms ± 24.5 ms [User: 1124.8 ms, System: 187.9 ms]
Range (min … max): 291.7 ms … 397.9 ms 50 runs
Benchmark 2: CURRENT
Time (mean ± σ): 754.7 ms ± 27.2 ms [User: 934.9 ms, System: 217.6 ms]
Range (min … max): 735.5 ms … 845.6 ms 50 runs
Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
Summary
NEW ran
2.45 ± 0.21 times faster than CURRENT
```
> Note: This last benchmark is running on my main machine which is more
"busy" compared to my benchmark machine. Because of this I had to
increase the `--runs` to get statistically better results. There is
still a warning present, but the overall numbers are still very
promising.
---
These benchmarks are running on our Tailwind UI project where we have
>1000 files, and >750 000 lines of code in those files.
| Before | After |
| --- | --- |
| <img width="385" alt="image"
src="https://github.com/user-attachments/assets/4786b842-bedc-4456-a9ca-942f72ca738c">
| <img width="382" alt="image"
src="https://github.com/user-attachments/assets/fb43cff8-95e7-453e-991e-d036c64659ba">
|
---
I am sure there is more we can do here, because reading all of these
1000 files only takes ~10ms, whereas parsing all these files takes
~180ms. But I'm still happy with these results as an incremental
improvement.
For good measure, I also wanted to make sure that we didn't regress on
smaller projects. Running this on Catalyst, we only have to deal with
~100 files and ~18 000 lines of code. In this case reading all the files
takes ~890µs and parsing takes about ~4ms.
| Before | After |
| --- | --- |
| <img width="381" alt="image"
src="https://github.com/user-attachments/assets/25d4859f-d058-4f57-a2f6-219d8c4b1804">
| <img width="390" alt="image"
src="https://github.com/user-attachments/assets/f06d7536-337b-4dc0-a460-6a9f141c65f5">
|
Not a huge difference, still better and definitely no regressions which
sounds like a win to me.
---
**Edit:** after talking to @thecrypticace, instead of splitting on any
whitespace we just split on newlines. This makes the chunks a bit
larger, but it reduces the overhead of the extractor itself. This now
results in a 2.45x speedup in Tailwind UI compared to 1.94x speedup.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
|
||
|
|
4bdc724a22
|
Fix scanning classes delimited by tab characters (#15169)
This PR fixes an issue where multi-line candidates in Svelte files couldn't be found as reported in #15148 After digging in, the real culprit seems to be that the reproduction used tab `\t` characters instead of spaces and we only delimited explicitly on spaces. Initially I couldn't reproduce this in an integration test until we (@thecrypticace and I) realised that `\t` was being used. ## Test plan: This PR adds an integration test that fails before the fix happens. The fix itself is easy in the sense that we just use all ascii whitespace characters instead of just spaces. Fixes: #15148 |
||
|
|
8122837dde
|
Allow simple utilities with numbers in Oxide (#15110)
Fixes #15072 --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> |
||
|
|
3cf5c2df79
|
Disallow empty arbitrary values (#15055)
This PR makes the candidate parser more strict by not allowing empty arbitrary values. Examples that are not allowed anymore: - `bg-[]` — arbitrary value - `bg-()` — arbitrary value, var shorthand - `bg-[length:]` — arbitrary value, with typehint - `bg-(length:)` — arbitrary value, with typehint, var shorthand - `bg-red-500/[]` — arbitrary modifier - `bg-red-500/()` — arbitrary modifier, var shorthand - `data-[]:flex` — arbitrary value for variant - `data-():flex` — arbitrary value for variant, var shorthand - `group-visible/[]:flex` — arbitrary modifier for variant - `group-visible/():flex` — arbitrary modifier for variant, var shorthand If you are trying to trick the parser by injecting some spaces like this: - `bg-[_]` Then that is also not allowed. |
||
|
|
3dc3bad781
|
Re-introduce automatic var injection shorthand (#15020)
This PR re-introduces the automatic var injection feature. For some backstory, we used to support classes such as `bg-[--my-color]` that resolved as-if you wrote `bg-[var(--my-color)]`. The is issue is that some newer CSS properties accepts dashed-idents (without the `var(…)`). This means that some properties accept `view-timeline-name: --my-name;` (see: https://developer.mozilla.org/en-US/docs/Web/CSS/view-timeline-name). To make this a tiny bit worse, these properties _also_ accept `var(--my-name-reference)` where the variable `--my-name-reference` could reference a dashed-ident such as `--my-name`. This makes the `bg-[--my-color]` ambiguous because we don't know if you want `var(--my-color)` or `--my-color`. With this PR, we bring back the automatic var injection feature as syntactic sugar, but we use a different syntax to avoid the ambiguity. Instead of `bg-[--my-color]`, you can now write `bg-(--my-color)` to get the same effect as `bg-[var(--my-color)]`. This also applies to modifiers, so `bg-red-500/[var(--my-opacity)]` can be written as `bg-red-500/(--my-opacity)`. To go full circle, you can rewrite `bg-[var(--my-color)]/[var(--my-opacity)]` as `bg-(--my-color)/(--my-opacity)`. --- This is implemented as syntactical sugar at the parsing stage and handled when re-printing. Internally the system (and every plugin) still see the proper `var(--my-color)` value. Since this is also handled during printing of the candidate, codemods don't need to be changed but they will provide the newly updated syntax. E.g.: running this on the Catalyst codebase, you'll now see changes like this: <img width="542" alt="image" src="https://github.com/user-attachments/assets/8f0e26f8-f4c9-4cdc-9f28-52307c38610e"> Whereas before we converted this to the much longer `min-w-[var(--button-width)]`. --- Additionally, this required some changes to the Oxide scanner to make sure that `(` and `)` are valid characters for arbitrary-like values. --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
92007a5b23
|
Fix crash when using @source containing .. (#14831)
This PR fixes an issue where a `@source` crashes when the path eventually resolves to a path ending in `..`. We have to make sure that we canonicalize the path to make sure that we are working with the real directory. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
3b2ca85138
|
Fix new file detection in PostCSS plugin (#14829)
We broke this at some point — probably when we tried to optimize rebuilds in PostCSS by not performing a full auto-source detection scan. This PR addresses this problem by: 1. Storing a list of found directories 2. Comparing their mod times on every scan 3. If the mod time has changed we scan the directory for new files which we then store and scan |
||
|
|
f3786253f2
|
Fix integration tests on Windows (#14824)
This PR fixes Windows related path issues after merging #14820 --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
d68a780f98
|
Auto source detection improvements (#14820)
This PR introduces a new `source(…)` argument and improves on the
existing `@source`. The goal of this PR is to make the automatic source
detection configurable, let's dig in.
By default, we will perform automatic source detection starting at the
current working directory. Auto source detection will find plain text
files (no binaries, images, ...) and will ignore git-ignored files.
If you want to start from a different directory, you can use the new
`source(…)` next to the `@import "tailwindcss/utilities"
layer(utilities) source(…)`.
E.g.:
```css
/* ./src/styles/index.css */
@import 'tailwindcss/utilities' layer(utilities) source('../../');
```
Most people won't split their source files, and will just use the simple
`@import "tailwindcss";`, because of this reason, you can use
`source(…)` on the import as well:
E.g.:
```css
/* ./src/styles/index.css */
@import 'tailwindcss' source('../../');
```
Sometimes, you want to rely on auto source detection, but also want to
look in another directory for source files. In this case, yuo can use
the `@source` directive:
```css
/* ./src/index.css */
@import 'tailwindcss';
/* Look for `blade.php` files in `../resources/views` */
@source '../resources/views/**/*.blade.php';
```
However, you don't need to specify the extension, instead you can just
point the directory and all the same automatic source detection rules
will apply.
```css
/* ./src/index.css */
@import 'tailwindcss';
@source '../resources/views';
```
If, for whatever reason, you want to disable the default source
detection feature entirely, and only want to rely on very specific glob
patterns you define, then you can disable it via `source(none)`.
```css
/* Completely disable the default auto source detection */
@import 'tailwindcss' source(none);
/* Only look at .blade.php files, nothing else */
@source "../resources/views/**/*.blade.php";
```
Note: even with `source(none)`, if your `@source` points to a directory,
then auto source detection will still be performed in that directory. If
you don't want that, then you can simply add explicit files in the globs
as seen in the previous example.
```css
/* Completely disable the default auto source detection */
@import 'tailwindcss' source(none);
/* Run auto source detection in `../resources/views` */
@source "../resources/views";
```
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
|
||
|
|
1467dab59e
|
Fix template migration issues (#14600)
This PR fixes two issues we found when testing the candidate codemodes: 1. Sometimes, core would emit the same candidate twice. This would result into rewriting a range multiple times, without realizing that this change might already be applied, causing it to swallow or duplicate some bytes. 2. The codemods were mutating the `Candidate` object, however since the `Candidate` parsing is _cached_ in core, it would sometimes return the same instance. This is an issue especially since we monkey patch the prefix to `null` when migrating prefixed candidates. This means that a candidate would be cached that would be _invalid relative to the real design system_. We fixed this by making sure the mutations would only be applied to clones of the `Candidate` and I changed the `DesignSystem` API to return `ReadOnly<T>` versions of these candidates. A better solution would maybe be to disable the cache at all but this requires broader changes in Core. |
||
|
|
35b84cc313
|
Improve @tailwindcss/postcss performance for initial builds (#14565)
This PR improves the performance of the `@tailwindcss/postcss` plugin. Before this change we created 2 compiler instances instead of a single one. On a project where a `tailwindcss.config.ts` file is used, this means that the timings look like this: ``` [@tailwindcss/postcss] Setup compiler: 137.525ms ⋮ [@tailwindcss/postcss] Setup compiler: 43.95ms ``` This means that with this small change, we can easily shave of ~50ms for initial PostCSS builds. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
bbe08c3b84
|
Add binary extensions found in macOS traces (#14584)
While we were doing some tracing for Rust memory issues, we noticed that
the builds became slower and slower. Turns out we did store the macOS
`.trace` dirs within the auto content directory of the Tailwind CSS
instance we were profiling and that _some of the files were massive
binary files that we were now scanning_.
Here's the anatomy of a single trace:
```
[ 320] .
├── [497K] form.template
├── [5.1K] open.creq
├── [ 261] UI_state_metadata.bin
├── [ 160] corespace
│ ├── [1.2K] MANIFEST.plist
│ ├── [ 96] currentRun
│ │ └── [ 96] core
│ │ └── [ 128] uniquing
│ │ ├── [ 128] arrayUniquer
│ │ │ ├── [10.0K] integeruniquer.data
│ │ │ └── [ 0] integeruniquer.index
│ │ └── [ 128] typedArrayUniquer
│ │ ├── [10.0K] integeruniquer.data
│ │ └── [ 0] integeruniquer.index
│ └── [ 96] run1
│ └── [ 192] core
│ ├── [ 224] uniquing
│ │ ├── [6.5K] EngineeringTypes.etypes
│ │ ├── [3.5K] strings
│ │ ├── [ 344] TOC
│ │ ├── [ 128] arrayUniquer
│ │ │ ├── [1.1K] integeruniquer.data
│ │ │ └── [ 96] integeruniquer.index
│ │ └── [ 128] typedArrayUniquer
│ │ ├── [1.0K] integeruniquer.data
│ │ └── [ 28] integeruniquer.index
│ ├── [ 192] stores
│ │ ├── [ 224] indexed-store-0
│ │ │ ├── [1.6K] bulkstore
│ │ │ ├── [1.2K] spindex.0
│ │ │ ├── [1.1K] bulkstore_descriptor
│ │ │ ├── [ 433] spec.plist
│ │ │ └── [ 188] schema.xml
│ │ ├── [ 224] indexed-store-1
│ │ │ ├── [7.5K] bulkstore
│ │ │ ├── [7.0K] spindex.0
│ │ │ ├── [1.6K] bulkstore_descriptor
│ │ │ ├── [ 522] spec.plist
│ │ │ └── [ 352] schema.xml
│ │ ├── [ 224] indexed-store-2
│ │ │ ├── [ 17K] bulkstore
│ │ │ ├── [7.2K] spindex.0
│ │ │ ├── [2.0K] bulkstore_descriptor
│ │ │ ├── [ 532] spec.plist
│ │ │ └── [ 412] schema.xml
│ │ └── [ 192] indexed-store-3
│ │ ├── [1.8K] bulkstore_descriptor
│ │ ├── [ 426] spec.plist
│ │ ├── [ 399] schema.xml
│ │ └── [ 50] bulkstore
│ ├── [ 96] core-config
│ │ └── [2.0K] exposedTableInfo.plist
│ └── [ 96] table-manager
│ └── [1.6K] tables.plist
├── [ 128] Trace1.run
│ ├── [966M] event_data_52237.oa
│ └── [ 52K] RunIssues.storedata
├── [ 128] instrument_data
│ ├── [ 96] EF4DC038-8A17-421A-8050-39DD0980C06F
│ │ └── [ 96] run_data
│ │ └── [ 17K] 1.run.zip
│ └── [ 96] F9F2B147-A042-43F0-A791-EA6D63C7C1E8
│ └── [ 96] run_data
│ └── [2.2K] 1.run.zip
├── [ 128] symbols
│ ├── [ 608] stores
│ │ ├── [453K] D14A8304-5F09-385D-9AA6-1B0C815B6356.symbolsarchive
│ │ ├── [ 36K] 4E9DB999-EFF4-3C83-B4E8-E1914D2C331E.symbolsarchive
│ │ ├── [3.7K] B5D897DF-D536-3668-BBAD-A17119439EF0.symbolsarchive
│ │ ├── [3.1K] 57FFCB9D-A6C9-3E9A-AA82-40F192626527.symbolsarchive
│ │ ├── [2.9K] 69AA9AB7-C5DE-3CAE-BF1A-384F9F36A3E0.symbolsarchive
│ │ ├── [2.8K] F453C5AE-3568-3AAA-AAA0-D2FDFBB9BC7A.symbolsarchive
│ │ ├── [2.6K] 62D27203-665F-3AA7-8BE9-7E3C3A847353.symbolsarchive
│ │ ├── [2.4K] 9896C713-054D-377D-88B4-45E1DE3FB6C5.symbolsarchive
│ │ ├── [2.1K] 249D8F21-72A2-3A80-ADC1-7BEAF24B5B58.symbolsarchive
│ │ ├── [2.1K] FA954AC0-FCC5-3711-800B-432011ACD89E.symbolsarchive
│ │ ├── [2.0K] E7ED11EE-AFB0-3B96-90A8-F1835726B9B8.symbolsarchive
│ │ ├── [1.3K] 5B476F9B-DF8B-356A-8582-615D4AD08504.symbolsarchive
│ │ ├── [1.3K] 6102110F-7ED8-34C2-95D3-C5ACCB41497E.symbolsarchive
│ │ ├── [1.1K] EC86EDBF-30B9-3BF8-A358-C8D51805B016.symbolsarchive
│ │ ├── [1.0K] 6740FF57-8D20-3FC7-97F5-B2AFB6E5F48A.symbolsarchive
│ │ ├── [ 963] 9A72FD37-D827-3D6D-B6F4-422621E36C94.symbolsarchive
│ │ └── [ 948] 67A46592-439B-36DA-8A08-A7CD777A43A8.symbolsarchive
│ └── [ 290] MANIFEST.plist
└── [ 96] shared_data
└── [ 96] 1.run
└── [ 14M] 533F36D4-2C90-4030-95CA-74077F2E26D7.zip
29 directories, 59 files
```
Note that the biggest binary file is a `.oa` but I've also added
`.storedata` and `.symbolsarchive` as binary extensions to this PR.
|
||
|
|
fad5c81045
|
Use official FxHash crate (#14530) | ||
|
|
732147a761
|
Add setup for template migrations (#14502)
This PR adds the initial setup and a first codemod for the template migrations. These are a new set of migrations that operate on files defined in the Tailwind v3 config as part of the `content` option (so your HTML, JavaScript, TSX files etc.). The migration for this is integrated in the new `@tailwindcss/upgrade` package and will require pointing the migration to an input JavaScript config file, like this: ``` npx @tailwindcss/upgrade --config tailwind.config.js ``` The idea of template migrations is to apply breaking changes from the v3 to v4 migration within your template files. ## Migrating !important syntax The first migration that I’m adding with this PR is to ensure we use the v4 important syntax that has the exclamation mark at the end of the utility. For example, this: ```html <div class="!flex sm:!block"></div> ``` Will now turn into: ```html <div class="flex! sm:block!"></div> ``` ## Architecture considerations Implementation wise, we make use of Oxide to scan the content files fast and efficiently. By relying on the same scanner als Tailwind v4, we guarantee that all candidates that are part of the v4 output will have gone through a migration. Migrations itself operate on the abstract `Candidate` type, similar to the type we use in the v4 codebase. It will parse the candidate into its parts so they can easily be introspected/modified. Migrations are typed as: ```ts type TemplateMigration = (candidate: Candidate) => Candidate | null ``` `null` should be returned if the `Candidate` does not need a migration. We currently use the v4 `parseCandidate` function to get an abstract definition of the candidate rule that we can operate on. _This will likely need to change in the future as we need to fork `parseCandidate` for v3 specific syntax_. Additionally, we're inlining a `printCandidate` function that can stringify the abstract `Candidate` type. It is not guaranteed that this is an identity function since some information can be lost during the parse step. This is not a problem though, because migrations will only run selectively and if none of the selectors trigger, the candidates are not updated. h/t to @RobinMalfait for providing the printer. So the overall flow of a migration looks like this: - Scan the config file for `content` files - Use Oxide to extract a list of candidate and their positions from these `content` files - Run a few migrations that operate on the `Candidate` abstract type. - Print the updated `Candidate` back into the original `content` file. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
a902128640
|
Improve Oxide scanner API (#14187)
This PR updates the API for interacting with the Oxide API. Until now,
we used the name `scanDir(…)` which is fine, but we do way more work
right now.
We now have features such as:
1. Auto source detection (can be turned off, e.g.: `@tailwindcss/vite`
doesn't need it)
2. Scan based on `@source`s found in CSS files
3. Do "incremental" rebuilds (which means that the `scanDir(…)` result
was stateful).
To solve these issues, this PR introduces a new `Scanner` class where
you can pass in the `detectSources` and `sources` options. E.g.:
```ts
let scanner = new Scanner({
// Optional, omitting `detectSources` field disables automatic source detection
detectSources: { base: __dirname },
// List of glob entries to scan. These come from `@source` directives in CSS.
sources: [
{ base: __dirname, pattern: "src/**/*.css" },
// …
],
});
```
The scanner object has the following API:
```ts
export interface ChangedContent {
/** File path to the changed file */
file?: string
/** Contents of the changed file */
content?: string
/** File extension */
extension: string
}
export interface DetectSources {
/** Base path to start scanning from */
base: string
}
export interface GlobEntry {
/** Base path of the glob */
base: string
/** Glob pattern */
pattern: string
}
export interface ScannerOptions {
/** Automatically detect sources in the base path */
detectSources?: DetectSources
/** Glob sources */
sources?: Array<GlobEntry>
}
export declare class Scanner {
constructor(opts: ScannerOptions)
scan(): Array<string>
scanFiles(input: Array<ChangedContent>): Array<string>
get files(): Array<string>
get globs(): Array<GlobEntry>
}
```
The `scanFiles(…)` method is used for incremental rebuilds. It takes the
`ChangedContent` array for all the new/changes files. It returns whether
we scanned any new candidates or not.
Note that the `scanner` object is stateful, this means that we don't
have to track candidates in a `Set` anymore. We can just call
`getCandidates()` when we need it.
This PR also removed some unused code that we had in the `scanDir(…)`
function to allow for sequential or parallel `IO`, and sequential or
parallel `Parsing`. We only used the same `IO` and `Parsing` strategies
for all files, so I just got rid of it.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
|
||
|
|
cc4689deef
|
Add Glimmer template extensions to Oxide content detection (#14199)
Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
541d84a3bb
|
Add @source support (#14078)
This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
a2159e80f5
|
Add Windows CI (#14065)
This PR changes the GitHub action workflow for V4 to start running all unit tests and build on both Ubuntu (our current default) _and_ Windows. This is to ensure we catch issues with paths and other Windows-specific things sooner. It does, however, not replace testing on Windows. |
||
|
|
19c8fe34fd
|
remove Rust benchmarks and fixtures (#13335)
We currently don't use these, and they are tested via the benchmarks we run as an end-to-end benchmark. |
||
|
|
1c48683a23
|
Hoist oxide/crates to just crates (#13333)
* move `oxide/crates` to `crates` * ignore `target/` folder * ensure pnpm points to `crates` instead of `oxide/crates` * ensure all paths point to `crates` instead of `oxide/crates` * update `oxide/crates` -> `crates` path in workflows * use correct path in .prettierignore * rename `crates/core` to `crates/oxide` * remove oxide folder * fix test script to run `cargo test` directly |