This PR fixes a Windows CI issue due to the recently merged #17846
There might be better ways to solve this, but essentially we want to
make sure we are always dealing with `\n` and not `\r\n`. This PR fixes
that by replacing all `\r\n` with `\n` in the tests.
You can use `@reference "tailwindcss"` or `@reference
"../path/to/your/css/file.css"` to reference your theme for use in
`@apply`, `theme(…)`, etc…
Unfortunatley, because the imported file still contains `@tailwind
utilities` it would trigger a re-scan of the filesystem — even though
the use of `@reference` ensures that no CSS can actually be output by
the import itself.
This PR does two things:
- Adds some explicit feature detection tests for what features we pick
up in a stylesheet based on the CSS written and how things are imported
- Explicitly ignores `@tailwind utilities` inside of `@reference` so it
isn't a trigger for file scanning
Because of how Vite itself handles dependencies editing files on disk
will still trigger a rebuild of any file using `@reference`. This is
because Vite rebuilds files when _any_ of its transitive dependencies
change.
For example, given this Vue file:
```vue
<style>
@reference "./styles.css";
</style>
<template> <!-- ... --> </template>
```
And this stylesheet:
```css
@import "tailwindcss";
```
The dependency chain looks like this: `file.vue -> styles.css -> {all
the sources in your project}`
Vite sees that a file (e.g. `index.html`) has changed, thus `styles.css`
needs change, which means `file.vue` needs to be compiled again as well.
Now in reality we depend on the _on disk_ version of styles.css not the
compiled version but Vite itself doesn't know that (or have a way to
indicate this afaik).
Coming up with a solution to that problem will have to be a separate PR
— but there is a workaround:
### 1. Inline the imports from `@import "tailwindcss";`
Replace this in your main stylesheet:
```css
@import "tailwindcss";
```
with this (this is basically what `node_modules/tailwindcss/index.css`
is):
```css
@layer theme, base, components, utilities;
@import 'tailwindcss/theme' layer(theme);
@import 'tailwindcss/preflight' layer(base);
@import 'tailwindcss/utilities' layer(utilities);
/* the rest of your styles imports, styles, etc… */
```
### 2. Split your stylesheet into "main" and "theme" parts
Your "theme" is comprised of the `@import 'tailwindcss/theme'
layer(theme);` import, any custom `@theme` blocks, any `@config`
directives, and any `@plugin` directives. Move all of these into their
own file.
For example, replace this with two files:
```css
@layer theme, base, components, utilities;
@import 'tailwindcss/theme' layer(theme);
@import 'tailwindcss/preflight' layer(base);
@import 'tailwindcss/utilities' layer(utilities);
@theme {
--color-primary: #c0ffee;
}
@plugin "./my-plugin.js";
/* the rest of your styles imports, styles, etc… */
```
with a theme file:
```css
@import 'tailwindcss/theme' layer(theme);
/* all your `@theme` stuff goes in this file */
@theme {
--color-primary: #c0ffee;
}
/* additionally any @config or @plugin does too */
@plugin "./my-plugin.js";
```
and your main CSS file:
```css
@layer theme, base, components, utilities;
@import './my-theme.css'; /* I replaced this import */
@import 'tailwindcss/preflight' layer(base);
@import 'tailwindcss/utilities' layer(utilities);
/* the rest of your styles imports, styles, etc… */
```
### 3. Import only your "theme" file in your Vue components / CSS
modules / etc…
```vue
<style>
@reference "./my-theme.css";
</style>
<template> <!-- ... --> </template>
```
Fixes#17693
This PR fixes and improves the HAML extractor by ensuring that whenever
we detect lines or blocks of Ruby code (`-`, `=` and `~`) characters at
the beginning of the line, we treat them as Ruby code.
Fixes: #17813
## Test Plan
1. Existing tests pass
2. Changed 1 existing test which embedded Ruby syntax
3. Added a dedicated test to ensure the HAML file in the linked issue is
parsed correctly
Running this in the internal extractor tool you can see that the
`w-[12px]`, `w-[16px]`, `h-[12px]`, and `h-[16px]` are properly
extracted.
Note: the `mr-12px` is also extracted, but not highlighted because this
is not a valid Tailwind CSS class.
<img width="1816" alt="image"
src="https://github.com/user-attachments/assets/fc5929ca-bc71-47d2-b21b-7abeec86f54d"
/>
This PR introduces a vastly improved upgrade migrations system, to
migrate your codebase and modernize your utilities to make use of the
latest variants and utilities.
It all started when I saw this PR the other day:
https://github.com/tailwindlabs/tailwindcss/pull/17790
I was about to comment "Don't forget to add a migration". But I've been
thinking about a system where we can automate this process away. This PR
introduces this system.
This PR introduces upgrade migrations based on the internal Design
System, and it mainly updates arbitrary variants, arbitrary properties
and arbitrary values.
## The problem
Whenever we ship new utilities, or you make changes to your CSS file by
introducing new `@theme` values, or adding new `@utility` rules. It
could be that the rest of your codebase isn't aware of that, but you
could be using these values.
For example, it could be that you have a lot of arbitrary properties in
your codebase, they look something like this:
```html
<div class="[color-scheme:dark] [text-wrap:balance]"></div>
```
Whenever we introduce new features in Tailwind CSS, you probably don't
keep an eye on the release notes and update all of these arbitrary
properties to the newly introduced utilities.
But with this PR, we can run the upgrade tool:
```console
npx -y @tailwindcss/upgrade@latest
```
...and it will upgrade your project to use the new utilities:
```html
<div class="scheme-dark text-balance"></div>
```
It also works for arbitrary values, for example imagine you have classes
like this:
```html
<!-- Arbitrary property -->
<div class="[max-height:1lh]"></div>
<!-- Arbitrary value -->
<div class="max-h-[1lh]"></div>
```
Running the upgrade tool again:
```console
npx -y @tailwindcss/upgrade@latest
```
... gives you the following output:
```html
<!-- Arbitrary property -->
<div class="max-h-lh"></div>
<!-- Arbitrary value -->
<div class="max-h-lh"></div>
```
This is because of the original PR I mentioned, which introduced the
`max-h-lh` utilities.
A nice benefit is that this output only has 1 unique class instead of 2,
which also potentially reduces the size of your CSS file.
It could also be that you are using arbitrary values where you (or a
team member) didn't even know an alternative solution existed.
E.g.:
```html
<div class="w-[48rem]"></div>
```
After running the upgrade tool you will get this:
```html
<div class="w-3xl"></div>
```
We can go further though. Since the release of Tailwind CSS v4, we
introduced the concept of "bare values". Essentially allowing you to
type a number on utilities where it makes sense, and we produce a value
based on that number.
So an input like this:
```html
<div class="border-[123px]"></div>
```
Will be optimized to just:
```html
<div class="border-123"></div>
```
This can be very useful for complex utilities, for example, how many
times have you written something like this:
```html
<div class="grid-cols-[repeat(16,minmax(0,1fr))]"></div>
```
Because up until Tailwind CSS v4, we only generated 12 columns by
default. But since v4, we can generate any number of columns
automatically.
Running the migration tool will give you this:
```html
<div class="grid-cols-16"></div>
```
### User CSS
But, what if I told you that we can keep going...
In [Catalyst](https://tailwindcss.com/plus/ui-kit) we often use classes
that look like this for accessibility reasons:
```html
<div class="text-[CanvasText] bg-[Highlight]"></div>
```
What if you want to move the `CanvasText` and `Highlight` colors to your
CSS:
```css
@import "tailwincdss";
@theme {
--color-canvas: CanvasText;
--color-highlight: Highlight;
}
```
If you now run the upgrade tool again, this will be the result:
```html
<div class="text-canvas bg-highlight"></div>
```
We never shipped a `text-canvas` or `bg-highlight` utility, but the
upgrade tool uses your own CSS configuration to migrate your codebase.
This will keep your codebase clean, consistent and modern and you are in
control.
Let's look at one more example, what if you have this in a lot of
places:
```html
<div class="[scrollbar-gutter:stable]"></div>
```
And you don't want to wait for the Tailwind CSS team to ship a
`scrollbar-stable` (or similar) feature. You can add your own utility:
```css
@import "tailwincdss";
@utility scrollbar-stable {
scrollbar-gutter: stable;
}
```
```html
<div class="scrollbar-stable"></div>
```
## The solution — how it works
There are 2 big things happening here:
1. Instead of us (the Tailwind CSS team) hardcoding certain migrations,
we will make use of the internal `DesignSystem` which is the source of
truth for all this information. This is also what Tailwind CSS itself
uses to generate the CSS file.
The internal `DesignSystem` is essentially a list of all:
1. The internal utilities
2. The internal variants
3. The default theme we ship
4. The user CSS
1. With custom `@theme` values
2. With custom `@custom-variant` implementations
3. With custom `@utility` implementations
2. The upgrade tool now has a concept of `signatures`
The signatures part is the most interesting one, and it allows us to be
100% sure that we can migrate your codebase without breaking anything.
A signature is some unique identifier that represents a utility. But 2
utilities that do the exact same thing will have the same signature.
To make this work, we have to make sure that we normalize values. One
such value is the selector. I think a little visualization will help
here:
| UTILITY | GENERATED SIGNATURE |
| ---------------- | ----------------------- |
| `[display:flex]` | `.x { display: flex; }` |
| `flex` | `.x { display: flex; }` |
They have the exact same signature and therefore the upgrade tool can
safely migrate them to the same utility.
For this we will prefer the following order:
1. Static utilities — essentially no brackets. E.g.: `flex`,
`grid-cols-2`
2. Arbitrary values — e.g.: `max-h-[1lh]`, `border-[2px]`
3. Arbitrary properties — e.g.: `[color-scheme:dark]`, `[display:flex]`
We also have to canonicalize utilities to there minimal form.
Essentially making sure we increase the chance of finding a match.
```
[display:_flex_] → [display:flex] → flex
[display:_flex] → [display:flex] → flex
[display:flex_] → [display:flex] → flex
[display:flex] → [display:flex] → flex
```
If we don't do this, then the signatures will be slightly different, due
to the whitespace:
| UTILITY | GENERATED SIGNATURE |
| ------------------ | ------------------------- |
| `[display:_flex_]` | `.x { display: flex ; }` |
| `[display:_flex]` | `.x { display: flex; }` |
| `[display:flex_]` | `.x { display: flex ; }` |
| `[display:flex]` | `.x { display: flex; }` |
### Other small improvements
A few other improvements are for optimizing existing utilities:
1. Remove unnecessary data types. E.g.:
- `bg-[color:red]` -> `bg-[red]`
- `shadow-[shadow:inset_0_1px_--theme(--color-white/15%)]` ->
`shadow-[inset_0_1px_--theme(--color-white/15%)]`
This also makes use of these signatures and if dropping the data type
results in the same signature then we can safely drop it.
Additionally, if a more specific utility exists, we will prefer that
one. This reduced ambiguity and the need for data types.
- `bg-[position:123px]` → `bg-position-[123px]`
- `bg-[123px]` → `bg-position-[123px]`
- `bg-[size:123px]` → `bg-size-[123px]`
2. Optimizing modifiers. E.g.:
- `bg-red-500/[25%]` → `bg-red-500/25`
- `bg-red-500/[100%]` → `bg-red-500`
- `bg-red-500/100` → `bg-red-500`
3. Hoist `not` in arbitrary variants
- `[@media_not_(prefers-color-scheme:dark)]:flex` →
`not-[@media_(prefers-color-scheme:dark)]:flex` → `not-dark:flex` (in
case you are using the default `dark` mode implementation
4. Optimize raw values that could be converted to bare values. This uses
the `--spacing` variable to ensure it is safe.
- `w-[64rem]` → `w-256`
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
This PR changes the `transition` utility to include five new properties:
- `display`
- `visibility`
- `content-visibility`
- `overlay`
- `pointer-eventes`
On its own, this change does nothing since these properties will apply
their change _immediately_. However, in combination with
`transition-discrete` this will ensure that you can now transition these
types without requiering `transition-all` or arbitrary transition
properties.
## Test plan
- Ensured this works in the Vite playground with native `<dialog>`
components
https://github.com/user-attachments/assets/89bf4a75-b681-4574-8bb4-845fffdec43b
Notice how:
- the backdrop stays open until the transition is over (that's because
of `overlay` in the property list)
- the dialog is displayed until the transition is over
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This PR improves the upgrade tooling at tiny bit to make sure that as
long as we didn't change any of the stylesheets, that we also don't sort
internal nodes and/or format the stylesheet at all.
This is important in case the Prettier rules are different or if a
totally different formatter is used.
Essentially, if we didn't have to change the stylesheets because of a
migration, we don't want to change it due to a formatter either.
To fix the CI issues we have, it turns out there are two issues:
1. The file-read was not retried, making it possible for all platforms
(but mostly Windows because it's the slowest) to sometimes not have the
file created yet before making the assertion.
2. A forward-slash in the assertion message path would always be a
backslash when run on Windows, thus the Windows test would never pass.
## Test plan
- [ci-all] and have the test pass two times.
This PR improves colors with alpha values where the alpha value results
in 100%.
Before this change, a class like `bg-red-500/100` would be generated as:
```css
.bg-red-500\/100 {
background-color: #ef4444;
}
@supports (color: color-mix(in lab, red, red)) {
.bg-red-500\/100 {
background-color: color-mix(in oklab, var(--color-red-500) 100%, transparent);
}
}
```
But we don't need the `color-mix`, or the fallback styles at all in case
the alpha value is 100%.
After this change the `bg-red-500/100` class will generate as:
```css
.bg-red-500\/100 {
background-color: var(--color-red-500);
}
```
Which is essentially the same as `bg-red-500`, but we can migrate that
away in the future. At least the generated CSS is smaller.
## Test plan
1. Added a test to ensure the generated value doesn't include color-mix
at all.
While working on another PR I noticed that some variants were re-printed
in an odd way.
Specifically, this PR fixes an issue where variants using the `@`-root
were incorrectly printed.
- `@lg` was printed as `@-lg`
- `@[400px]` was printed as `@-[400px]`
This is now special cased where the `-` is not inserted for `@`-root
variants.
## Test plan
1. Added a test to ensure the `@`-root variants are printed correctly.
This PR adds the following utilities that can be used to match an
elements line height:
- `h-lh`
- `min-h-lh`
- `max-h-lh`
These are all equivalent to providing `1lh` as an arbitrary value. e.g.
`h-[1lh]`
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.
This PR bumps all Tailwind CSS related dependencies when running the
upgrade tool _if_ the dependency exists in your package.json file.
E.g.: if you have `tailwindcss` and `@tailwindcss/vite` in your
package.json, then both will be updated to the latest version.
This PR is not trying to be smart and skip updating if you are already
on the latest version. It will just try and update the dependencies and
your package manager will do nothing in case it was already the latest
version.
## Test plan
Ran this on one of my personal projects and this was the output:
<img width="1023" alt="image"
src="https://github.com/user-attachments/assets/a189fe7a-a58a-44aa-9246-b720e7a2a892"
/>
Notice that I don't have `@tailwindcss/vite` logs because I am using
`@tailwindcss/postcss`.
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]
This PR ignores `.db` files by default. We already ignored `.sqlite` and
`.sqlite3` files but we didn't ignore `.db` files which is a common
extension for database files as well.
Due to the binary nature of `.db` files, scanning these could result in
hard to debug errors such as:

Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1328
The alpha and beta releases used `_` in theme keys to represent a `.`.
This meant we used `--leading-1_5` instead of `--leading-1\.5` to add
utilities like `leading-1.5`. We prefer the use of the escaped dot now
but still want to make sure suggestions for the legacy key format still
works as expected when surrounded by numbers.
This is the same as #16433 but for `@utility` since we apparently missed
this when emitting suggestions for it
Right now if you have a completely empty theme we'll still suggest
`shadow`, `inset-shadow`, and `text-shadow` as utilities even tho they
won't exist. This fixes this by checking for the theme key when
computing the suggestions.
Resolves https://github.com/tailwindlabs/tailwindcss/issues/17699
GeoJSON files are giant JSON files for geographic data structures and
will never contain Tailwind classes, but because they are often huge
they will slow down the build a lot if scanned.
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
We generate a glob to ignore binary extensions that looks something like
this:
```
*.{mp4,pages,exe,…}
```
However, if you have a folder that happens to end in `.pages` for
example, then this folder will be ignored in its entirety.
To solve this, we added a new flag to the `Gitignore` struct so we can
register a bunch of ignore rules that _only_ apply to paths and not
folders.
Fixes: #17569
## Test plan
- Added a unit test
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
To publish the newly adde WASM builds, we rely on the
`node-linker=hoisted` `.npmrc` flag that isn't read when `pnpm
--recursive` is used. To work around it, this PR excludes the wasm
package from the `--recursive` part and manually published it
afterwards.
## Test Plan
Ensured this does not error now when trying a `--dry run`.
<img width="1273" alt="Screenshot 2025-04-11 at 17 43 38"
src="https://github.com/user-attachments/assets/68a28552-0125-4da1-92ff-74e58368abe4"
/>
Closes#17448Closes#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>
Closes#17621
Chrome as a pretty ugly rendering glitch when using a `skew-*` utility
in Tailwind 4: https://play.tailwindcss.com/HuiZtbrHOc
The was not an issue in v3 since transforms were set up in a different
way. Without the `var(…)` syntax, the difference boils down to this:
```css
.skew-v3 {
transform: rotate(0) skewX(-20deg);
}
.skew-v4 {
transform: rotateX(0) rotateY(0) rotateZ(0) skewX(-20deg);
}
```
It appears that using any of the single-dimension rotate functions will
cause the Chrome rendering to glitch.
After doing some digging, we found [that initially these `@property`s
were defined as type `<transform-function>` and later changed to
`*`](https://github.com/tailwindlabs/tailwindcss/issues/15144). With a
type of `*`, it makes sense that the initial value of these variables
can default to `initial` without any compromises, allowing us to default
to something like this now:
```css
.skew-new {
transform: skewX(-20deg);
}
```
Tested this change in the latest version of Chrome (135) and it does
make the rendering glitch in the initial issue disappear. By using the
`var(--tw-rotate-x,)` syntax we also ensure this works on older versions
of Safari (tested on Safari 15.5 and 16.4).
Note, however, that there are still glitches happening when you combine
rotate and skew in the latest version of Chrome or when you transition
the `skew(…)` variable. This also happens in plain CSS with no variables
though, so there isn't something we can do about this:
https://play.tailwindcss.com/g3FXPEJHpn
## Test plan
- Tested on latest Chrome, Firefox, and Safari as well as Safari 15.5
and 16.4.
<img width="564" alt="Screenshot 2025-04-09 at 18 01 51"
src="https://github.com/user-attachments/assets/2e0b1c96-7c4d-41a8-b3d0-0f6134a3e635"
/>
Fixes#17643.
This PR completely removes the `color-mix()` function for
`shadow-inherit`. This does mean intensity and alpha channel support has
been removed when using `shadow-inherit`[^1].
With intensity modifiers in #17398, all colors are wrapped in
`color-mix()`. However, it seems `inherit` does not work as a value in
`color-mix()` in Firefox or Chrome (don't have a means to test Safari).
[^1]: While writing this, I noticed other color utilities allow alpha
channel modifier syntax for `inherit` - do we want to look at removing
those too?
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
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>
Fixes#17614.
Candidate parsing for variants only account for the root `@` if there no
hyphens. It seems like the current logic assumes if it *does* have a
hyphen, then it would be one of `@min` or `@max`. However, with:
```css
@theme {
--container-foo-bar: 1440px;
}
```
Then `@foo-bar` should be valid. However, we only check for `@foo-bar`
and `@foo` as roots, but never `@`. This PR adds a check for `@` at the
very end after iterating through root permutations.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Discovered while triaging #17556
This PR improves the `color-mix(...)` polyfill to ensure it works when a
theme key links to another theme key via a `var(...)` call.
Imagine this setup:
```css
@theme {
--color-red: var(--color-red-500);
--color-red-500: oklch(63.7% 0.237 25.331);
}
@source inline("text-red/50");
````
Since `--color-red` will link to `--color-red-500` _which is also a
known theme variable_, we can inline the value of `--color-red-500` into
the fallback now:
```css
.text-red\\/50 {
color: var(--color-red);
}
@supports (color: color-mix(in lab, red, red)) {
.text-red\\/50 {
color: color-mix(in oklab, var(--color-red) 50%, transparent);
}
}
```
This will allow for slightly less confusing behavior.
The code added also handles recursive definitions where a color is
linking to another color that is again linking to the first one (by
adding a `Set` to keep track of already seen variable names).
## Test plan
- Added unit test