243 Commits

Author SHA1 Message Date
Philipp Spiess
af1d4aa683 Temporarily disable broken Windows test 2025-04-28 10:53:46 +02:00
Philipp Spiess
62ca1ec42d Fix Windows tests 2025-04-25 13:02:18 +02:00
Philipp Spiess
52000a30f0
PostCSS: Improve error recovery (#17754)
Closes #17295

This commit addresses an issue where the PostCSS plugin would get stuck
in an error state when processing files with e.g. invalid @apply
directives.

This change prevents the PostCSS plugin from getting stuck in an error
states particularly when the error happened inside an `@import`ed CSS
files (as these were not registered as dependencies correctly before).

## Error overlays

Some frameworks (e.g. Angular 19 or Next.js) handle errors inside
PostCSS transforms to render a nice error overlay. This works well and
gives immediate feedback that something went wrong. However, even when
dependencies are registered before an error is thrown, these frameworks
_will not consider changes to these dependencies anymore_ when an error
occurs, as you can see in this Next.js example:


https://github.com/user-attachments/assets/985c9dd7-daf8-4628-b4ad-6543ef220954

To avoid conditions where errors are not recoverable, this PR makes it
so that these overlays will no longer show up in the app and only be
logged to the output console. This will need follow-up upstream work
before we can revisit this.

## Test plan

- Tested with the repro in #17295. The error can now be recovered from.
- Tested with a Next.js app where the issue in the screencast above is
now no longer happening.
- Added an integration test for errors in `@import`-ed files
- Added a unit test for the changed `@apply` behavior.
2025-04-25 12:02:10 +02:00
Robin Malfait
8e826b18f3
Ensure @tailwindcss/upgrade runs on Tailwind CSS v4 projects and is idempotent (#17717)
This PR ensures that the `@tailwindcss/upgrade` tool works on existing
Tailwind CSS v4 projects. This PR also ensures that the upgrade tool is
idempotent, meaning that it can be run multiple times and it should
result in the same output.

One awesome feature this unlocks is that you can run the upgrade tool on
your codebase at any time and upgrade classes if you still have some
legacy syntaxes, such as `bg-[var(--my-color)]`, in your muscle memory.

One small note: If something changed in the first run, re-running will
not work immediately because your git repository will not be clean and
the upgrade tool requires your git repo to be clean. But once you
verified and committed your changes, the upgrade tool will be
idempotent.

Idempotency is guaranteed by ensuring that some migrations are skipped
by checking what version of Tailwind CSS you are on _before_ the version
is upgraded.

For the Tailwind CSS version: We will resolve `tailwindcss` itself to
know the _actual_ version that is installed (the one resolved from
`node_modules`). Not the one available in your package.json. Your
`package.json` could be out of sync if you reverted changes but didn't
run `npm install` yet.

Back to Idempotency:

For example, we have migrations where we change the variant order of
stacked variants. If we would run these migrations every time you run
the upgrade tool then we would be flip-flopping the order every run.

See: https://tailwindcss.com/docs/upgrade-guide#variant-stacking-order

Another example is where we rename some utilities. For example, we
rename:

| Before      | After       |
| ----------- | ----------- |
| `shadow`    | `shadow-sm` |
| `shadow-sm` | `shadow-xs` |

Notice how we have `shadow-sm` in both the `before` and `after` column.

If we would run the upgrade tool again, then we would eventually migrate
your original `shadow` to `shadow-sm` (first run) and then to
`shadow-xs` (second run). Which would result in the wrong shadow.

See: https://tailwindcss.com/docs/upgrade-guide#renamed-utilities

---

The order of upgrade steps changed a bit as well to make the internals
are easier to work with and reason about.

1. Find CSS files
2. Link JS config files (if you are in a Tailwind CSS v3 project)
3. Migrate the JS config files (if you are in a Tailwind CSS v3 project)
4. Upgrade Tailwind CSS to v4 (or the latest version at that point)
5. Migrate the stylesheets (we used to migrate the source files first)
6. Migrate the source files

This is done so that step 5 and 6 will always operate on a Tailwind CSS
v4 project and we don't need to check the version number again. This is
also necessary because your CSS file will now very likely contain
`@import "tailwindcss";` which doesn't exist in Tailwind CSS v3.

This also means that we can rely on the same internals that Tailwind CSS
actually uses for locating the source files. We will use
`@tailwindcss/oxide`'s scanner to find the source files (and it also
keeps your custom `@source` directives into account).

This PR also introduces a few actual migrations related to recent
features and changes we shipped.

1. We migrate deprecated classes to their new names:

   | Before                | After                 |
   | --------------------- | --------------------- |
   | `bg-left-top`         | `bg-top-left`         |
   | `bg-left-bottom`      | `bg-bottom-left`      |
   | `bg-right-top`        | `bg-top-right`        |
   | `bg-right-bottom`     | `bg-bottom-right`     |
   | `object-left-top`     | `object-top-left`     |
   | `object-left-bottom`  | `object-bottom-left`  |
   | `object-right-top`    | `object-top-right`    |
   | `object-right-bottom` | `object-bottom-right` |

   Introduced in:

   - https://github.com/tailwindlabs/tailwindcss/pull/17378
   - https://github.com/tailwindlabs/tailwindcss/pull/17437

2. We migrate simple arbitrary variants to their dedicated variant:

   | Before                  | After               |
   | ----------------------- | ------------------- |
   | `[&:user-valid]:flex`   | `user-valid:flex`   |
   | `[&:user-invalid]:flex` | `user-invalid:flex` |

   Introduced in:

   - https://github.com/tailwindlabs/tailwindcss/pull/12370

3. We migrate `@media` variants to their dedicated variant:

| Before | After |
| ----------------------------------------------------- |
------------------------- |
| `[@media_print]:flex` | `print:flex` |
| `[@media(prefers-reduced-motion:no-preference)]:flex` |
`motion-safe:flex` |
| `[@media(prefers-reduced-motion:reduce)]:flex` | `motion-reduce:flex`
|
| `[@media(prefers-contrast:more)]:flex` | `contrast-more:flex` |
| `[@media(prefers-contrast:less)]:flex` | `contrast-less:flex` |
| `[@media(orientation:portrait)]:flex` | `portrait:flex` |
| `[@media(orientation:landscape)]:flex` | `landscape:flex` |
| `[@media(forced-colors:active)]:flex` | `forced-colors:flex` |
| `[@media(inverted-colors:inverted)]:flex` | `inverted-colors:flex` |
| `[@media(pointer:none)]:flex` | `pointer-none:flex` |
| `[@media(pointer:coarse)]:flex` | `pointer-coarse:flex` |
| `[@media(pointer:fine)]:flex` | `pointer-fine:flex` |
| `[@media(any-pointer:none)]:flex` | `any-pointer-none:flex` |
| `[@media(any-pointer:coarse)]:flex` | `any-pointer-coarse:flex` |
| `[@media(any-pointer:fine)]:flex` | `any-pointer-fine:flex` |
| `[@media(scripting:none)]:flex` | `noscript:flex` |

The new variants related to `inverted-colors`, `pointer`, `any-pointer`
and `scripting` were introduced in:

   - https://github.com/tailwindlabs/tailwindcss/pull/11693
   - https://github.com/tailwindlabs/tailwindcss/pull/16946
   - https://github.com/tailwindlabs/tailwindcss/pull/11929
   - https://github.com/tailwindlabs/tailwindcss/pull/17431

   This also applies to the `not` case, e.g.:

| Before | After |
| --------------------------------------------------------- |
----------------------------- |
| `[@media_not_print]:flex` | `not-print:flex` |
| `[@media_not(prefers-reduced-motion:no-preference)]:flex` |
`not-motion-safe:flex` |
| `[@media_not(prefers-reduced-motion:reduce)]:flex` |
`not-motion-reduce:flex` |
| `[@media_not(prefers-contrast:more)]:flex` | `not-contrast-more:flex`
|
| `[@media_not(prefers-contrast:less)]:flex` | `not-contrast-less:flex`
|
| `[@media_not(orientation:portrait)]:flex` | `not-portrait:flex` |
| `[@media_not(orientation:landscape)]:flex` | `not-landscape:flex` |
| `[@media_not(forced-colors:active)]:flex` | `not-forced-colors:flex` |
| `[@media_not(inverted-colors:inverted)]:flex` |
`not-inverted-colors:flex` |
| `[@media_not(pointer:none)]:flex` | `not-pointer-none:flex` |
| `[@media_not(pointer:coarse)]:flex` | `not-pointer-coarse:flex` |
| `[@media_not(pointer:fine)]:flex` | `not-pointer-fine:flex` |
| `[@media_not(any-pointer:none)]:flex` | `not-any-pointer-none:flex` |
| `[@media_not(any-pointer:coarse)]:flex` |
`not-any-pointer-coarse:flex` |
| `[@media_not(any-pointer:fine)]:flex` | `not-any-pointer-fine:flex` |
| `[@media_not(scripting:none)]:flex` | `not-noscript:flex` |

For each candidate, we run a set of upgrade migrations. If at the end of
the migrations the original candidate is still the same as the new
candidate, then we will parse & print the candidate one more time to
pretty print into consistent classes. Luckily parsing is cached so there
is no real downside overhead.

Consistency (especially with arbitrary variants and values) will reduce
your CSS file because there will be fewer "versions" of your class.

Concretely, the pretty printing will apply changes such as:

| Before                 | After             |
| ---------------------- | ----------------- |
| `bg-[var(--my-color)]` | `bg-(--my-color)` |
| `bg-[rgb(0,_0,_0)]`    | `bg-[rgb(0,0,0)]` |

Another big important reason for this change is that these classes on
their own
would have been migrated _if_ another migration was relevant for this
candidate.
This means that there are were some inconsistencies. E.g.:

| Before | After | Reason |
| ----------------------- | ---------------------- |
------------------------------------ |
| `!bg-[var(--my-color)]` | `bg-(--my-color)!` | Because the `!` is in
the wrong spot |
| `bg-[var(--my-color)]` | `bg-[var(--my-color)]` | Because no
migrations rand |

As you can see, the way the `--my-color` variable is used, is different.
This
changes will make sure it will now always be consistent:
| Before | After |
| ----------------------- | ---------------------- |
| `!bg-[var(--my-color)]` | `bg-(--my-color)!` |
| `bg-[var(--my-color)]` | `bg-(--my-color)` |

Yay!

Of course, if you don't want these more cosmetic changes, you can always
ignore the upgrade and revert these changes and only commit the changes
you want.

# Test plan

- All existing tests still pass.
- But I had to delete 1 test (we tested that Tailwind CSS v3 was
required).
- And had to mock the `version.isMajor` call to ensure we run the
individual migration tests correctly.
- Added new tests to test:
  1. Migrating Tailwind CSS v4 projects works
  1. Idempotency of the upgrade tool

[ci-all]
2025-04-22 11:10:46 -04:00
Philipp Spiess
83ce4c0a30
Add experimental @tailwindcss/oxide-wasm32-wasi (#17558)
Closes #17448
Closes #13133

This PR adds an a new Oxide target for `wasm32-wasip1-threads`:
`@tailwindcss/oxide-wasm32-wasi`. The goal of this is to enable more
environments to run Oxide, including (but not limited to) StackBlitz.

We're making use of `napi-rs`'s upcoming v3 features to simplify the
setup here, meaning `napi-rs` will configure the WASM target and create
an npm package that works across Node and browser environments.

## MacOS AArch64 issues

While setting up an integration test for the new WASM target, I ran into
an issue where FS reads where not terminating on macOS. After some
research I found this to be a limitation of the Node.js container
interface right now, see: https://github.com/nodejs/node/issues/47193

### Windows issues

We also found that the Node.js wasi container does not properly support
Windows: https://github.com/nodejs/uvwasi/issues/11

For now we, it's probably best for MacOS AArch64 users and Windows users
to use the native modules instead.

## Test plan

The `@tailwindcss/oxide-wasm32-wasi` npm package can be built locally
via `pnpm build` and then run with the Oxide API. A usage example can be
taken from the newly added integration test.

Furthermore this was tested to work as a polyfill on StackBlitz:
https://stackblitz.com/edit/vitejs-vite-uks3gt5p

[ci-all]

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-11 17:19:55 +02:00
Justin Wong
c0af1e2129
Fix fontSize array upgrade (#17630)
Fixes #17622.

Adds a specific handling case in `themeableValues()` in
`packages/tailwindcss/src/compat/apply-config-to-theme.ts`. It seems
like this has unique handling in v3 for an array value, whereby the
second value is treated as a `line-height`.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-11 13:23:54 +02:00
Philipp Spiess
3f434a6f00
Vite: Don't register the current CSS file as a dependency on itself (#17533)
Closes #17512

One of the changes of the Oxide API in 4.1 is that it now emits the
input CSS file itself as a dependency. This was fine in most of our
testing but it turns out that certain integrations (in this case a Qwik
project) don't like this and will silently crash with no CSS file being
added anymore.

This PR fixes this by making sure we don't add the input file as a
dependency on itself and also adds an integration test to ensure this
won't regress again.

## Test plan

- Tested with the repro provided in #17512
- Added a minimal integration test based on that reproduction that I
also validated will _fail_, if the fix is reverted.
2025-04-03 18:50:16 +02:00
Philipp Spiess
4200a1ecc4
Fix slow incremental builds (especially on Windows) (#17511)
This PR fixes slow rebuilds on Windows where rebuilds go from ~2s to
~20ms.

Fixes: #16911
Fixes: #17522

## Test plan

1. Tested it on a reproduction with the following results:

Before:


https://github.com/user-attachments/assets/10c5e9e0-3c41-4e1d-95f6-ee8d856577ef

After:


https://github.com/user-attachments/assets/2c7597e9-3fff-4922-a2da-a8d06eab9047

Zooming in on the times, it looks like this:
<img width="674" alt="image"
src="https://github.com/user-attachments/assets/85eee69c-bbf6-4c28-8ce3-6dcdad74be9c"
/>

But with these changes:
<img width="719" alt="image"
src="https://github.com/user-attachments/assets/d89cefda-0711-4f84-bfaf-2bea11977bf7"
/>

We also tested this on Windows with the following results:
Before:
<img width="961" alt="image"
src="https://github.com/user-attachments/assets/3a42f822-f103-4598-9a91-e659ae09800c"
/>

After:
<img width="956" alt="image"
src="https://github.com/user-attachments/assets/05b6b6bc-d107-40d1-a207-3638aba3fc3a"
/>


[ci-all]

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-03 17:13:15 +02:00
Philipp Spiess
60b0da90ce
Polyfill: Fall back to first color value when color-mix(…) contains unresolvable var(…) (#17513)
This PR further improves the `color-mix(…)` polyfill to create a
reasonable fallback if dynamic values that can not statically be
resolved are used. This refers to either the use of `currentcolor` or
any variables that are not static theme variables.

Here are two examples that now generate a reasonable fallback instead of
not showing any color at all:

```css
.text-\\(--my-color\\)\\/\\(--my-opacity\\) {
  color: var(--my-color);
}
@supports (color: color-mix(in lab, red, red)) {
  .text-\\(--my-color\\)\\/\\(--my-opacity\\) {
    color: color-mix(in oklab, var(--my-color) var(--my-opacity), transparent);
  }
}
```

```css
.text-current\\/50 {
  color: currentColor;
}

@supports (color: color-mix(in lab, red, red)) {
  .text-current\\/50 {
    color: color-mix(in oklab, currentColor 50%, transparent);
  }
}
```

## Test plan

- Made sure the test diffs are looking reasonable
- Tested this on a production site with `<p className="text-shadow-lg/50
[--my-color:red] text-shadow-(color:--my-color)">shadow test</p>`
- Browsers that do not support `color-mix(…)` will properly show a red
shadow now albeit with 100% opacity: iOS 15.5 and Chrome 110
- Browsers that I have tested to make sure it still works there with
opacity: Firefox 127, Firefox 128, Latest Chrome, Safari, Firefox
- Browsers that do show a black shadow because of `var(…)var(…)` being
chained with no space by lightningcss: Chrome 111
2025-04-03 15:12:34 +00:00
Robin Malfait
81a676f129
Fix race condition in Next.js with --turbopack (#17514)
This PR fixes an issue where if you use Next.js with `--turbopack` a
race condition happens because the `@tailwindcss/postcss` plugin is
called twice in rapid succession.

The first call sees an update and does a partial update with the new
classes. Next some internal `mtimes` are updated. The second call
therefore doesn't see any changes anymore because the `mtimes` are the
same, therefore it's serving its stale data.

Fixes: #17508

## Test plan

- Tested with the repro provided in #17508
- Added a new unit test that calls into the PostCSS plugin directly for
the same change from the same JavaScript run-loop.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-03 17:07:38 +02:00
Teddy Bradford
3e41e9ffe6
Replace currentColor with currentcolor (lowercase) (#17510)
Replaces `currentColor` with `currentcolor` (lowercase) to match what's
defined in [CSS Color Module Level
4](https://www.w3.org/TR/css-color-4/#currentcolor-color) and
[MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword)
(see: https://github.com/mdn/content/pull/16592).

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-03 16:09:12 +02:00
Philipp Spiess
4484192ca3
Use @layer properties for @property polyfills (#17506)
This PR changes how polyfills for `@property` are inserted. The main
motivation is to remove the need to rely on the correct placement of
`@layer base;`—Something that's not really required right not in
Tailwind CSS v4 and we'd like to keep it this way.

The idea is that the polyfills are inserted for you automatically. To
ensure they always take precedence, we insert an empty `@layer
properties;` at the top of the CSS file so that later, when we emit all
`@property` rules and their fallback, we can use this new named layer to
ensure the rules have a higher order.

Unfortunately, just putting `@layer properties;` at the beginning of a
file would not work as `lightningcss` incorrectly hoists all content
into the first occurrence of a layer name meaning these rules might be
inserted _before_ eventual external imports:


![image](https://github.com/user-attachments/assets/c5a1694d-1549-47ed-ad0f-266807be4730)

To work around this, we have to insert that layer name after any
eventual remaining external `@imports` for now.

## Test plan

- Updated snapshot tests
- Deployed a new version of the website with the patch applied to ensure
it works across browsers:
https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/.
Tested on: Safari on iOS 15.5, Safari on iOS 16.0, Firefox 127, Firefox
128, Chrome 110, Chrome latest, Safari latest, Firefox latest
2025-04-02 18:16:28 +02:00
Robin Malfait
3c937ecee7
Inject polyfills after @import and body-less @layer (#17493)
This PR fixes an issue where polyfills were injected at the top, but
they should be after `@import` and body-less `@layer` rules.

This is necessary in case you are using Google fonts like this for
example:
```css
@import url('https://fonts.google.com');
@import "tailwindcss";
```

While the `@import url(…);` sits above `@import "tailwindcss";` in the
final generated CSS we injected the polyfills at the very beginning.

This PR will inject the polyfills after the first AST Node that is not:
1. A comment
2. An external import — `@import url(…)`
3. A body-less layer — `@layer foo, bar, baz;`

The snapshots look a little confusing, but that's because Lightning CSS
is optimizing the output and moving things around a bit:

<img width="1482" alt="image"
src="https://github.com/user-attachments/assets/a0552c8b-93df-4e1d-ad90-8b8abf9492b1"
/>

[Lightning CSS
Playground](https://lightningcss.dev/playground/index.html#%7B%22minify%22%3Afalse%2C%22customMedia%22%3Atrue%2C%22cssModules%22%3Afalse%2C%22analyzeDependencies%22%3Afalse%2C%22targets%22%3A%7B%22chrome%22%3A6225920%7D%2C%22include%22%3A0%2C%22exclude%22%3A0%2C%22source%22%3A%22%40layer%20theme%2C%20base%2C%20components%2C%20utilities%3B%5Cn%5Cn%40supports%20(((-webkit-hyphens%3A%20none))%20and%20(not%20(margin-trim%3A%20inline)))%20or%20((-moz-orient%3A%20inline)%20and%20(not%20(color%3A%20rgb(from%20red%20r%20g%20b))))%20%7B%5Cn%20%20%40layer%20base%20%7B%5Cn%20%20%20%20*%2C%20%3Abefore%2C%20%3Aafter%2C%20%3A%3Abackdrop%20%7B%5Cn%20%20%20%20%20%20--tw-font-weight%3A%20initial%3B%5Cn%20%20%20%20%7D%5Cn%20%20%7D%5Cn%7D%5Cn%5Cn%40layer%20theme%20%7B%5Cn%20%20%3Aroot%2C%20%3Ahost%20%7B%5Cn%20%20%20%20--font-sans%3A%20ui-sans-serif%2C%20system-ui%2C%20sans-serif%2C%20%5C%22Apple%20Color%20Emoji%5C%22%2C%20%5C%22Segoe%20UI%20Emoji%5C%22%2C%20%5C%22Segoe%20UI%20Symbol%5C%22%2C%20%5C%22Noto%20Color%20Emoji%5C%22%3B%5Cn%20%20%20%20--font-mono%3A%20ui-monospace%2C%20SFMono-Regular%2C%20Menlo%2C%20Monaco%2C%20Consolas%2C%20%5C%22Liberation%20Mono%5C%22%2C%20%5C%22Courier%20New%5C%22%2C%20monospace%3B%5Cn%20%20%20%5Cn%20%20%7D%5Cn%7D%5Cn%5Cn%40layer%20base%20%7B%5Cn%20%20*%2C%20%3Aafter%2C%20%3Abefore%2C%20%3A%3Abackdrop%20%7B%5Cn%20%20%20%20box-sizing%3A%20border-box%3B%5Cn%20%20%20%20border%3A%200%20solid%3B%5Cn%20%20%20%20margin%3A%200%3B%5Cn%20%20%20%20padding%3A%200%3B%5Cn%20%20%7D%5Cn%7D%5Cn%5Cn%40layer%20utilities%20%7B%5Cn%20%20.text-2xl%20%7B%5Cn%20%20%20%20font-size%3A%20var(--text-2xl)%3B%5Cn%20%20%20%20line-height%3A%20var(--tw-leading%2C%20var(--text-2xl--line-height))%3B%5Cn%20%20%7D%5Cn%7D%5Cn%5Cn%40property%20--tw-font-weight%20%7B%5Cn%20%20syntax%3A%20%5C%22*%5C%22%3B%5Cn%20%20inherits%3A%20false%5Cn%7D%22%2C%22visitorEnabled%22%3Afalse%2C%22visitor%22%3A%22%7B%5Cn%20%20Color(color)%20%7B%5Cn%20%20%20%20if%20(color.type%20%3D%3D%3D%20'rgb')%20%7B%5Cn%20%20%20%20%20%20color.g%20%3D%200%3B%5Cn%20%20%20%20%20%20return%20color%3B%5Cn%20%20%20%20%7D%5Cn%20%20%7D%5Cn%7D%22%2C%22unusedSymbols%22%3A%5B%5D%2C%22version%22%3A%22local%22%7D)

Fixes: #17494
2025-04-02 11:05:35 +02:00
Philipp Spiess
156afc6d67
Improve compatibility with Safari 15 (#17435)
This PR improves the compatibility with Tailwind CSS v4 with unsupported
browsers with the goal to greatly improve compatibility with Safari 15.

To make this work, this PR makes the following changes to all code

- Change `oklab(…)` default theme values to use a percentage in the
first place (so instead of `--color-red-500: oklch(0.637 0.237 25.331);`
we now define it as `--color-red-500: oklch(63.7% 0.237 25.331);` since
this syntax has much broader support on Safari).
- Polyfill `@property` with a `@supports` query targeting older versions
of Safari and Firefox *
- Create fallbacks for the `color-mix(…)` function that use _inlined
color values from your theme_ so that they can be computed a compile
time by `lightningcss`. These fallbacks will convert to srgb to increase
compatibility.
- Create fallbacks for the _relative color_ feature used in the new
shadow utilities and using `color-mix(…)` in case _relative color_ is
applied on `currentcolor` (due to limited browser support)
- Create fallbacks for gradient interpolation methods (e.g. to support
`bg-linear-to-r/oklab`)
- Polyfill `@media` queries range syntax.

## A simplified example

Given this example CSS input:

```css
@import 'tailwindcss';
@source inline('from-cyan-500/50 bg-linear-45');
```

Here's the updated output CSS including the newly added polyfills and
updated `oklab` values:

```css
.bg-linear-45 {
  --tw-gradient-position: 45deg;
  background-image: linear-gradient(var(--tw-gradient-stops));
}

@supports (background-image: linear-gradient(in lab, red, red)) {
  .bg-linear-45 {
    --tw-gradient-position: 45deg in oklab;
  }
}

.from-cyan-500\\/50 {
  --tw-gradient-from: oklab(71.5% -.11682 -.08247 / .5);
  --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}

@supports (color: color-mix(in lab, red, red)) {
  .from-cyan-500\\/50 {
    --tw-gradient-from: color-mix(in oklab, var(--color-cyan-500) 50%, transparent);
  }
}

:root, :host {
  --color-cyan-500: oklch(71.5% .143 215.221);
}

@supports (((-webkit-hyphens: none)) and (not (margin-trim: 1lh))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
  @layer base {
    *, :before, :after, ::backdrop {
      --tw-gradient-position: initial;
      --tw-gradient-from: #0000;
      --tw-gradient-via: #0000;
      --tw-gradient-to: #0000;
      --tw-gradient-stops: initial;
      --tw-gradient-via-stops: initial;
      --tw-gradient-from-position: 0%;
      --tw-gradient-via-position: 50%;
      --tw-gradient-to-position: 100%;
    }
  }
}

@property --tw-gradient-position {
  syntax: "*";
  inherits: false
}

@property --tw-gradient-from {
  syntax: "<color>";
  inherits: false;
  initial-value: #0000;
}

@property --tw-gradient-via {
  syntax: "<color>";
  inherits: false;
  initial-value: #0000;
}

@property --tw-gradient-to {
  syntax: "<color>";
  inherits: false;
  initial-value: #0000;
}

@property --tw-gradient-stops {
  syntax: "*";
  inherits: false
}

@property --tw-gradient-via-stops {
  syntax: "*";
  inherits: false
}

@property --tw-gradient-from-position {
  syntax: "<length-percentage>";
  inherits: false;
  initial-value: 0%;
}

@property --tw-gradient-via-position {
  syntax: "<length-percentage>";
  inherits: false;
  initial-value: 50%;
}

@property --tw-gradient-to-position {
  syntax: "<length-percentage>";
  inherits: false;
  initial-value: 100%;
}
```

## \* A note on `@property` polyfills and CSS modules

On Next.js, CSS module files are required to be _pure_, meaning that all
selectors must either be scoped to a class or an ID. Fortunatnyl for us,
this does not apply to `@property` rules which we've been using before
to initialize CSS variables.

However, since we're now bringing back the `@property` polyfills, that
would cause unexpected rules to be exported from the CSS file as this:

```css
@reference "tailwindcss";

.skew {
  @apply skew-7;
}
```

Would turn to the following file:

```css
.skew {
  /* … */
}
@supports (/*…*/) {
  @layer base {
    *, :before, :after, ::backdrop {
      --tw-gradient-position: initial;
    }
  }
}
@property /* … */ 
```

Notice that this adds a `*` selector which is not considered pure.

Unfortunately there is no way for us to silence this warning or work
around it, as the dependency causing this errors
([`postcss-modules-local-by-default`](https://github.com/css-modules/postcss-modules-local-by-default))
is bundled into Next.js. To work around crashes, these polyfills will
not apply to CSS modules processed by the PostCSS extension for now.

## Testing on tailwindcss.com

To see the changes in effect, take a look at this screencast that
compares tailwindcss.com on iOS 15.5 with a version that has the patches
of this PR applied:

https://github.com/user-attachments/assets/1279d6f5-3c63-4f30-839c-198a789f4292

## Test plan

- Tested on tailwindcss.com via a preview build:
https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/
- Updated tests
- Ensure we also test on Chrome 111, Safari 16.4, Firefox 128 to
make sure we have no regressions. Also tested on Safari 16.4, 15.5, 18.0
2025-04-01 13:33:22 +02:00
Robin Malfait
d54e23d5a1
Ensure webpack executable is found in CI (#17470)
This PR will fix CI on the main branch where `webpack` could not be
found on macOS (but it worked on Linux).

Going to run [ci-all] to verify the changes.
2025-03-31 19:21:51 +02:00
Robin Malfait
53801091a0
Watch CSS module files for changes (#17467)
This PR is a follow-up PR for:
https://github.com/tailwindlabs/tailwindcss/pull/17433

In the other PR we allow scanning CSS files for extracting usages of CSS
variables. This is important for `.module.css` files that reference
these variables but aren't in the same big AST of the main CSS file.

This PR also makes sure to watch for changes in those registered CSS
files and re-extract the variables when they change.

This PR took a bit longer than expected because I was trying to make
sure that writing to `./dist/out.css` works without infinite-looping
(e.g.: we had issues with this in Tailwind CSS v3 with webpack).

But I couldn't reproduce the issue at all. I did had some code that
tried to detect if the CSS file contained license headers and skip in
(because then it's very likely an output CSS file) but even without it
the tests were fine.

I setup integration tests with `@tailwindcss/cli` itself, and with tools
that use webpack. Added a test for Next.js, and a dedicated webpack test
as well.

Even without tests, locally, I couldn't reproduce an infinite loop due
to changes in an output CSS file...

Eventually dropped the code that tries to detect output CSS files.

One thing to keep in mind is that if you change any of your "main" CSS
files, then we will trigger a full rebuild anyway, so this change is
only required for unrelated CSS files (like CSS module files) that use
CSS variables.

## Test plan

1. Added integration tests for the CLI and Next.js
2. Added new dedicated test for webpack
2025-03-31 18:44:06 +02:00
Robin Malfait
1ef97759e3
Add @source not support (#17255)
This PR adds a new source detection feature: `@source not "…"`. It can
be used to exclude files specifically from your source configuration
without having to think about creating a rule that matches all but the
requested file:

```css
@import "tailwindcss";
@source not "../src/my-tailwind-js-plugin.js";
```

While working on this feature, we noticed that there are multiple places
with different heuristics we used to scan the file system. These are:

- Auto source detection (so the default configuration or an `@source
"./my-dir"`)
- Custom sources ( e.g. `@source "./**/*.bin"` — these contain file
extensions)
- The code to detect updates on the file system

Because of the different heuristics, we were able to construct failing
cases (e.g. when you create a new file into `my-dir` that would be
thrown out by auto-source detection, it'd would actually be scanned). We
were also leaving a lot of performance on the table as the file system
is traversed multiple times for certain problems.

To resolve these issues, we're now unifying all of these systems into
one `ignore` crate walker setup. We also implemented features like
auto-source-detection and the `not` flag as additional _gitignore_ rules
only, avoid the need for a lot of custom code needed to make decisions.

High level, this is what happens after the now:

- We collect all non-negative `@source` rules into a list of _roots_
(that is the source directory for this rule) and optional _globs_ (that
is the actual rules for files in this file). For custom sources (i.e
with a custom `glob`), we add an allowlist rule to the gitignore setup,
so that we can be sure these files are always included.
- For every negative `@source` rule, we create respective ignore rules.
- Furthermore we have a custom filter that ensures files are only read
if they have been changed since the last time they were read.

So, consider the following setup:

```css
/* packages/web/src/index.css */
@import "tailwindcss";
@source "../../lib/ui/**/*.bin";
@source not "../../lib/ui/expensive.bin";
```

This creates a git ignore file that (simplified) looks like this:

```gitignore
# Auto-source rules
*.{exe,node,bin,…}
*.{css,scss,sass,…}
{node_modules,git}/

# Custom sources can overwrite auto-source rules
!lib/ui/**/*.bin

# Negative rules
lib/ui/expensive.bin
```

We then use this information _on top of your existing `.gitignore`
setup_ to resolve files (i.e so if your `.gitignore` contains rules e.g.
`dist/` this line is going to be added _before_ any of the rules lined
out in the example above. This allows negative rules to allow-list your
`.gitignore` rules.

To implement this, we're rely on the `ignore` crate but we had to make
various changes, very specific, to it so we decided to fork the crate.
All changes are prefixed with a `// CHANGED:` block but here are the
most-important ones:

- We added a way to add custom ignore rules that _extend_ (rather than
overwrite) your existing `.gitignore` rules
- We updated the order in which files are resolved and made it so that
more-specific files can allow-list more generic ignore rules.
- We resolved various issues related to adding more than one base path
to the traversal and ensured it works consistent for Linux, macOS, and
Windows.

## Behavioral changes

1. Any custom glob defined via `@source` now wins over your `.gitignore`
file and the auto-content rules.
   - Resolves #16920
3. The `node_modules` and `.git` folders as well as the `.gitignore`
file are now ignored by default (but can be overridden by an explicit
`@source` rule).
   - Resolves #17318
   - Resolves #15882
4. Source paths into ignored-by-default folders (like `node_modules`)
now also win over your `.gitignore` configuration and auto-content
rules.
    -  Resolves #16669
5. Introduced `@source not "…"` to negate any previous rules.
   - Resolves #17058
6. Negative `content` rules in your legacy JavaScript configuration
(e.g. `content: ['!./src']`) now work with v4.
   - Resolves #15943 
7. The order of `@source` definitions matter now, because you can
technically include or negate previous rules. This is similar to your
`.gitingore` file.
9. Rebuilds in watch mode now take the `@source` configuration into
account
   - Resolves #15684

## Combining with other features

Note that the `not` flag is also already compatible with [`@source
inline(…)`](https://github.com/tailwindlabs/tailwindcss/pull/17147)
added in an earlier commit:

```css
@import "tailwindcss";
@source not inline("container");
```

## Test plan

- We added a bunch of oxide unit tests to ensure that the right files
are scanned
- We updated the existing integration tests with new `@source not "…"`
specific examples and updated the existing tests to match the subtle
behavior changes
- We also added a new special tag `[ci-all]` that, when added to the
description of a PR, causes the PR to run unit and integration tests on
all operating systems.

[ci-all]

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-03-25 15:54:41 +01:00
Rudi Visser
baa016a1c9
Add Input & Output check to CLI (#17311)
Throw an error if the input and output file for the CLI are identical.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-03-24 12:00:36 +01:00
Philipp Spiess
fac8f7df62
Vite: Emit build dependencies on partial rebuilds (#17347)
Closes #17339

This PR fixes an issue that caused changes to `@import`-ed CSS files to
no longer rebuild the stylesheet after a change was made to a template
file.

The change in the template file causes a fast-path in the Vite plugin
now after changes in 4.0.8: _partial rebuilds_. For that branch we do
not need to re-evaluate your input CSS since we know only the candidate
list changed. However, we still need to emit all build dependencies as
via `addWatchFile(…)`, otherwise Vite will not correctly register
updates for these dependencies anymore.

## Test plan

- Updated the kitchen-sink Vite update tests to ensure that an
`@import`-ed CSS file can be updated even after a partial rebuild.
- Ensure this works in our Vite playground
2025-03-24 11:54:55 +01:00
Philipp Spiess
3f313b4eb2
Ensure that the CSS file rebuilds if a new CSS variable is used from templates (#17301)
Fixes #17288

This PR fixes an issue where changes in template files that _only mark a
new CSS variable as used_ was not causing the CSS file to rebuilds.

This also fixes a flaky integration test that was caused by a missing
`await` line on the final assertion (causing it to sometimes run the
assertion in time while the test runner was still running).

## Test plan

Turns out we already had an integration test for this but it wasn't
correctly running due to the missing await.
2025-03-20 12:52:02 +01:00
Devon Govett
cec7f0557b
Fix segmentation fault when loading @tailwindcss/oxide in a Worker thread (#17276)
When Tailwind is loaded in a Node Worker thread, it currently causes a
segmentation fault on Linux when the thread exits. This is due to a
longstanding issue in Rust that affects all native modules:
https://github.com/rust-lang/rust/issues/91979. I reported this years
ago but unfortunately it is still not fixed, and seems to have gotten
worse in Rust 1.83.0 and later. Looks like Tailwind recently updated
Rust versions and this issue started appearing when run in tools like
Parcel that use worker threads.

The workaround is to prevent the native module from ever being unloaded.
One way to do that is to always load the native module in the main
thread in addition to workers, but this is hard to enforce.
@Brooooooklyn found another method, which is to use a linker option for
this. I tested this on an Ubuntu system and verified it fixed the issue.
You can test with the following script.

```js
// test.js
const {Worker} = require('worker_threads');
new Worker('./worker.js');

// worker.js
require('@tailwindcss/oxide');
```

Without this change, a segmentation fault will occur.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-03-18 16:28:20 -04:00
Philipp Spiess
d6d913ec39
Don't use color-mix(…) on currentColor (#17247)
Closes #17194

This PR works around a crash when rendering opacity on `currentColor`
(as used by the placeholder styles in preflight) on Safari 16.4 and
Safari 16.5. Unfortunately it seems that the [`color-mix(…)` function is
not compatible with `currentColor` for these versions of
Safari](https://stackoverflow.com/questions/76436497/the-color-mix-property-involving-currentcolor-causes-safari-to-crash).
We tried a few different ways to work around this without success:

- Using an `@supports` media query to target these Safari versions and
overwriting the placeholder still makes these browsers crash.
- Changing the way we apply opacity to `currentColor` in core doesn't
seem to work for non-placeholder values:
https://github.com/tailwindlabs/tailwindcss/issues/17194#issuecomment-2728949181
However, a wrong opacity is still better than a complete browser crash.

The work-around of using the `oklab(…)` function does seem to work for
`::placeholder` styles in preflight though according to our testing so
this PR applies this change to preflight.

## Test plan

- See https://play.tailwindcss.com/WSsSTLHu8h?file=css 
- Tested on Chrome/Safari 16.4/Safari 18.3/Firefox

<img width="564" alt="Screenshot 2025-03-17 at 11 32 47"
src="https://github.com/user-attachments/assets/cfd0db71-f39a-4bc0-bade-cea70afe50ae"
/>
2025-03-17 13:23:02 +01:00
Philipp Spiess
225f3233b6
Enable URL rewriting for PostCSS (#16965)
Fixes #16636 

This PR enables URL rebasing for PostCSS. Furthermore it fixes an issue
where transitive imports rebased against the importer CSS file instead
of the input CSS file. While fixing this we noticed that this is also
broken in Vite right now and that our integration test swallowed that
when testing because it did not import any Tailwind CSS code and thus
was not considered a Tailwind file.

## Test plan

- Added regression integration tests
- Also validated it against the repro of
https://github.com/tailwindlabs/tailwindcss/issues/16962:
  
<img width="1149" alt="Screenshot 2025-03-05 at 16 41 01"
src="https://github.com/user-attachments/assets/85396659-d3d0-48c0-b1c7-6125ff8e73ac"
/>

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-03-07 12:18:10 +01:00
Philipp Spiess
294952f170
Handle BOM (#16800)
Resolves #15662 
Resolves #15467

## Test plan

Added integration tests for upgrade tooling (which already worked
surprisingly?) and CLI.
2025-02-25 16:07:16 +01:00
Jordan Pittman
662c6862ac
Make JS APIs available to plugins and configs in the Standalone CLI (#15934)
This PR ensures we bundle the relevant JS APIs in the Standalone CLI
like `tailwindcss`, `tailwindcss/plugin`, `tailwindcss/colors`, etc…

Before, when loading plugins or configs, imports for those resources
would fail.

Fixes #15235

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-02-25 13:21:46 +01:00
Philipp Spiess
b38948337d
Make @reference emit variable fallbacks instead of CSS variable declarations (#16774)
Fixes #16725

When using `@reference "tailwindcss";` inside a separate CSS root (e.g.
Svelte `<style>` components, CSS modules, etc.), we have no guarantee
that the CSS variables will be defined in the main stylesheet (or if
there even is one). To work around potential issues with this we decided
in #16676 that we would emit all used CSS variables from the `@theme`
inside the `@reference` block.

However, this is not only a bit surprising but also unexpected in CSS
modules and Next.js that **requires CSS module files to only create
scope-able declarations**. To fix this issue, we decided to not emit CSS
variables but instead ensure all `var(…)` calls we create for theme
values in reference mode will simply have their fallback value added.

This ensures styles work as-expected even if the root Tailwind file does
not pick up the variable as being used or _if you don't add a root at
all_. Furthermore we do not duplicate any variable declarations across
your stylesheets and you still have the ability to change variables at
runtime.

## Test plan

- Updated snapshots everywhere (see diff)
- New Next.js CSS modules integration test
2025-02-25 11:36:43 +01:00
Philipp Spiess
59e003e6d1
Vite: Don't crash with virtual module dependencies (#16780)
Fixes #16732

If we can not get the mtime from a file, chances are that the resource
is a virtual module. This is perfectly legit and we can fall back to
what we did before the changes in `4.0.8` (which is to rebuild the root
every time a change contains a dependency like that).

## Test plan

Added a test to mimic the setup from the repor in #16732. Also ensured
the repro now passes:

<img width="1278" alt="Screenshot 2025-02-24 at 17 29 38"
src="https://github.com/user-attachments/assets/d111273d-579f-44c2-82f5-aa32d6a1879a"
/>

Note that importing virtual modules directly in CSS does not work as the
resolver we use does not resolve against the Vite runtime it seems. This
is unrelated to the regression added in `4.0.8` though and something to
look into in the future.
2025-02-25 11:29:58 +01:00
Robin Malfait
113142a0e4
Use amount of properties when sorting (#16715)
Right now we sort the nodes based on a pre-defined sort order based on
the properties that are being used. The property sort order is defined
in a list we maintain.

We also have to make sure that the property count is taken into account
such that if all the "sorts" are the same, that we fallback to the
property count. Most amount of properties should be first such that we
can override it with more specific utilities that have fewer properties.

However, if a property doesn't exist, then it wouldn't be included in a
list of properties therefore the total count was off.

This PR fixes that by counting all the used properties. If a property
already exists it is counted twice. E.g.:
```css
.foo {
  color: red;

  &:hover {
    color: blue;
  }
}
```

In this case, we have 2 properties, not 1 even though it's the same
`color` property.

## Test plan:

1. Updated the tests that are now sorted correctly
2. Added an integration test to make sure that `prose-invert` is defined
after the `prose-stone` classes when using the `@tailwindcss/typography`
plugin where this problem originated from.

Note how in this play (https://play.tailwindcss.com/wt3LYDaljN) the
`prose-invert` comes _before_ the `prose-stone` which means that you
can't apply the `prose-invert` classes to invert `prose-stone`.
2025-02-21 15:02:07 +01:00
Philipp Spiess
7bece4de7c
Re-enable: Only expose used CSS variables (#16676)
This PR re-enables the changes necessary to remove unused theme
variables and keyframes form your CSS.

This change was initially landed as #16211 and then later reverted in
#16403 because we found some unexpected interactions with using `@apply`
and CSS variables in multi-root setups like CSS modules or Vue inline
`<style>` blocks that were no longer seeing their required variables
defined.

This issue is fixed by now ensuring that theme variables that are
defined within an `@reference "…"` boundary will still be emitted in the
generated CSS when used (as this would otherwise not generate a valid
stylesheet).

So given the following input CSS:

```css
@reference "tailwindcss";
.text-red {
  @apply text-red-500;
}
```

We will now compile this to:

```css
@layer theme {
  :root, :host {
    --text-red-500: oklch(0.637 0.237 25.331);
  }
}
.text-red {
  color: var(--text-red-500);
}
```

This PR also improves the initial implementation to not mark theme
variables as used if they are only used to define other theme variables.
For example:

```css
@theme {
  --font-sans:
    ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
    'Noto Color Emoji';
  --font-mono:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
    monospace;

  --default-font-family: var(--font-sans);
  --default-mono-font-family: var(--font-mono);
}

.default-font-family {
  font-family: var(--default-font-family);
}
```

This would be reduced to the following now as `--font-mono` is only used
to define another variable and never used outside the theme block:

```css
:root, :host {
  --font-sans:
    ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
    'Noto Color Emoji';
  --default-font-family: var(--font-sans);
}

.default-font-family {
  font-family: var(--default-font-family);
}
```

## Test plan

- See updated unit and integration tests
- Validated it works end-to-end by using a SvelteKit example
2025-02-21 10:45:22 +01:00
Philipp Spiess
1d56525fa0
Fix integration tests for Windows (#16693)
The Nuxt preview server always starts on port 3000 even if that port is
taken. With the added tests in #16631 there is now a higher chance these
ports are already taken since e.g. react router prefers to start at port
3000 and so do other servers.
This PR changes this so that we assign a random port inside the test
instead.

## Test plan

- Ensure Windows CI is green again
2025-02-20 17:50:50 +01:00
Philipp Spiess
88b762b539
Vite: Remove module-graph scanner (#16631)
Alternative to #16425

Fixes #16585
Fixes #16389
Fixes #16252
Fixes #15794
Fixes #16646
Fixes #16358

This PR changes the Vite plugin to use the file-system to discover
potential class names instead of relying on the module-graph. This comes
after a lot of testing and various issue reports where builds that span
different Vite instances were missing class names.

Because we now scan for candidates using the file-system, we can also
remove a lot of the bookkeeping necessary to make production builds and
development builds work as we no longer have to change the resulting
stylesheet based on the `transform` callbacks of other files that might
happen later.

This change comes at a small performance penalty that is noticeable
especially on very large projects with many files to scan. However, we
offset that change by fixing an issue that I found in the current Vite
integration that did a needless rebuild of the whole Tailwind root
whenever any source file changed. Because of how impactful this change
is, I expect many normal to medium sized projects to actually see a
performance improvement after these changes. Furthermore we do plan to
continue to use the module-graph to further improve the performance in
dev mode.

## Test plan

- Added new integration tests with cases found across the issues above.
- Manual testing by adding a local version of the Vite plugin to repos
from the issue list above and the [tailwindcss
playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
Philipp Spiess
ec1d7d4b85
Upgrade: Use latest tag for packages (#16620) 2025-02-18 11:57:51 +01:00
Philipp Spiess
08972f294f
Scan Next.js dynamic route segments with manual @source rules (#16457)
Part of #16287

## Test plan

Added unit and integration tests

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-02-18 11:51:58 +01:00
Freeman
1c905f2adb
minor: better type safety and best practice to escape function (#16579) 2025-02-17 13:49:17 +01:00
Philipp Spiess
a1e083afc5
Upgrade: Handle darkMode value with block syntax (#16507)
Closes #16171

This PR handles `darkMode` variant configs containing braces (so
creating sub-rules) the same way we handle it in the interop layer.
Since the interop layer runs inside the `addVariant` API that we do not
run here, I instead oped to copy the one liner.

## Test plan

Updated one of the migration tests to include a rule that wasn't working
before. Ensured the new output works via
https://play.tailwindcss.com/nR99uhKtv3
2025-02-14 16:48:52 +01:00
Philipp Spiess
9bbe2e3d08
Revert: Only expose used CSS variables (#16403)
This reverts #16211

We found some unexpected interactions with using `@apply` and CSS
variables in multi-root setups like CSS modules or Vue inline `<style>`
blocks that were broken due to that change. We plan to re-enable this
soon and include a proper fix for those scenarios.

## Test plan

- Updated snapshots
- Tested using the CLI in a new project:
<img width="1523" alt="Screenshot 2025-02-10 at 13 08 42"
src="https://github.com/user-attachments/assets/defe0858-adb3-4d61-9d2c-87166558fd68"
/>

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-02-10 13:26:13 +01:00
Robin Malfait
a659159bf1
Bump and pin prettier (#16382)
This PR bumps the Prettier dependencies, and also pins the version.

Noticed that a PR with a single empty commit started failing at the time
of writing this
(https://github.com/tailwindlabs/tailwindcss/pull/16306). This is
because prettier released a new minor version which results in slightly
different output.

Let's bump prettier and handle the differences, but also pin the version
to avoid this in the future.
2025-02-10 10:54:45 +01:00
Robin Malfait
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>
2025-02-07 17:12:47 +00:00
Philipp Spiess
e1a85ac260
Vite: Skip parsing stylesheets with the ?commonjs-proxy flag (#16238)
Fixes #16233

Vite has a number of special parameters that can be appended to `.css`
files that make it actually load as a JavaScript module. One such
parameter that we haven't handled before is the `?commonjs-proxy` flag.
When importing e.g. `plotly.js/lib/core`, the dependency tree would
eventually load a file called `*.css?commonjs-proxy`. We previously
scanned this for candidates even though it was not, in-fact, a
stylesheet.

This PR fixes this by adding the `?commonjs-proxy` to the ignore list. I
have also updated `SPECIAL_QUERY_RE` to more closely match the Vite
implementation. It does seem like this was the only condition we were
missing, though:
2b2299cbac/packages/vite/src/node/plugins/css.ts (L511-L517)

## Test plan

Add and import `plotly.js/lib/core` into a Vite app. I also added an
integration test to do that.

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-02-04 12:45:23 +01:00
Robin Malfait
7f1d0970c3
Do not emit empty rules/at-rules (#16121)
This PR is an optimization where it will not emit empty rules and
at-rules. I noticed this while working on
https://github.com/tailwindlabs/tailwindcss/pull/16120 where we emitted:
```css
:root, :host {
}
```

There are some exceptions for "empty" at-rules, such as:

```css
@charset "UTF-8";
@layer foo, bar, baz;
@custom-media --modern (color), (hover);
@namespace "http://www.w3.org/1999/xhtml";
```

These don't have a body, but they still have a purpose and therefore
they will be emitted.

However, if you look at this:

```css
/* Empty rule */
.foo {
}

/* Empty rule, with nesting */
.foo {
  .bar {
  }
  .baz {
  }
}

/* Empty rule, with special case '&' rules */
.foo {
  & {
    &:hover {
    }
    &:focus {
    }
  }
}

/* Empty at-rule */
@media (min-width: 768px) {
}

/* Empty at-rule with nesting*/
@media (min-width: 768px) {
  .foo {
  }

  @media (min-width: 1024px) {
    .bar {
    }
  }
}
```

None of these will be emitted.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-01-31 17:56:52 +01:00
Philipp Spiess
95722020fe
Vite: Transform <style> blocks in html files (#16069)
Fixes #16036

This adds a new rule to treat `<style>` blocks found within `.html` file
as Tailwind CSS targets.

## Test plan

- Tested using the Vite extension (dev) and a new integration test
(prod)

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-01-31 14:26:45 +00:00
Robin Malfait
3aa0e494bf
Do not emit @keyframes in @theme reference (#16120)
This PR fixes na issue where `@keyframes` were emitted if they wre in a
`@theme
reference` and anothe `@theme {}` (that is not a reference) was present.

E.g.:

```css
@reference "tailwindcss";

@theme {
  /* ... */
}
```

Produces:
```css
:root, :host {
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
@keyframes ping {
  75%, 100% {
    transform: scale(2);
    opacity: 0;
  }
}
@keyframes pulse {
  50% {
    opacity: 0.5;
  }
}
@keyframes bounce {
  0%, 100% {
    transform: translateY(-25%);
    animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
  }
  50% {
    transform: none;
    animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
  }
}
```

With this PR, the produced CSS looks like this instead:
```css
:root, :host {
}
```

Note: the empty `:root, :host` will be solved in a subsequent PR.

### Test plan

Added some unit tests, and a dedicated integration test.
2025-01-31 15:13:17 +01:00
Philipp Spiess
c09bb5e256
Fix Vite issues with SolidStart (#16052)
Fixes #16045

This PR fixes two Vite issues found with SolidStart:

- SolidStart seems to emit an empty HTML chunk (where the content is
literally just `/`) with _no pathname_. Since we use the path to
generate an `id` for HTML chunks, this would currently cause a crash.
This was reported in #16045
- While testing the fix for the above, we also found that hot reloading
was not working in SolidStart since `4.0.0-alpha.22`. After doing some
bisecting we found that this is happening as SolidStart has the same
module ID in different servers and we were invalidating the root when we
shouldn't. After trying to restructure this code so that it only cleans
up the root when it is _no longer part of any server_, we noticed some
other compatibility issues with Nuxt and SvelteKit. It seems that the
safest bet is to no longer update a root at all during rebuilds in the
SSR step. This makes `invalidateAllRoots` a function that only notifiers
the servers about a change which is conceptually also less confusing.

## Test plan

- Added an integration test for SolidStart dev mode
- Manually tested the dev mode across all Vite based templates in
https://github.com/philipp-spiess/tailwindcss-playgrounds: Astro, Nuxt,
Remix, Solid, SvelteKit, and Vue.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-01-30 16:58:20 +01:00
Philipp Spiess
a4761ea967
Do not scan tailwind-merge sources for candidates (#16005)
Resolves #15722

This PR adds a list of ignored dependencies (in the current form only
`tailwind-merge`) to the Vite extension which, when included in the
dependency tree, are no longer scanned by the Tailwind CSS compiler for
candidates. This is to work around an issue where some dependencies
contain vast lists of valid Tailwind CSS class names which would
otherwise always be inlined in the build.

## Test plan

This was tested in our Vite playground on both dev and prod builds
across macOS and Windows:

### Windows prod build before

![telegram-cloud-photo-size-4-5823632138052944949-y](https://github.com/user-attachments/assets/a2bca1c7-cef3-4f04-a588-cbf1afdbdd37)

### Windows prod build after

This includes a debug `console.log(…)` to make sure it matches the right
module.

![telegram-cloud-photo-size-4-5823632138052944951-y](https://github.com/user-attachments/assets/109b82c5-1946-4d44-b812-d68474686eb7)

### Windows dev build after

This includes a debug `console.log(…)` to make sure it matches the right
module.

![telegram-cloud-photo-size-4-5823632138052944948-y](https://github.com/user-attachments/assets/41cbe7f1-f997-4c6c-b5fe-01d574f7a999)
2025-01-29 17:32:06 +01:00
Hugo van Rijswijk
9fef2bde50
Add :host rule to @theme layer (#15975)
Resolves #15799
Resolves #14478
Part-of #15005

Adds a `:host` selector for the `@theme` layer. This is necessary for
the `@theme` layer to work correctly in shadow DOM.

Also updates the snapshots for the tests that were affected by this
change (in a separate commit).

## Test plan

Tested via the Vite playground:

<img width="1121" alt="Screenshot 2025-01-29 at 15 06 49"
src="https://github.com/user-attachments/assets/a7908135-5ff8-472f-a053-d2c6d5c81e1b"
/>

Additionally made sure that `@property` defaults also work across
Firefox, Chrome, and Safari (the `@property` definition from the root is
pulled in) and added a UI spec.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-01-29 17:20:29 +01:00
Jordan Pittman
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>
2025-01-28 16:13:16 +00:00
Robin Malfait
4035ab0b76
Implement @variant (#15663)
This PR replaces `@variant` with `@custom-variant` for registering
custom variants via your CSS.

In addition, this PR introduces `@variant` that can be used in your CSS
to use a variant while writing custom CSS.

E.g.:

```css
.btn {
  background: white;

  @variant dark {
    background: black;
  }
}
```

Compiles to:

```css
.btn {
  background: white;
}

@media (prefers-color-scheme: dark) {
  .btn {
    background: black;
  }
}
```

For backwards compatibility, the `@variant` rules that don't have a body
and are
defined inline:

```css
@variant hocus (&:hover, &:focus);
```

And `@variant` rules that are defined with a body and a `@slot`:

```css
@variant hocus {
  &:hover, &:focus {
    @slot;
  }
}
```

Will automatically be upgraded to `@custom-variant` internally, so no
breaking changes are introduced with this PR.

---

TODO:
- [x] ~~Decide whether we want to allow multiple variants and if so,
what syntax should be used. If not, nesting `@variant <variant> {}` will
be the way to go.~~ Only a single `@variant <variant>` can be used, if
you want to use multiple, nesting should be used:

```css
.foo {
  @variant hover {
    @variant focus {
      color: red;
    }
  }
}
```
2025-01-21 10:20:35 -05:00
Robin Malfait
f93c42fcfc
Write to stdout when --output is omitted (#15656) 2025-01-17 15:23:34 +00:00
Philipp Spiess
2de644b20e
Remove @property fallbacks for Firefox (#15622)
This PR removes the `@property` fallbacks added in #13655. This is
possible because we're targeting a minimum Firefox version of 128 which
[includes support for \`@property\`
rules](https://developer.mozilla.org/de/docs/Web/CSS/@property).

<img width="1284" alt="Screenshot 2025-01-14 at 11 36 44"
src="https://github.com/user-attachments/assets/ae070781-35c1-4165-be51-baa63f28db5b"
/>
2025-01-14 15:51:44 +01:00
depfu[bot]
ae8fb146a7
Update fast-glob 3.3.2 → 3.3.3 (patch) (#15607) 2025-01-13 11:11:35 +01:00