This PR fixes a bug where CSS was generated for `start` and `end`. This
was accidentally introduced when we moved the `start-*` and `end-*`
utilities to the legacy utilities. But this meant that we now generate
CSS for `start` and `end` even if no value is provided.
Fixes: #20002
## Test plan
1. Updated the tests to make sure of `--spacing: 0.25rem` which made the
tests fail, and are fixed again by applying the fix.
2. Other tests are still passing
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.
### What changed?
#### ✳️ enhanced-resolve (5.20.1 → 5.21.0) ·
[Repo](https://github.com/webpack/enhanced-resolve) ·
[Changelog](https://github.com/webpack/enhanced-resolve/blob/main/CHANGELOG.md)
<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/webpack/enhanced-resolve/releases/tag/v5.21.0">5.21.0</a></h4>
<blockquote><h3 dir="auto">Minor Changes</h3>
<ul dir="auto">
<li>
<p dir="auto">Added promise API and support to resolve without <code
class="notranslate">context</code> and <code
class="notranslate">resolveContext</code>. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/520">#520</a>)</p>
</li>
<li>
<p dir="auto">Add <code
class="notranslate">extensionAliasForExports</code> option. When <code
class="notranslate">true</code>, <code
class="notranslate">extensionAlias</code> also applies to paths resolved
through the <code class="notranslate">package.json</code> <code
class="notranslate">exports</code> field. Off by default to match
Node.js; opt in for full TypeScript-resolver parity with packages that
ship <code class="notranslate">.ts</code> sources alongside the compiled
<code class="notranslate">.js</code> they declare in <code
class="notranslate">exports</code>. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/554">#554</a>)</p>
</li>
</ul>
<h3 dir="auto">Patch Changes</h3>
<ul dir="auto">
<li>
<p dir="auto">Properly handle DOS device paths (<code
class="notranslate">\\?\…</code> and <code
class="notranslate">\\.\…</code>). (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/551">#551</a>)</p>
</li>
<li>
<p dir="auto">Prevent fallback to parent node_modules when the <code
class="notranslate">exports</code> field target file is not found. (by
<a href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/495">#495</a>)</p>
</li>
<li>
<p dir="auto">Imports field spec deviation: non-relative targets (e.g.
<code class="notranslate">"#a": "#b"</code>) no longer re-enter imports
resolution, aligning with the Node.js ESM spec where <code
class="notranslate">PACKAGE_IMPORTS_RESOLVE</code> does not recursively
resolve <code class="notranslate">#</code> specifiers. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/503">#503</a>)</p>
<p dir="auto">Previously <code class="notranslate">{ "#a": "#b", "#b":
"./the.js" }</code> would chain-resolve <code
class="notranslate">#a</code> to <code
class="notranslate">./the.js</code>; now it correctly fails, matching
Node.js behavior.</p>
</li>
<li>
<p dir="auto">Move <code class="notranslate">cachedJoin</code>/<code
class="notranslate">cachedDirname</code>/<code
class="notranslate">createCachedBasename</code> caches from module-level
globals to per-Resolver instances. This prevents unbounded memory growth
in long-running processes — when a Resolver is garbage collected, its
join/dirname/basename caches are released with it. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/507">#507</a>)</p>
</li>
<li>
<p dir="auto">Fixed when <code class="notranslate">tsconfig: true</code>
is used (default config file) and no <code
class="notranslate">tsconfig.json</code> exists. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/502">#502</a>)</p>
</li>
<li>
<p dir="auto">Apply the <code class="notranslate">extensionAlias</code>
option to the <code class="notranslate">imports</code> field to be align
with typescript resolution. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/549">#549</a>)</p>
</li>
<li>
<p dir="auto">Improved performance of the many plugins. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/529">#529</a>)</p>
</li>
<li>
<p dir="auto">Replace the <code
class="notranslate">Set<string></code>-based resolver stack with a
singly-linked <code class="notranslate">StackEntry</code> class that
exposes a Set-compatible API. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/526">#526</a>)</p>
<p dir="auto">Each <code class="notranslate">doResolve</code> call now
prepends a single linked-list node instead of cloning the entire Set,
making stack push O(1) in time and memory. Recursion detection walks the
linked list (O(n)), but because the stack is typically shallow this is
much cheaper than cloning a Set per call.</p>
</li>
<li>
<p dir="auto">Cache the result of <code
class="notranslate">stripJsonComments</code> + <code
class="notranslate">JSON.parse</code> in <code
class="notranslate">readJson</code> using a <code
class="notranslate">WeakMap</code> keyed by the raw file buffer. This
avoids redundant comment-stripping and JSON parsing on every resolve
call that reads tsconfig.json files (via <code
class="notranslate">stripComments: true</code>), improving
TsconfigPathsPlugin warm performance by ~20-35% depending on the depth
of the <code class="notranslate">extends</code> chain. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/524">#524</a>)</p>
</li>
<li>
<p dir="auto">Avoid OOM in CachedInputFileSystem when duration is
Infinity. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/527">#527</a>)</p>
</li>
</ul></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please
let us know.</a></em></p>
</details>
<details>
<summary>Commits</summary>
<p><a
href="ebc67d3896...35035ca158">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</details>
---

[Depfu](https://depfu.com) will automatically keep this PR
conflict-free, as long as you don't add any commits to this branch
yourself. You can also trigger a rebase manually by commenting with
`@depfu rebase`.
<details><summary>All Depfu comment commands</summary>
<blockquote><dl>
<dt>@depfu rebase</dt><dd>Rebases against your default branch and
redoes this update</dd>
<dt>@depfu recreate</dt><dd>Recreates this PR, overwriting any edits
that you've made to it</dd>
<dt>@depfu merge</dt><dd>Merges this PR once your tests are passing and
conflicts are resolved</dd>
<dt>@depfu cancel merge</dt><dd>Cancels automatic merging of this
PR</dd>
<dt>@depfu close</dt><dd>Closes this PR and deletes the branch</dd>
<dt>@depfu reopen</dt><dd>Restores the branch and reopens this PR (if
it's closed)</dd>
<dt>@depfu pause</dt><dd>Ignores all future updates for this dependency
and closes this PR</dd>
<dt>@depfu pause [minor|major]</dt><dd>Ignores all future minor/major
updates for this dependency and closes this PR</dd>
<dt>@depfu resume</dt><dd>Future versions of this dependency will
create PRs again (leaves this PR as is)</dd>
</dl></blockquote>
</details>
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Small PR that improves the overall quality of the codebase. It's a bit
of everything:
1. Using correct variants for the variants we are testing
2. Use `@reference` instead of `@import` in a test, testing the
`@reference` according to the test name
3. Use `using` for Vitest related mocks. They have a `Symbol.dispose`
implemented, so we can don't have to restore mocks ourselves (right now,
some of them are not cleaned up at all).
4. Updated deprecated `.toThrowError` with `.toThrow` APIs
## Test plan
Everything still passes.
[ci-all]
This PR is an internal change only related to how we visualize source
maps.
As part of this PR
(https://github.com/tailwindlabs/tailwindcss/pull/19996) I added a test
for source maps related to how `@variant` is processed. And while the
result was correct, I had a hard time verifying if this was _actually_
correct. I did the mental mapping of comparing locations from the output
to the input.
<img width="495" height="495" alt="image"
src="https://github.com/user-attachments/assets/2b1c12cd-43ee-461a-b93b-bafe6ce1cce5"
/>
With this PR, I want to make that more visual by actually printing the
input source(s) and output file and highlight the necessary parts:
<img width="1101" height="1085" alt="image"
src="https://github.com/user-attachments/assets/14390026-f211-4cfc-8c6c-3293105f6403"
/>
I didn't want to get too clever here. But printing line numbers also
helps in case we point to different spots on the same line:
<img width="1200" height="981" alt="image"
src="https://github.com/user-attachments/assets/e75f7622-eb51-49ee-928e-fdc8672d2c3f"
/>
And if we point to different files, then we visualize these as well:
<img width="851" height="957" alt="image"
src="https://github.com/user-attachments/assets/77e65801-b46b-4579-8659-d919a8750f60"
/>
If you combine this with snapshot tests, then it's very easy to verify
that locations match up correctly. Each source map location is
highlighted and references a symbol starting at `A`, `B`, etc.
A change to the source maps _can_ result in a big diff, and even this PR
introduces a big diff because of the preflight diffs. But at least you
can see how things line up.
## Test plan
1. All tests still pass
2. Added tests for the source map visualizer that is only used in tests
3. No actual source code was touched
This PR improves and simplifies the `@variant` usage.
When we originally added support for `@variant`, we wanted to keep
things simple, where we could only use a single variant at a time. The
original PR did have a more complex system with all these features
enabled, but we wanted to make sure that we only introduced the
additional complexity when the community felt like it was needed.
But of course we still wanted to make sure that you could do compound
and stacked variants, it just required some additional code.
For compound variants, where you want to use variant `a` and variant
`b`, you could duplicate the rules as siblings:
```css
.foo {
@variant a {
display: flex;
}
@variant b {
display: flex;
}
}
```
But with this PR, you can comma separate each variant to get the same
effect:
```css
.foo {
@variant a, b {
display: flex;
}
}
```
You can think of this as-if we are expanding this syntax into the
aforementioned syntax. In other words, we would do the duplication for
you.
Additionally, you also want to be able to stack variants. For that you
had to nest your `@variant` rules:
```css
.foo {
@variant a {
@variant b {
display: flex;
}
}
}
```
Not the end of the world, but it can get pretty nested if you want to
use multiple variants. Luckily we already have a syntax for this in
normal Tailwind CSS classes: `a🅱️flex`. Which is exactly what we can
use here as well:
```css
.foo {
@variant a:b {
display: flex;
}
}
```
Again, conceptually you can think of this syntax being expanded into the
syntax from above.
Last but not least, we can also combine these:
```css
.foo {
background: black;
@variant a, b:c {
background: red;
@variant d, e:f {
background: blue;
}
}
}
```
This conceptually translates into the much more verbose version today:
```css
.foo {
background: black;
@variant a {
background: red;
@variant d {
background: blue;
}
@variant e {
@variant f {
background: blue;
}
}
}
@variant b {
@variant c {
background: red;
@variant d {
background: blue;
}
@variant e {
@variant f {
background: blue;
}
}
}
}
}
```
The biggest downside is that this could potentially easily balloon your
CSS file size if you're not careful. Because with this, it's pretty easy
to add one more variant that introduces a lot of duplicated CSS.
This feature is completely backwards compatible, you can still nest your
`@variant` calls yourself if you want, and combine them with these
features if you want.
This is also a continuation of #19526 and #19884, but for some reason I
don't have push rights, so I'm creating this new PR instead. I did keep
the original commits of those PRs so these contributors are still
properly marked as contributors.
<img width="808" height="135" alt="image"
src="https://github.com/user-attachments/assets/bee334ab-39d7-4d4d-a48f-afa2253cf17b"
/>
<img width="349" height="75" alt="image"
src="https://github.com/user-attachments/assets/fb68906c-db74-43f7-83e4-918ad3d4a036"
/>
Closes: #19526Closes: #19884
## Test plan
1. Added a bunch of new tests to verify this new behavior
2. Added tests that compare the short (new) version, and the long (old)
version
3. Added a sourcemap related test to ensure that the src and dst
locations are correct
4. Existing tests still pass
---------
Co-authored-by: orteth01 <tortega128@gmail.com>
Co-authored-by: Ray Knight <array.knight+github@gmail.com>
## Summary
Fixes#16948
When defining multiple CSS `@utility foo-*` with different value types
(e.g., one for colors, one for numbers), only the first handler was
tried. If it returned `null` (value didn't match), the compile loop
stopped, preventing subsequent handlers from being attempted.
```css
@utility foo-* {
color: --value(--color-*);
}
@utility foo-* {
font-size: --spacing(--value(number));
}
```
Previously, `foo-red-500` worked but `foo-123` did not (or vice versa
depending on definition order).
The fix distinguishes between CSS `@utility` handlers and JS plugin
`matchUtilities` handlers:
- **CSS `@utility`** (no typed options): `null` means "try the next
handler" - allows multiple definitions with different value types to
coexist
- **JS `matchUtilities`** (with explicit types): `null` means "the value
was invalid for this type, stop" - preserves existing behavior where
typed utilities prevent invalid values from falling through
## Test plan
- Added test: two `@utility foo-*` definitions with different value
types - verifies both `foo-red-500` (color) and `foo-123` (number)
produce correct CSS
- All 4621 existing tests pass (including the `matchUtilities`
type-safety tests)
- `pnpm build && pnpm test` passes
This contribution was developed with AI assistance (Claude Code).
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Fixes a few minor typos across the codebase (e.g. 'overriden' ->
'overridden', 're-use' -> 'reuse').
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Prevent the upgrade tool from rewriting CSS properties inside inline
`style` attributes.
This fixes cases like `style="flex-grow: 1"` being changed to
`style="grow: 1"` and adds regression tests.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR improves the canonicalization around arbitrary variants
containing `[&:has(…)]`, by converting them to `has-[…]`.
Essentially when you have a variant, with `&:has(…)`, we will convert
it:
```diff
- [&:has([role=checkbox])]:flex
+ has-[[role=checkbox]]:flex
```
This also means that if the arbitrary selector inside of the `&:has(…)`
can be converted to something that aligns with known variants, then we
will do that as well:
```diff
// `data-*` can be hoisted outside of the arbitrary value
- [&:has([data-slot=description])]:flex
+ has-data-[slot=description]:flex
// `aria-visible="true"` can be entirely replaced by `aria-visible`
- [&:has([aria-visible="true"])]:flex
+ has-aria-visible:flex
```
Noticed this while looking into:
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1562
## Test plan
1. Added additional tests for this use case
2. Existing tests still pass
This PR improves the canonicalization when dealing with arbitrary
values.
As part of the canonicalization process we compute a signature for a
given utility. This way we can ensure that when we canonicalize a
candidate into a simpler candidate that it's still equivalent if the
signatures match.
One thing we do during signature computation is normalizing dimensions
(value + unit) into the same unit to make comparisons easier.
For example:
```css
.foo { margin-top: 20in; }
.bar { margin-top: 1920px; }
```
Will both get converted to `1920px` and therefore `foo` and `bar` will
have the same signature.
Up until this part, everything is fine. However, this normalization also
leaks when we try to canonicalize arbitrary values. One of the things we
do is try to move the `-` into the arbitrary value:
```diff
- -mt-[20in]
+ mt-[calc(20in_*_-1)]
```
This is obviously not cleaner, but we can perform some canonicalization
of the arbitrary value. As part of that we do constant folding _and_ the
normalization of base units. That means that we would see this:
```diff
- -mt-[20in]
- mt-[calc(20in_*_-1)]
+ mt-[-1920px]
```
But this might be very confusing because it might not make sense where
the `1920px` even came from... the only thing we should have done here
is constant fold that calc expression.
That's what this PR does, it only does the constant folding but without
the unit normalization:
```diff
- -mt-[20in]
- mt-[calc(20in_*_-1)]
+ mt-[-20in]
```
Which is exactly what we want!
Fixes:
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1573
## Test plan
1. Added a regression test based on the linked issue
2. All other tests still pass
This PR fixes a printing bug during canonicalization where it converts:
```
[&:has(~_*_*:checked)]:text-green-500
```
into:
```
[&:has(~**:checked)]:text-green-500
```
This is because the `_` was marked as insignificant and therefore
removed. This PR fixes that and maintains the whitespace (`_`)
characters when needed.
Additionally, in the comments of the linked issue somebody mentioned
that:
```
w-[calc(100%_-_--spacing(60))]
```
was turned into:
```
w-[calc(100%---spacing(60))]
```
...and while that's still correct and parseable, it's not the prettiest.
This PR will still get rid of the whitespace, but introduce wrapping
parens `(…)` instead, in case readability is not ideal.
In this case, we will turn it into:
```diff
- w-[calc(100%_-_--spacing(60))]
- w-[calc(100%---spacing(60))]
+ w-[calc(100%-(--spacing(60)))]
```
Of course there are some cases where we don't need to introduce `(…)`
unnecessarily:
- `shadow-[inset_0px_1px_--theme(--color-white/15%)]` would not be
turned into `shadow-[inset_0px_1px_(--theme(--color-white/15%))]`
because no readability is gained when it's part of a normal space
separated list
- `m-[--spacing(12.34)]` would not be turned into
`m-[(--spacing(12.34))]` because there is nothing else it can conflict
with
- `m-[calc(--spacing(12.34)*2)]` would not be turned into
`m-[calc((--spacing(12.34))*2)]` because it's the first argument and
doesn't conflict with the `*`
- `m-[min(100%,--spacing(12.34))]` would not be turned into
`m-[min(100%,(--spacing(12.34)))]` because a `,` doesn't cause
readability issues
Fixes:
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1544
## Test plan
1. Added new tests to ensure the bug is fixed
2. Added new tests to ensure readability is improved (and not degraged)
when whitespace was used to improve readability
## Summary
`@tailwindcss/postcss` derives `inputBasePath` from `result.opts.from`:
```ts
let inputFile = result.opts.from ?? ''
let inputBasePath = path.dirname(path.resolve(inputFile))
```
When PostCSS calls the plugin without `from` (some bundlers, including
Turbopack, do this for certain CSS inputs), `inputFile` is `''`,
`path.resolve('')` returns `process.cwd()`, and `path.dirname(...)`
therefore returns the **parent of CWD**. The downstream `compileAst({
base: inputBasePath })` call then asks the resolver to find
`tailwindcss` from one level above the project root, which fails with:
```
Can't resolve 'tailwindcss' in '<parent of CWD>'
```
The plugin already computes `base = opts.base ?? process.cwd()` near the
top. Reusing that as the fallback gives a sensible default (CWD) and
respects an explicit `opts.base` when set.
```diff
-let inputBasePath = path.dirname(path.resolve(inputFile))
+let inputBasePath = inputFile
+ ? path.dirname(path.resolve(inputFile))
+ : base
```
## Test plan
Added a test in `packages/@tailwindcss-postcss/src/index.test.ts` that
processes `@import 'tailwindcss'` via `processor.process(input)` with no
`from` option. Before the fix, this throws `Error: Can't resolve
'tailwindcss' in '<parent of CWD>'`; after the fix, the import resolves
and the processor returns non-empty CSS.
I wasn't able to run the suite locally — `pnpm build` requires `cargo`
for `@tailwindcss/oxide` and I don't have a Rust toolchain set up — so
the test has been written to match existing conventions in
`index.test.ts` (vitest, plain
`postcss([tailwindcss({...})]).process(...)`), and I'm relying on CI to
verify.
---------
Co-authored-by: rebasecase <rebasecase@localhost>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR bumps dependencies in all the packages, typically just bumping
to the latest patch release.
Closes: #19936Closes: #19917Closes: #19899Closes: #19897Closes: #19845Closes: #19832Closes: #19967Closes: #19968
## Test plan
1. All tests still pass
2. All integration tests still pass
[ci-all] to verify Linux, Windows and macOS
Fixes#19964
CSS files imported via JS that only use `@variant` (no `@apply`,
`theme()`, or utility classes) are silently skipped by the Vite plugin.
The `@variant` directive gets passed through raw to the browser, which
drops it as an unknown at-rule.
**Root cause** — `Features.Variants` is missing from the [feature
detection
bitmask](https://github.com/tailwindlabs/tailwindcss/blob/v4.2.4/packages/%40tailwindcss-vite/src/index.ts#L526):
```ts
// before
Features.AtApply | Features.JsPluginCompat | Features.ThemeFunction | Features.Utilities
// after
Features.AtApply | Features.JsPluginCompat | Features.ThemeFunction | Features.Utilities | Features.Variants
```
**Reproduction** — https://github.com/remorses/tailwind-variant-bug
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR fixes an issue where resolving of certain CSS or JS files
results in the wrong paths. The issue happens if you have a setup where
a relative file path _also_ exists in the parent folder:
```css
/* src/foo.css */
.foo-in-root {}
/* src/theme/a.css */
@import "./foo.css"; /* This resolved to the file above, instead of the file below */
/* src/theme/foo.css */
.foo-in-theme {}
```
This happened because we resolved relative to a `base` folder, but Vite
expects an `importer` instead. The difference is subtle, but they expect
a file. On that file they use `let base = path.dirname(importer)` to get
a base path out themselves.
This in turn means that if you pass in a folder, you get this:
```js
path.dirname('/path/to/my-project') // /path/to
```
If we gave it a proper file, then we get the proper base path
```js
path.dirname('/path/to/my-project/index.css') // /path/to/my-project
```
I'm actually surprised that this didn't cause issues earlier... but it
did result in error since we recently started resolving files using
Vite's `aliasOnly: true` feature such taht Vite aliases work as well.
With this change, we now make sure that:
1. We use a proper `importer` instead of the `base` path
2. We refactor the resolving logic such that we try with `aliasOnly:
true` first, then `aliasOnly: false`
We also still ensure that in the CSS resolver we expect a `.css` file,
and in the JS resolver we _don't_ expect a `.css` file (which can happen
if a `"browser": "./dist/index.css"` field in package.json points to a
CSS file, daisyUI does this for example).
Fixes: #19956
## Test plan
1. Added additional (failing) integration tests to reproduce the linked
issue
2. Existing integration tests pass
While the build still worked, the linked issue resulted in a much bigger
file size because the wrong .css files were included. With this fix, the
number is correct again:
<img width="1234" height="1037" alt="image"
src="https://github.com/user-attachments/assets/fdff803c-0db2-4066-92fa-064c2816b35c"
/>
Since this is touching code related to previous PRs, I wanted to
manually make sure that these still work as expected:
- https://github.com/tailwindlabs/tailwindcss/issues/19950: This one is
about daisyUI and the `.css` file referenced in the package.json's
`"browser"` field: <img width="782" height="1229" alt="image"
src="https://github.com/user-attachments/assets/fb7b9d3b-527b-414b-94b0-2be7e96056f1"
/>
- https://github.com/tailwindlabs/tailwindcss/issues/19946: This one is
about the Vite alias being just a single `@` causing issues with
`@plugin "@tailwindcss/typography";` for example: <img width="1694"
height="1856" alt="image"
src="https://github.com/user-attachments/assets/d1935ecf-e80a-4945-a69a-ec3c5223efab"
/>
[ci-all]
Edit: some edits by @RobinMalfait
---
## Summary
Fix a regression in `@tailwindcss/vite` introduced by `#19803` where JS
plugin resolution could incorrectly resolve a package to its `browser`
CSS entry.
In cases like `daisyui`, Vite can resolve `@plugin "daisyui"` to
`daisyui.css` instead of the package's JS entry, which causes Tailwind
to try to load a CSS file as a JS plugin and fail with:
```txt
Unknown file extension ".css"
```
This change keeps the `aliasOnly: false` behavior from `#19803` so
tsconfig path resolution still works, but adds a JS-entry guard to
`customJsResolver` in `@tailwindcss/vite`. If Vite resolves a plugin
request to a non-JS file like `.css`, the custom resolver now returns
`undefined` so Tailwind's internal fallback resolver can resolve the
package as a JS plugin entry instead.
I also added integration coverage for a package whose `main`/`module`
points to JS while `browser` points to CSS, and verified that `@plugin
"pkg"` still resolves to the JS entry in both build and dev mode.
## Test plan
Added new integration tests in `integrations/vite/resolvers.test.ts`
covering a package with:
- `main` / `module` -> JS
- `browser` -> CSS
- `@plugin "pkg"` -> should resolve to JS, not CSS
Verified with:
```sh
pnpm test:integrations vite/resolvers.test.ts -t "browser points to CSS"
pnpm test:integrations vite/resolvers.test.ts -t "resolves tsconfig paths"
```
These verify that:
- `@plugin` no longer resolves to a CSS browser entry
- the original tsconfig paths fix from `#19803` still works in both
build and dev mode
---
Maintainer edits:
Instead of hardcoding file extensions, first try to resolve aliases and
then fallback to the default resolving system we had before. We still
check for a `.css` extension, even in the JS resolver because some
dependencies (like `daisyUI`) put the CSS file there instead of in an
`exports.style`. If we detect that, we still fallback to the default
resolving logic.
This should be compatible with the original issue we were trying to fix
where we wanted to make Vite aliases work.
Fixes: #19950
[ci-all]
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR fixes an issue when using `@tailwindcss/vite` and you're trying
to resolve paths. In the latest 4.2.3 release, we added support for
following the vite `aliases` option.
However, some people run into issues because if you just use `@` as an
alias then using `@tailwindcss/typography` wouldn't resolve because it's
a package, and not something local.
With this PR we fix that by making sure that Vite's resolver can
actually resolve to an absolute path. If not, then we fallback to the
resolving that happens in `@tailwindcss/node`.
Fixes: #19946
## Test plan
1. Added an integration test that reproduces this issue, and is now
solved
2. Tested it on a reproduction provided in the corresponding issue
Before:
<img width="1694" height="1856" alt="image"
src="https://github.com/user-attachments/assets/c50f7104-78b3-476d-9e60-0c83e0976a5c"
/>
After:
<img width="1694" height="1856" alt="image"
src="https://github.com/user-attachments/assets/44ea0e3b-f60f-479f-a4a1-203a6230475b"
/>
<!--
👋 Hey, thanks for your interest in contributing to Tailwind!
**Please ask first before starting work on any significant new
features.**
It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.
For more info, check out the contributing guide:
https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md
-->
## Summary
<!--
Provide a summary of the issue and the changes you're making. How does
your change solve the problem?
-->
Removed a duplicated optimize: { minify: false } example from
@tailwindcss/postcss README (doc-only, one file).
## Test plan
<!--
Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.
-->
Check the duplicate block is removed, the earlier optimize example still
exists, and only packages/@tailwindcss-postcss/README.md changed.
References https://github.com/tailwindlabs/tailwindcss/pull/19391.
References https://github.com/tailwindlabs/tailwindcss/pull/16274.
Right now, when using the standalone build of the TailwindCSS CLI, you
cannot use a custom `NODE_PATH`, but you can when using it via Node.js
directly.
A custom NODE_PATH allows you to resolve imports from multiple
locations. For example, in [Phoenix
LiveView](https://github.com/phoenixframework/phoenix_live_view/), we
have a feature where you can write scripts in templates that we extract
at compile time to a custom folder and users can import those in their
application bundle by saying
```javascript
import { hooks as colocatedHooks } from "phoenix-colocated/my_app"
```
where the "phoenix-colocated" folder lives in a different location than
the usual `node_modules` folder. This works fine with the default
esbuild setup, as it respects `NODE_PATH`, so we can pass it a custom
location.
We want to also support colocating CSS in templates soon, but the same
approach doesn't work with the standalone Tailwind CLI we ship with
default Phoenix projects. It works when running Tailwind through
Node.js, but we don't want to tell users they need to install it, just
to use the feature.
This patch changes the lookup logic for the standalone CLI to also
account for `NODE_PATH`. Note that you can pass multiple paths, that are
split according the the OS PATH separator.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
<!--
👋 Hey, thanks for your interest in contributing to Tailwind!
**Please ask first before starting work on any significant new
features.**
It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.
For more info, check out the contributing guide:
https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md
-->
## Summary
Fix a typo in the @tailwindcss/postcss README by changing Lighting CSS
to Lightning CSS in the optimize option documentation.
<!--
Provide a summary of the issue and the changes you're making. How does
your change solve the problem?
-->
## Test plan
Verified the README now says Lightning CSS and that the diff is
docs-only.
<!--
Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.
-->
This PR adds a few more canonicalizations for some cases I noticed on
our templates.
When dealing with arbitrary values, and the utility is a "negative"
utility, then we will try to put the `-` inside of the arbitrary value:
```diff
- -left-[9rem]
+ left-[-9rem]
```
The idea is that the arbitrary value is already an escape hatch for when
a value is not available by default. The `-` in front uses an implicit
`calc(<expression> * -1)` which might be confusion if you have an value
like this already.
This also can allow for some further optimizations. For example
```diff
- -mt-[492px]
↓↓↓↓↓↓↓ Into a simpler arbitrary value
+ mt-[-492px]
↓↓↓↓↓↓↓ Into a bare value
+ mt-123
```
This PR also improve the constant folding of calc expressions a bit more
such that nested calc expressions with 2 constants and an unknown can be
folded. Bit of a mouthful, but it allows us to handle this:
```diff
- mt-[calc(-1*calc(-1*var(--foo)))]
↓↓↓↓↓↓↓ The -1 * -1 becomes a no-op
+ mt-[var(--foo)]
↓↓↓↓↓↓↓ Into the shorthand for CSS variables
+ mt-(--foo)
```
Now that we can handle moving the `-` into the arbitrary value, there
are also cases where we can get the `-` _out_ of the arbitrary value:
```diff
- mt-[calc(-1*var(--foo))]
↓↓↓↓↓↓↓ Simplify calc, move `-` to the front
+ -mt-[var(--foo)]
↓↓↓↓↓↓↓ Into the shorthand for CSS variables
+ -mt-(--foo)
```
Another missing piece that this PR adds is the concept of canonicalizing
or normalizing calc expressions. This is a separate step used when
calculating the signature for each utility. This allows us to normalize
`calc(-1*var(--foo))` and `calc(var(--foo)*-1)`. Without this they would
not be considered the same, but not it will.
It's only used when comparing values, it won't unify the actual
arbitrary values with this logic (at least for now).
With the additional constant folding logic and the canonicalization when
comparing signatures it unlocks the necessary power to perform the above
transformations.
## Test plan
1. Existing tests still pass
2. Added additional tests for the constant folding logic
3. Added tests for the canonicalization of calc expressions
4. Added new tests where we move the `-` inside the value, or move the
`-` outside of the arbitrary value.
This are getting a little bit out of hand here, so this is an initial
refactor.
## Test plan
1. All tests are still there
2. All tests are still passing
This PR adds more canonicalization rules for deprecated utilities.
| Before | After |
| --- | --- |
| `overflow-ellipsis` | `text-ellipsis` |
| `start-full` | `inset-s-full` |
| `-start-full` | `-inset-s-full` |
| `start-auto` | `inset-s-auto` |
| `start-px` | `inset-s-px` |
| `-start-px` | `-inset-s-px` |
| `start-8` | `inset-s-8` |
| `-start-8` | `-inset-s-8` |
| `start-123` | `inset-s-123` |
| `-start-123` | `-inset-s-123` |
| `end-full` | `inset-e-full` |
| `-end-full` | `-inset-e-full` |
| `end-auto` | `inset-e-auto` |
| `end-px` | `inset-e-px` |
| `-end-px` | `-inset-e-px` |
| `end-8` | `inset-e-8` |
| `-end-8` | `-inset-e-8` |
| `end-123` | `inset-e-123` |
| `-end-123` | `-inset-e-123` |
In a few cases we already had canonicalization rules, for example
`start-8` where `8` is one of the default suggested spacing scale
values. But this now adds support for positive and negative values that
exceed the default suggested spacing scale as well as some keywords.
## Test plan
1. Existing tests pass
2. Added new tests to ensure these canonicalizations work
This PR is an attempt to make the upgrade tooling more stable.
### TL;DR
1. When migrating from Tailwind CSS v3 → Tailwind CSS v4, only migrate
files listed in the `config.content` instead of relying on v4's auto
content detection feature
2. Skip writing files that have not been changed
3. Write changed files in a safe way: first write to a temporary file,
then rename the file atomically
4. Never migrate files that are git ignored, even if they are listed in
the `config.content` file
5. Always ignore `.env` and `.env.*` files when scanning for files. Most
people will have this in their `.gitignore` file, but if not, then this
is a fallback mechanism.
---
Looking at the #18972 issue, it looks like some people are running into
weird situations where some of the contents is just gone.
I have never been able to reproduce this on my own devices and in my own
projects unfortunately. But there is definitely _something_ happening
that's not right that people are running into.
Therefore, this PR is an attempt to fix what I think _might_ be wrong,
but I'm not 100% sure if these fixes are enough, or if something else is
still happening here.
This builds on top of the #19779 PR which has some small fixes, but is
incomplete to make this work.
### What's happening
Looking at some of the comments, it looks like a few things are
happening such as:
1. The upgrade tool is emptying out my files — it looks like these are
only happening if you ctrl+c while the process is taking a while. It
could be that a lot of files are being checked and therefore the tooling
looks like its stuck.
6. The upgrade tool is looking at files it shouldn't look at — in
Tailwind CSS v4 we have this concept of the auto-content detection. This
means that we will look at any plain text file that is not git ignored.
### Fixes
#### Emptying out files
The files being emptied looks like it's because how `fs.writeFile`
behaves by default. It opens the file handle with the `w` flag, which
will first truncate the file before writing the new contents. This is
not a single atomic operation, so a killed process in the middle will
cause invalid state.
When we migrate your template files, everything is happening in promises
to migrate things at the same time. When a lot of files are being
scanned, truncating might have happened already before we write the new
content. Since we migrate a bunch of files in parallel, a ctrl+c could
cause data loss in multiple files.
To mitigate this, I switched to an alternative way of writing files.
1. First, we do some quick checks where if the contents didn't change we
just bail out immediately. Files that don't include Tailwind CSS classes
won't change, and therefore we don't need to override these files with
the same contents.
2. When the migrated contents is empty, we bail out as well. I'm 100%
sure that this is not the spot where the "emptying out" happens, I still
believe it happens in the `writeFile` itself, but added it just in case.
7. Next, I introduced a safe write, where we first write to a temporary
file in the same folder. We could write it to `/tmp`, but then we can't
guarantee that we are on the same file system.
If we ctrl+c at this stage, then the worst case scenario is that you
have additional temporary files in your project, but your original files
are still there.
Once that file was written, we will use the atomic `fs.rename`. This
should be atomic as long as we are on the same file system, so either
the rename didn't happen yet, or it completed.
I added an integration test for this, but I had to change the
`writeFile` implementation slightly. In the test, we will truncate the
file first, after that we will write the new contents. This is so that
we have enough time to kill the current process and allows us to verify
that we didn't clear out the file. Again, this is a hacky way of testing
this, just because I can't reproduce this issue myself, let alone
reproduce it reliable in a CI environment.
Note: we are also using `realpath` to make sure that we are updating the
real file. Otherwise, if we were dealing with a symlinked file, we would
override the symlink with a "hard" copy instead.
#### Touching files that should not be touched
During the migration, we rely on the Tailwind CSS v4 auto detection
logic which means that it will scan any plain text file that is not git
ignored. Therefore changes to php files could happen because in theory
they could contain Tailwind CSS classes.
To solve this, when migrating from Tailwind CSS v3 to Tailwind CSS v4,
we will _only_ take the sources into account that were listed in the
`config.content` array. Since this was a requirement in Tailwind CSS v3,
it should be safe to rely on this array.
Additionally, this will make sure that we are dealing with way fewer
files to migrate as well.
On top of that, files that match the patterns in the content array that
are git ignored will also be skipped. This is to prevent that we mutate
files in `node_modules` for example.
In one of the comments I read that `.env` files were emptied out. In
most cases people will have these files gitignored but I explicitly
added `.env` and `.env.*` as files to never ever touch by default when
scanning.
Last but not least, this also updates the output a little bit of the
upgrade tool in case we skip content files (because of git ignore) and
if we changed a file.
<img width="1122" height="1376" alt="image"
src="https://github.com/user-attachments/assets/318fdbbf-e319-4c7e-9648-ee9283842624"
/>
- "Git ignored folder, skipping: `./node_modules`": this is because the
content array looks like this while the `node_modules` are being
ignored:
<img width="1090" height="398" alt="image"
src="https://github.com/user-attachments/assets/7d694720-5671-47ec-bb2e-f24c5f2c4248"
/>
- "Migrated
`./resources/views/vendor/filament-panels/components/logo.blade.php`":
this is because **I** made a change to showcase this feature.
Fixes: #18972Closes: #19779
### Test plan
1. Existing tests still pass
1. Added a dedicated integration test to ensure that we only take
`config.content` into account when migrating from Tailwind CSS v3 to
Tailwind CSS v4 projects.
1. Added a dedicated integration test to make sure that files listed in
`config.content` that are also git ignored, will still be skipped.
1. Added a dedicated integration test to ensure that when `writeFile` is
cancelled mid-write that our old files are still present.
1. Added a dedicated integration test to ensure that we ignore `.env`
and `.env.*` files even if you didn't git ignore them.
[ci-all] To verify on Windows
---------
Co-authored-by: Sami <sychocouldy@gmail.com>
This PR fixes an issue where `placeholder-*` utilities were reading
values from `--background-color` instead of `--placeholder-color`. In
Tailwind CSS v3, we read from `placeholderColor` which is why we should
use `--placeholder-color` here as well.
f38be227df/src/corePlugins.js (L2317)
That said, this is technically a breaking change in case somebody relies
on `--background-color` for `placeholder` values. But since this is text
related, and most people will rely on the default `--color` values
instead, I think it's safe to change this as-is.
In the unlikely event that somebody _does_ rely on this, then we have 2
options:
1. Guide them to make use of `--placeholder-color` instead (preferred
solution)
2. Re-add `--background-color` after the `--placeholder-color` (band-aid
solution, but might be worth it who knows)
Fixes: #19838
## Test plan
1. Existing tests still pass
2. Verified in the Tailwind CSS v3 codebase that we did read from
`placeholderColor` which in turn reads from `color` by default. Which is
equivalent to `--placeholder-color` and `--color` in Tailwind CSS v4.
This PR adds more declaration expansions such that we can collapse more
utilities.
While testing #19837 I noticed that in my tests some utilities weren't
canonicalized correctly. As part of that PR, we check for
`parsedCandidate.value === null`, which means that a functional utility
without a value is skipped. We do have utilities like that such as
`border` (which is equivalent to `border-1`). But while testing, I
noticed that `border-x border-y` should collapse to `border` but they
didn't. This PR fixes that.
By expanding these properties to their long-form physical properties
(instead of the shorter logical properties) we make the signatures of
utilities a bit bigger, but also more correct such that we can collapse
the physical form into logical utilities.
To make this more concrete, this PR allows for the following
canonicalizations now:
| Input | Output |
| --- | --- |
| `border-t-123 border-r-123 border-b-123 border-l-123` | `border-123` |
| `border-t-1 border-r-1 border-b-1 border-l-1` | `border` |
| `border-t-123 border-b-123` | `border-y-123` |
| `border-l-123 border-r-123` | `border-x-123` |
| `border-t-red-500 border-r-red-500 border-b-red-500 border-l-red-500`
| `border-red-500` |
| `border-t-red-500 border-b-red-500` | `border-y-red-500` |
| `border-l-red-500 border-r-red-500` | `border-x-red-500` |
| `scroll-mt-123 scroll-mr-123 scroll-mb-123 scroll-ml-123` |
`scroll-m-123` |
| `scroll-mt-123 scroll-mb-123` | `scroll-my-123` |
| `scroll-ml-123 scroll-mr-123` | `scroll-mx-123` |
| `scroll-pt-123 scroll-pr-123 scroll-pb-123 scroll-pl-123` |
`scroll-p-123` |
| `scroll-pt-123 scroll-pb-123` | `scroll-py-123` |
| `scroll-pl-123 scroll-pr-123` | `scroll-px-123` |
| `overflow-x-hidden overflow-y-hidden` | `overflow-hidden` |
| `overscroll-x-contain overscroll-y-contain` | `overscroll-contain` |
## Test plan
1. Existing tests pass
2. Added a few more tests to verify that these canonicalizations work
The guard on `dynamicUtilities` restricted root-swapping to named
values, so arbitrary values like `px-[1.2rem] py-[1.2rem]` were never
collapsed into `p-[1.2rem]`.
This is what caused #19835 — in `--stream` mode, the collapse only
happened if an earlier line caused the shorthand to be registered in
`STATIC_UTILITIES_KEY` as a side effect, making the output
non-deterministic. The underlying issue is that arbitrary value collapse
wasn't supported at all.
The fix relaxes the guard from `parsedCandidate.value?.kind !== 'named'`
to `parsedCandidate.value === null`. `cloneCandidate` and
`printCandidate` already handle arbitrary values, so the root-swapping
machinery works without other changes.
The iteration in `dynamicUtilities` is over
`designSystem.utilities.keys('functional')` — a fixed set of roots, not
input-proportional — so the performance cost of including arbitrary
values should be negligible.
Fixes#19835.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
<!--
👋 Hey, thanks for your interest in contributing to Tailwind!
**Please ask first before starting work on any significant new
features.**
It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.
For more info, check out the contributing guide:
https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md
-->
## Summary
<!--
Provide a summary of the issue and the changes you're making. How does
your change solve the problem?
-->
`@tailwindcss/webpack` currently uses `this.resourcePath` as the cache
key, which ignores the resource query. When the same CSS file is
imported multiple times with different `resourceQuery` values, all of
those imports share a single `CacheEntry`. That means the utilities
discovered for one entry can leak into the CSS output for another entry.
This PR changes the cache key to use `this.resource` (path + query)
instead, while still using `this.resourcePath` for all filesystem work.
## Test plan
<!--
Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.
-->
- `pnpm test:integrations -- webpack/loader.test.ts`
- Confirms all existing webpack loader integration tests pass.
- Confirms the new `@tailwindcss/webpack loader isolates cache by
resource including query` test passes, verifying that two entries
importing the same CSS file with different queries produce isolated
outputs (`dist/a.css` only contains `only-a` / `--color-red-500`, and
`dist/b.css` only contains `only-b` / `--color-blue-500`).
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Change `aliasOnly` from `true` to `false` when calling Vite's resolver
so that the full resolution pipeline runs, including the oxc resolver
responsible for tsconfig path resolution.
When `aliasOnly` was `true`, only the @rollup/plugin-alias plugin ran,
which meant `resolve.tsconfigPaths: true` had no effect on CSS `@import`
or JS `@plugin` resolution in `@tailwindcss/vite`.
Closes#19802.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR fixes an issue where the compiler can crash if it encounters an
invalid codepoint.
When we extract potential candidates from files, it could be that we
encounter values that look like a class or a CSS variable, if it turns
out that it's an invalid CSS variable we can ignore it.
The problem is that sometimes there are escaped values in there that
result in invalid code points crashing the compiler.
This PR fixes that by gracefully handling that and making sure that
invalid code points are replaced by `\uFFFD` as per the spec.
The bug report
(https://github.com/tailwindlabs/tailwindcss/issues/19786) has a clean
example where a piece of text looks like a CSS variable, but contains
invalid code points.
```
--Coding-Projects-CharacterMapper-Master-Workspace\d8819554-4725-4235-9d22-2d0ed572e924
```
Luckily we can fix this today by ignoring the file paths that contain
these strings using `@source not "…";`, but the better way is to
actually fix this.
To solve this, instead of blindly passing numbers to
`String.fromCodePoint`, we will first validate whether it's a valid
codepoint:
1. `0x0000` — `0x10FFFF` (inclusive) is the range of valid code points.
See: https://infra.spec.whatwg.org/#code-point
2. `0xD800` — `0xDBFF` (inclusive) are leading surrogates. See:
https://infra.spec.whatwg.org/#leading-surrogate
3. `0xDC00` — `0xDFFF` (inclusive) are trailing surrogates. See:
https://infra.spec.whatwg.org/#trailing-surrogate
In the code we use the `0xD800` — `0xDFFF` range because the ranges
overlap.
There are various references in the spec to replace surrogates (and
invalid codepoints) with `\uFFFD`. Here is one of them:
https://drafts.csswg.org/css-syntax-3/#consume-escaped-code-point
Fixes: https://github.com/tailwindlabs/tailwindcss/issues/19786Fixes: #19801 (this issue talks about a similar invalid code point
issue)
## Test plan
1. Added a regression test where the above string was used as a CSS
variable
2. Added a regression test for the unescape functionality to make sure
that invalid code points and surrogates are replaced by the `\uFFFD`
replacement character.
[ci-all] Just to verify on Windows as well
This PR adds support for canonicalizations for `tracking-*` utilities.
This one is a bit of a funny one, if you take a look at the linked
issue, there is a beautiful table:
| Utility Name | Value | Arbitrary Value | Throws Suggestion |
| - | -: | - | - |
| tracking-tighter | -0.05em | tracking-[-0.05em] | ✗ |
| tracking-tight | -0.025em | tracking-[-0.025em] | ✗ |
| tracking-normal | 0em | tracking-[0em] | ✗ |
| tracking-wide | 0.025em | tracking-[0.025em] | ✗ |
| tracking-wider | 0.05em | tracking-[0.05em] | ✗ |
| tracking-widest | 0.1em | tracking-[0.1em] | ✓ |
It doesn't really make sense to _why_ only the `tracking-widest` one is
properly suggested here. Until you look a little bit closer.
Turns out that `-tracking-tighter` is equivalent to `tracking-wider`,
`-tracking-tight` is equivalent to `tracking-wide` and so on.
The way the canonicalization works internally is by generating a
signature for a given utility class. If two utilities have the exact
same signature, we can consider them the same. In this case
`tracking-widest` and `tracking-[0.1em]` have the same signature.
One of the rules we have internally is that if we find more than one
replacement utility then we don't really know what to do, so we bail.
Because if you get `foo` or `bar`, which one do you pick?
If we refer to this above table again, the moment we want to
canonicalize the `tracking-[-0.05em]` we get two suggestions:
`tracking-tighter` and `-tracking-wider`, since we don't know what to
do, we bail and we don't suggest anything.
So the reason that `tracking-widest` _was_ suggested is just because we
don't have a `-tracking-tightest`.
How do we fix this? Well, since we have `tracking-*` and `-tracking-*`
utilities, I wanted to deprecate the `-tracking-*` ones for named
utilities (where the values come from your theme) because that doesn't
really make sense.
However, we have this exact pattern documented here:
https://tailwindcss.com/docs/letter-spacing#using-negative-values Which
means that I can't just deprecate those utilities.
<img width="723" height="511" alt="image"
src="https://github.com/user-attachments/assets/164b659b-abe9-4f6e-a176-701dd7ea505a"
/>
Instead, I added a different rule which says that if you get multiple
possible replacements, then we prefer the "positive" one, the one
without the `-`. Also added some additional checks to make sure that if
you get `foo`, `-bar`, `baz`, that we also bail because we know that we
should prefer `foo` or `baz` over `-bar`, but we don't know if we should
pick `foo` or `baz`...
This additional rule does solve the original issue, and we already
prefer possible values over negative values in other places (related to
bare values).
Fixes:
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1558
## Test plan
1. Existing tests pass
2. Added regression tests to make sure that the table from above _does_
get canonicalized correctly into the expected values.
This PR bumps Lightning CSS. The `node/index.js` file in the package
changed, so we had to update the patched version of it as well.
We also had to update the patches for `@parcel/watcher` and
`lightningcss` now that they ship with `detect-libc@2`. Unfortunately,
we still require the patches because we also need to take the
`process.env.PLATFORM_LIBC` into account. Another issue is that bun
needs to be able to statically analyze the `require(…)` calls, so using
the nice `parts.push` approach doesn't really work here.
# Test plan
1. Existing tests should pass
2. Updated some tests to reflect the changes in the generated CSS based
on the version bump.
[ci-all]
This PR fixes a bug in the canonicalization process where if a few
utilities collapse into a smaller one, and the smaller one is part of
the original list, then it results in an empty list.
It will be more clear with an example. Let's say you have this setup:
```
w-[calc(1rem+0.25rem)] h-[calc(1rem+0.25rem)] size-5
```
The first step is that this will result in:
```
w-5 h-5 size-5
```
Then the `w-5 h-5` can turn into `size-5`. But the existing `size-5`,
can also be replaced by the `size-5`.
Internally, when we have a replacement, then we mark all the classes
that can be replaced as "droppable", so they would be dropped from the
list. But in this scenario we also marked `size-5` as droppable,
resulting in an empty list.
If an additional class existed:
```
w-[calc(1rem+0.25rem)] h-[calc(1rem+0.25rem)] size-5 flex
```
The result would be
```
flex
```
Instead of the expected:
```
size-5 flex
```
## Test plan
1. Existing tests pass
2. Added new tests with and without an additional class
This PR adds support for canonicalization of utilities that accept bare
values and exceed the default spacing scale we use for intellisense.
Right now, all utilities are behind functions, so the only way to know
whether something compiles is by compiling a candidate, e.g. `w-8` and
passing it to the utility functions. To help us, we use the intellisense
APIs that we use for suggestions.
Most utilities that accept bare values, have suggestions up until
`*-96`, so `w-96 h-96` would be canonicalized to `size-96`. But the
moment we exceed that, the result stays as-is.
```
→ w-96 h-96
= size-96
→ w-1234 h-1234
= h-1234 w-1234
```
This PR ensures that the last scenario also gets canonicalized to
`size-1234` instead of staying as `h-1234 w-1234`.
```
→ w-96 h-96
= size-96
→ w-1234 h-1234
= size-1234
```
## Test plan
1. Existing tests pass
2. Added new tests for utilities with bare values
[ci-all] just to see if this additional logic doesn't cause timeouts in
CI for WIndows. In my testing this doesn't have a significant impact on
performance at all.
## Summary
- Adds `--stream` flag to `tailwindcss canonicalize` that reads
candidate groups from stdin line by line and writes canonicalized
results to stdout
- Keeps the design system loaded across requests, making it suitable as
a long-running sidecar process
- Empty lines pass through, keeping request/response pairs aligned
## Motivation
Non-JS tools (formatters, editor plugins, etc.) currently have no
lightweight way to canonicalize Tailwind classes. The existing batch
mode works for one-off use, but tools that need to canonicalize
repeatedly pay the cost of loading the design system each time.
With `--stream`, a tool can start `tailwindcss canonicalize --stream`
once and send candidate groups over stdin as needed:
```sh
$ echo -e "py-3 p-1 px-3\nmt-2 mr-2 mb-2 ml-2" | tailwindcss canonicalize --stream
p-3
m-2
```
Related discussion:
https://github.com/tailwindlabs/tailwindcss/discussions/19736
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Removes unused braces and @types/braces from @tailwindcss/upgrade
package.json as reported in issue #19794
---------
Co-authored-by: CG1AI <sidwu@CG1AIdeMac-mini.local>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR does some generic cleanup to the codebase.
I'm playing with oxfmt and oxlint and noticed some unnecessary escapes.
Might add these dependencies to the project later (and rolldown for
building). But baby steps for now.
## Test plan
1. All tests should still pass
This PR adds support for Vite 8 when using the `@tailwindcss/vite`
package.
From the package's perspective, not a lot had to change, just the `vite`
peer dependency now has an additional `^8.0.0` version range.
Closes: #19789
## Test plan
1. Existing tests pass
2. Manually tested in the `./playgrounds/vite` playground
3. Added Vite 8 integration tests to verify that the plugin works with
Vite 8
<!--
👋 Hey, thanks for your interest in contributing to Tailwind!
**Please ask first before starting work on any significant new
features.**
It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.
For more info, check out the contributing guide:
https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md
-->
## Summary
- Closes https://github.com/tailwindlabs/tailwindcss/issues/19744
- Closes https://github.com/vitejs/vite-plugin-react/issues/1118
- Closes https://github.com/wakujs/waku/issues/1963
The change in https://github.com/tailwindlabs/tailwindcss/pull/19670
didn't take account for server only modules managed by SSR framework.
Forcing full reload for this path breaks server HMR. This PR added a
check to determine whether the same modified file has associated modules
in a different environment module graph to avoid this.
## Test plan
<!--
Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.
-->
Added an integration test for React router HDR (server loader hmr). This
test fails on main.
Also the local build is tested on `@vitejs/plugin-rsc` CI and confirmed
the fix https://github.com/vitejs/vite-plugin-react/pull/1132
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR fixes an internal source map warning when running tests. To
solve this, we encode the `=` as `\x3d` instead. I'm not 100% sure if
Vitest was hanging on this but it solved the following warning:
```
✓ @tailwindcss/cli src/utils/format-ns.test.ts (21 tests) 3ms
11:43:59 AM [vite] (ssr) Failed to load source map for /Users/robin/github.com/tailwindlabs/tailwindcss/packages/@tailwindcss-node/dist/index.mjs.
Error: An error occurred while trying to read the map file at ${i}
Error: ENOENT: no such file or directory, open '/Users/robin/github.com/tailwindlabs/tailwindcss/packages/@tailwindcss-node/dist/${i}'
at open (node:internal/fs/promises:634:25)
at Object.readFile (node:internal/fs/promises:1238:14)
at extractSourcemapFromFile (file:///Users/robin/github.com/tailwindlabs/tailwindcss/node_modules/.pnpm/vite@7.0.0_@types+node@20.19.1_jiti@2.6.1_lightningcss@1.31.1_patch_hash=tzyxy3asfxcqc7ihroou_7epcep7uhfc7zidzdmxzgvdzwi/node_modules/vite/dist/node/chunks/dep-Bsx9IwL8.js:8349:65)
at loadAndTransform (file:///Users/robin/github.com/tailwindlabs/tailwindcss/node_modules/.pnpm/vite@7.0.0_@types+node@20.19.1_jiti@2.6.1_lightningcss@1.31.1_patch_hash=tzyxy3asfxcqc7ihroou_7epcep7uhfc7zidzdmxzgvdzwi/node_modules/vite/dist/node/chunks/dep-Bsx9IwL8.js:26405:22)
```
## Test plan
1. Existing tests pass
2. Added a failing test to ensure that the source maps are emitted
correctly