mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
769 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
884f02c46b
|
Fix "Cannot read properties of undefined" crash on malformed arbitrary value (#18133)
This PR fixes a crash when an arbitrary value was malformed and crashed
the build.
If you have a utility like `[--btn-border:var(--color-maroon)/90)]`
which is malformed, it will crash the build. It might not be easy to
spot but the easy is the additional `)` after the `90`.
The reason this crashes is because we parse the value
`var(--color-maroon)/90)` and when we see `)` we assume it's the end of
a "function" which also assumes it was preceded by a `(`. This is not
the case and we crash.
This PR fixes that by not assuming the parsed object is available and
uses `?` to be safe and only access `nodes` if it's available.
I'm actually not 100% sure what the best solution is in this scenario
because these candidates could (and will) be returned from Oxide so even
if we throw a more descriptive error, it will still crash the build and
you might not even have control over the candidate.
This candidate will now eventually generate the following CSS:
```css
.\[--btn-border\:var\(--color-maroon\)\/90\)\] {
--btn-border: var(--color-maroon) / ;
}
```
Which still looks odd, but even Lightning CSS doesn't throw an error in
this case (because it's a CSS variable definition), so I think it's the
best we can do. If you open your devtools you will see the weird values,
so it's still debug-able.
<img width="359" alt="image"
src="https://github.com/user-attachments/assets/2eb48662-64de-4417-a2da-1577bf9075b5"
/>
Fixes: #17064
## Test plan
Manually tested the candidate that crashed it, and after the change
generated the above CSS. Then used it in JSFiddle to proof it's fixed
now. https://jsfiddle.net/z850ykew/
Couldn't use Tailwind Play because the candidate will cause a crash
there as well 😅
|
||
|
|
9cb38993d2
|
Upgrade deprecated order-none to order-0 (#18126)
This PR adds an initial version for deprecated utilities. Right now it's hardcoded to just the `order-none` utility. This means that `order-0` and `order-[0]` will not be migrated to `order-none` anymore. We did that automatically because we prefer named utilities over bare values and arbitrary values. With this PR, `order-none` is ignored. Similarly, `order-none` will be migrated to `order-0` instead (defined in a separate migration for deprecated values). Made it a new migration instead of using the legacy migration because there all utilities still exist, but are defined differently (e.g.: `shadow`, `shadow-sm`, `shadow-xs`). This PR is also an initial version, it doesn't add any form of `deprecated` flag or feature on a per-utility implementation basis. This therefor has the side effect that if you have a custom `order-none` defined, that it will also be ignored during migrations. ## Test plan 1. Added tests to ensure the `order-0` is not migrated to `order-none` 2. Added tests to ensure `order-none` is migrated to `order-0` (if it's safe to do so, the signature is still computed to ensure the output is the same). 3. Ran this on the Tailwind Plus codebase and ensured that `order-0` is not migrated to `order-none` and that `order-none` is migrated to `order-0`. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
7013ca37ed
|
Update semver 7.7.1 → 7.7.2 (patch) (#18084)
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? #### ✳️ semver (7.7.1 → 7.7.2) · [Repo](https://github.com/npm/node-semver) · [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) <details> <summary>Release Notes</summary> <h4><a href="https://github.com/npm/node-semver/releases/tag/v7.7.2">7.7.2</a></h4> <blockquote><h2 dir="auto"> <a href="https://bounce.depfu.com/github.com/npm/node-semver/compare/v7.7.1...v7.7.2">7.7.2</a> (2025-05-12)</h2> <h3 dir="auto">Bug Fixes</h3> <ul dir="auto"> <li> <a href=" |
||
|
|
9df5ba78bf
|
Fix PostCSS crash when using toJSON() (#18083)
When I added source maps to PostCSS I mistakenly assumed that `.source`
on a node could be `undefined`. The comment above the property in
PostCSS says that `source` can be `undefined` but this is a commentary
on the value upon **access** not its expected value on **write**:
```ts
declare abstract class Node_ {
/**
* …
*
* The nodes that are created manually using the public APIs
* provided by PostCSS will have `source` undefined and
* will be absent in the source map.
*
* …
*/
source?: Node.Source
}
```
Rather, what these types mean is that *if the property exists* it must
be defined. But otherwise the property can be missing if a node has no
source location metadata. This generally wasn't a problem with the
string-returning APIs but the `toJSON()` API in PostCSS expects that
`source` is defined if present. This caused a crash because our license
comment doesn't have a source location.
I've addressed this by deleting the `source` property from the node if
source location data is not available.
Fixes #18082
ref https://github.com/parcel-bundler/parcel/issues/10161
|
||
|
|
a42251cc29
|
Improve performance of upgrade tool (#18068)
This PR improves the performance of the upgrade tool due to a regression introduced by https://github.com/tailwindlabs/tailwindcss/pull/18057 Essentially, we had to make sure that we are not in `<style>…</style>` tags because we don't want to migrate declarations in there such as `flex-shrink: 0;` The issue with this approach is that we checked _before_ the candidate if a `<style` cold be found and if we found an `</style>` tag after the candidate. We would basically do this check for every candidate that matches. Running this on our Tailwind UI codebase, this resulted in a bit of a slowdown: ```diff - Before: ~13s + After: ~5m 39s ``` ... quite the difference. This is because we have a snapshot file that contains ~650k lines of code. Looking for `<style>` and `</style>` tags in a file that large is expensive, especially if we do it a lot. I ran some numbers and that file contains ~1.8 million candidates. Anyway, this PR fixes that by doing a few things: 1. We will compute the `<style>` and `</style>` tag positions only once per file and cache it. This allows us to re-use this work for every candidate that needs it. 2. We track the positions, which means that we can simply check if a candidate's location is within any of 2 start and end tags. If so, we skip it. Running the numbers now gets us to: ```diff - Before: ~5m 39s + After: ~9s ``` Much better! --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
71fb9cdf59
|
Improve @tailwindcss/upgrade and pnpm workspaces support (#18065)
This PR fixes an issue where an error such as: <img width="1702" alt="image" src="https://github.com/user-attachments/assets/4e6f75c7-3182-4497-939e-96cff08c55ae" /> Will be thrown during the upgrade process. This can happen when you are using `pnpm` and your CSS file includes a `@import "tailwindcss";`. In this scenario, `tailwindcss` will be loaded from a shared `.pnpm` folder outside of the current working directory. In this case, we are also not interested in migrating _that_ file, but we also don't want the upgrade process to just crash. I didn't see an option to ignore errors like this, so wrapped it in a try/catch instead. It also fixes another issue where if you are using a pnpm workspace and run the upgrade tool from the root, then it throws you an error that you cannot add dependencies to the workspace root unless `-w` or `--workspace-root` flags are passed. For this, we disable the check entirely using the `--ignore-workspace-root-check` flag. If we always used the `--workspace-root` flag, then the dependencies would always be added to the root, regardless of where you are running the script from which is not what we want. ## Test plan Before: <img width="1816" alt="image" src="https://github.com/user-attachments/assets/78246876-3eb6-4539-a557-d3d366f1b3a3" /> After: <img width="1816" alt="image" src="https://github.com/user-attachments/assets/a65e4421-d7c5-4d83-b35d-934708543e25" /> Before: <img width="1816" alt="image" src="https://github.com/user-attachments/assets/53772661-2c4a-4212-84d9-a556a0ad320f" /> After: <img width="1816" alt="image" src="https://github.com/user-attachments/assets/5bfaf20e-34b8-44fd-9b59-e72d36738879" /> |
||
|
|
c7d368b3c4
|
Do not migrate declarations in <style> blocks (#18057)
This PR improves the upgrade tool by making sure that we don't migrate
CSS declarations in `<style>…</style>` blocks.
We do this by making sure that:
1. We detect a declaration, the current heuristic is that the candidate
is:
- Preceded by whitespace
- Followed by a colon and whitespace
```html
<style>
.foo {
flex-shrink: 0;
^ ^^
}
</style>
```
2. We are in a `<style>…</style>` block
```html
<style>
^^^^^^
.foo {
flex-shrink: 0;
}
</style>
^^^^^^^^
```
The reason we have these 2 checks is because just relying on the first
heuristic alone, also means that we will not be migrating keys in JS
objects, because they typically follow the same structure:
```js
let classes = {
flex: 0,
^ ^^
}
```
Another important thing to note is that we can't just ignore anything in
between `<style>…</style>` blocks, because you could still be using
`@apply` that we _do_ want to migrate.
Last but not least, the first heuristics is not perfect either. If you
are writing minified CSS then this will likely fail if there is no
whitespace around the candidate.
But my current assumption is that nobody should be writing minified CSS,
and minified CSS will very likely be generated and gitignored. In either
situation, replacements in minified CSS will not be any worse than it is
today.
I'm open to suggestions for better heuristics.
## Test plan
1. Added an integration test that verifies that we do migrate `@apply`
and don't migrate the `flex-shrink: 0;` declaration.
Fixes: #17975
|
||
|
|
74e084ad27
|
Prepare v4.1.7 release (#18040)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
1ada8e0f22
|
Make candidate template migrations faster (#18025)
This PR makes the migrations for templates much faster. To make this
work, I also had to move things around a bit (so you might want to check
this PR commit by commit). I also solved an issue by restructuring the
code.
### Performance
For starters, we barely applied any caching when migrating candidates
from α to β. The problem with this is that in big projects the same
candidates will appear _everywhere_, so caching is going to be useful
here.
One of the reasons why we didn't do any caching is that some migrations
were checking if a migration is actually safe to do. To do this, we were
checking the `location` (the location of the candidate in the template).
Since this location is unique for each template, caching was not
possible.
So the first order of business was to hoist the `isSafeMigration` check
up as the very first thing we do in the migration.
If we do this first, then the only remaining code relies on the
`DesignSystem`, `UserConfig` and `rawCandidate`.
In a project, the `DesignSystem` and `UserConfig` will be the same
during the migration, only the `rawCandidate` will be different which
means that we can move all this logic in a good old `DefaultMap` and
cache the heck out of it.
Running the numbers on our Tailwind Plus repo, this results in:
```
Total seen candidates: 2 211 844
Total migrated candidates: 7 775
Cache hits: 1 575 700
```
That's a lot of work we _don't_ have to do. Looking at the timings, the
template migration step goes from ~45s to ~10s because of this.
Another big benefit of this is that this makes migrations _actually_
safe. Before we were checking if a migration was safe to do in specific
migrations. But other migrations were still printing the candidate which
could still result in an unsafe migration.
For example when migrating the `blur` and the `shadow` classes, the
`isSafeMigration` was used. But if the input was `!flex` then the safety
check wasn't even checked in this specific migration.
### Safe migrations
Also made some changes to the `isSafeMigration` logic itself. We used to
start by checking the location, but thinking about the problem again,
the actual big problem we were running into is classes that are short
like `blur`, and `shadow` because they could be used in other contexts
than a Tailwind CSS class.
Inverting this logic means that more specific Tailwind CSS classes will
very likely _not_ cause any issues at all.
For example:
- If you have variants: `hover:focus:flex`
- If you have arbitrary properties: `[color:red]`
- If you have arbitrary values: `bg-[red]`
- If you have a modifier: `bg-red-500/50`
- If you have a `-` in the name: `bg-red-500`
Even better if we can't parse a candidate at all, we can skip the
migrations all together.
This brings us to the issue in #17974, one of the issues was already
solved by just hoisting the `isSafeMigration`. But to make the issue was
completely solved I also made sure that in Vue attributes like
`:active="…"` are also considered unsafe (note: `:class` is allowed).
Last but not least, in case of the `!duration` that got replaced with
`duration!` was solved by verifying that the candidate actually produces
valid CSS. We can compute the signature for this class.
The reason this wasn't thrown away earlier is because we can correctly
parse `duration` but `duration` on its own doesn't exist,
`duration-<number>` does exist as a functional utility which is why it
parsed in the first place.
Fixes: #17974
## Test plan
1. Ran the tool on our Tailwind UI Templates repo to compare the new
output with the "old" behavior and there were no differences in output.
2. Ran the tool on our Tailwind Plus repo, and the template migration
step went from ~45s to ~10s.
3. Added additional tests to verify the issues in #17974 are fixed.
[ci-all] let's run this on all CI platforms...
|
||
|
|
f3157cd9a6
|
Ignore custom variants with :merge(…) selectors (#18020)
Closes #15617 ## Summary This PR ignores `addVariant(…)` legacy JS plugin calls for variants that are using the [`:merge(…)` selector](https://v3.tailwindcss.com/docs/plugins#parent-and-sibling-states) for parent and sibling states. We can ignore these now because in v4, `group-*` and `peer-*` variants _compound automatically_ and you don't have to define them anymore. ## Test plan Added a unit test to ensure that the `optional` variant example from the v3 docs work as expected. |
||
|
|
e57a2f5a3a
|
Change casing of utilities with named values to kebab-case to match u… (#18017)
Fixes #16156 ## Summary This PR adds a new 3 -> 4 template migration that changes the casing of in both utility values and modifier values from camelCase to kebab-case to match the updated CSS variable names. ## Test plan - Added integration test, see the diff in the PR. |
||
|
|
4db711d1e4
|
Fix -rotate-* with arbitary values (#18014)
Fixes #18013 ## Test plan See updated unit tests |
||
|
|
498f9ff003
|
Migrate bare values to named values (#18000)
This PR improves the upgrade tool by also migrating bare values to named values defined in the `@theme`. Recently we shipped some updates dat allowed us to migrate arbitrary values (with square brackets), but we didn't migrate bare values yet. That means that in this example: ```html <div class="aspect-[16/9]"></div> <div class="aspect-16/9"></div> ``` We migrated this to: ```html <div class="aspect-video"></div> <div class="aspect-16/9"></div> ``` With this change, we will also try and migrate the bare value to a named value. So this example: ```html <div class="aspect-[16/9]"></div> <div class="aspect-16/9"></div> ``` Now becomes: ```html <div class="aspect-video"></div> <div class="aspect-video"></div> ``` ## Test plan 1. Added unit tests for the new functionality. 2. Ran this on a local project Before: <img width="432" alt="image" src="https://github.com/user-attachments/assets/ce1adfbd-7be1-4062-bea5-66368f748e44" /> After: <img width="382" alt="image" src="https://github.com/user-attachments/assets/a385c94c-4e4c-4e1c-ac73-680c56ac4081" /> |
||
|
|
ef2e6c71fe
|
Upgrade: Migrate outline class (#17996)
This PR adds a migration from `outline` to `outline-solid` for the v3 -> v4 upgrade tool. ## Test plan - Added integration test |
||
|
|
4fba87bc90
|
Upgrade lightningcss to 1.30.0 (#17979)
|
||
|
|
ba944ca3b1
|
Upgrade bun to 1.2.13 (#17981)
|
||
|
|
5688f0a7ae
|
Add ignore pattern for node_modules in globby search in upgrade cli (#17969)
When passing `gitignore: true` to globby it will start a search for all .gitignore files, this initial search includes node_modules making it hang forever for large monorepos with many files inside node_modules |
||
|
|
0d975f5f06
|
Update dedent 1.5.3 → 1.6.0 (minor) (#17965)
Here is everything you need to know about this upgrade. Please take a good look at what changed and the test results before merging this pull request. ### What changed? #### ✳️ dedent (1.5.3 → 1.6.0) · [Repo](https://github.com/dmnd/dedent) · [Changelog](https://github.com/dmnd/dedent/blob/main/CHANGELOG.md) <details> <summary>Release Notes</summary> <h4><a href="https://github.com/dmnd/dedent/releases/tag/v1.6.0">1.6.0</a></h4> <blockquote><h2 dir="auto">What's Changed</h2> <ul dir="auto"> <li>feat: add <code class="notranslate">trimWhitespace</code> option by <a href="https://bounce.depfu.com/github.com/43081j">@43081j</a> in <a href="https://bounce.depfu.com/github.com/dmnd/dedent/pull/97">#97</a> </li> </ul> <h2 dir="auto">New Contributors</h2> <ul dir="auto"> <li> <a href="https://bounce.depfu.com/github.com/43081j">@43081j</a> made their first contribution in <a href="https://bounce.depfu.com/github.com/dmnd/dedent/pull/97">#97</a> </li> </ul> <p dir="auto"><strong>Full Changelog</strong>: <a href="https://bounce.depfu.com/github.com/dmnd/dedent/compare/v1.5.3...v1.6.0"><tt>v1.5.3...v1.6.0</tt></a></p></blockquote> <p><em>Does any of this look wrong? <a href="https://depfu.com/packages/npm/dedent/feedback">Please let us know.</a></em></p> </details> <details> <summary>Commits</summary> <p><a href=" |
||
|
|
f0986ce127
|
Prevent duplicate suggestions when using @theme and @utility together (#17675)
Fixes https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1313 Right now given this CSS: ```css @theme reference { --text-header: 1.5rem; } @utility text-header { text-transform: uppercase; } ``` You'll see two entries for `text-header` in IntelliSense completions but we only want you to see one. This PR solves this by merging their modifier lists and de-duping by class name. |
||
|
|
3386049b7b
|
Fix upgrade error when using @import … source(…) (#17963)
This PR fixes an issue when running the upgrade tool and if any of the
CSS files has an import that looks like this:
```css
@import "tailwindcss" source("…");
```
This was trying to resolve `tailwindcss" source("…` instead of
`tailwindcss`.
This PR fixes that.
## Test plan
1. Ran it locally on a project
Before:
<img width="1158" alt="image"
src="https://github.com/user-attachments/assets/09bf5d69-797c-4330-ade1-edc213f7ce5c"
/>
After:
<img width="1029" alt="image"
src="https://github.com/user-attachments/assets/d1c9e194-30e2-4564-83c5-d9a259a67e90"
/>
|
||
|
|
2d139984da
|
Prepare v4.1.6 release (#17951)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
56b22bb1d3
|
Add support for source maps (#17775)
Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file) |
||
|
|
17ca56d386
|
Fix bug with nested @apply rules in utility classes (#17924) (#17925)
Fixes #17924 When an `@apply` pointed to utility that nested usages of `@apply`, these nested usages were not properly carried through the dependency chain. This was because we were only tracking dependencies on the immediate parent rather than all parents. To fix this, this PR: - Modifies the dependency resolution to track dependencies through the entire parent path - Uses a `walk(…)` for the node replacement logic so that all nested `@apply` usages are also resolved (as these are now tracked in the dependency list anyways ## Test Plan - Added a regression test for #17924 to the unit tests and ensure existing tests don't break |
||
|
|
d8c4df8001
|
Write to log file when using DEBUG=* (#17906)
This PR improves the debugability of the scanner when using `DEBUG=*` by
writing to a `tailwindcss-{pid}.log` file in the current working
directory.
It will include all the tracing information from the scanner. This PR
also introduces `Discovering {path}` and `Reading {path}` logs.
- `Discovering {path}` — this is logged when we are traversing the file
system looking for files. We use the `ignore` crate, and log this
information in the `filter_entry` callback. If a file was already
ignored by `.gitignore` files, this won't show up, but it also means
that we will not read it.
- `Reading {path}` — this is when we are actually reading the file so we
can start extracting potential Tailwind CSS classes.
These will give you some insights in what paths are being scanned, and
if we get stuck, where we get stuck.
Also, we are appending to the file. In the log below, you can already
see that a `tailwindcss-<number>.log` file exists already even though it
didn't exist before running the command. This should make it easier to
debug if we get stuck on a specific file/folder because the file will be
populated with information.
There are a few reasons for appending to a file:
1. There is a lot of output, so spamming the stdout/stderr is not ideal
2. If you run the same command again, after changing your `@source`
directives, you could diff the outputs. (although, the timestamps will
be different)
3. When using `DEBUG=*`, a lot of other tools also output debug
information, so writing to a file should make this better.
<details>
<summary>Example log</summary>
```log
2025-05-06T23:13:45.912292Z INFO scan_sources: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.912697Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/out.css"
2025-05-06T23:13:45.912716Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/tailwindcss-61347.log"
2025-05-06T23:13:45.912748Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app"
2025-05-06T23:13:45.912786Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/test"
2025-05-06T23:13:45.912814Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/test/utils.ts"
2025-05-06T23:13:45.912851Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils"
2025-05-06T23:13:45.912873Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/flatten.ts"
2025-05-06T23:13:45.912884Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/matrix.ts"
2025-05-06T23:13:45.912893Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/default-map.ts"
2025-05-06T23:13:45.912904Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/tailwind.css"
2025-05-06T23:13:45.912914Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/root.tsx"
2025-05-06T23:13:45.912936Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain"
2025-05-06T23:13:45.912962Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/expression.ts"
2025-05-06T23:13:45.912972Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/walk-ast.ts"
2025-05-06T23:13:45.912995Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature"
2025-05-06T23:13:45.913019Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/parser.test.ts"
2025-05-06T23:13:45.913029Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/validate.test.ts"
2025-05-06T23:13:45.913039Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/tokenizer.ts"
2025-05-06T23:13:45.913048Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/validate.ts"
2025-05-06T23:13:45.913058Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/parser.ts"
2025-05-06T23:13:45.913067Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/tokenizer.test.ts"
2025-05-06T23:13:45.913077Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/ast.ts"
2025-05-06T23:13:45.913086Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/version-control.ts"
2025-05-06T23:13:45.913095Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tokenizer.ts"
2025-05-06T23:13:45.913105Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/type-checker.test.ts"
2025-05-06T23:13:45.913121Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation-result.ts"
2025-05-06T23:13:45.913505Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tmp.test.ts"
2025-05-06T23:13:45.913514Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/version-control.test.ts"
2025-05-06T23:13:45.913523Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tokenizer.test.ts"
2025-05-06T23:13:45.913531Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation.ts"
2025-05-06T23:13:45.913554Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions"
2025-05-06T23:13:45.913583Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/sequence.test.ts"
2025-05-06T23:13:45.913592Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/date.ts"
2025-05-06T23:13:45.913601Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/lookup.test.ts"
2025-05-06T23:13:45.913613Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/statistics.ts"
2025-05-06T23:13:45.913622Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/intrinsics.test.ts"
2025-05-06T23:13:45.913631Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/math.ts"
2025-05-06T23:13:45.913640Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/engineering.ts"
2025-05-06T23:13:45.913648Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/types.test.ts"
2025-05-06T23:13:45.913656Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/text.ts"
2025-05-06T23:13:45.913665Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/text.test.ts"
2025-05-06T23:13:45.913673Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/intrinsics.ts"
2025-05-06T23:13:45.913681Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/logic.ts"
2025-05-06T23:13:45.913689Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/date.test.ts"
2025-05-06T23:13:45.913697Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/types.ts"
2025-05-06T23:13:45.913705Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/logic.test.ts"
2025-05-06T23:13:45.913713Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/lookup.ts"
2025-05-06T23:13:45.913720Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/sequence.ts"
2025-05-06T23:13:45.913728Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/engineering.test.ts"
2025-05-06T23:13:45.913741Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/index.ts"
2025-05-06T23:13:45.913749Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/math.test.ts"
2025-05-06T23:13:45.913757Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/statistics.test.ts"
2025-05-06T23:13:45.913783Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/__snapshots__"
2025-05-06T23:13:45.913817Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/spreadsheet.test.ts"
2025-05-06T23:13:45.913826Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/type-checker.ts"
2025-05-06T23:13:45.913833Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/function-utils.ts"
2025-05-06T23:13:45.913841Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/spreadsheet.ts"
2025-05-06T23:13:45.913849Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/expression.test.ts"
2025-05-06T23:13:45.913857Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation.test.ts"
2025-05-06T23:13:45.913879Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/routes"
2025-05-06T23:13:45.913896Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/routes/_index.tsx"
2025-05-06T23:13:45.914172Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/README.md"
2025-05-06T23:13:45.914197Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/public"
2025-05-06T23:13:45.914228Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/public/fonts"
2025-05-06T23:13:45.914268Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/package.json"
2025-05-06T23:13:45.914289Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/scripts"
2025-05-06T23:13:45.914310Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/scripts/generate-documentation.ts"
2025-05-06T23:13:45.914332Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/.github"
2025-05-06T23:13:45.914383Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/.github/workflows"
2025-05-06T23:13:45.914410Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/.github/workflows/ci.yml"
2025-05-06T23:13:45.914420Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/tsconfig.json"
2025-05-06T23:13:45.914455Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/vite.config.ts"
2025-05-06T23:13:45.914486Z INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/biome.json"
2025-05-06T23:13:45.914512Z INFO scan_sources: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.914515Z INFO extract_candidates: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.914518Z INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.914524Z INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: Reading 58 file(s)
2025-05-06T23:13:45.914808Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/tailwindcss-61347.log"
2025-05-06T23:13:45.914990Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/routes/_index.tsx"
2025-05-06T23:13:45.915138Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/.github/workflows/ci.yml"
2025-05-06T23:13:45.915140Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/statistics.test.ts"
2025-05-06T23:13:45.915145Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/tokenizer.ts"
2025-05-06T23:13:45.915163Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/tsconfig.json"
2025-05-06T23:13:45.915153Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/engineering.ts"
2025-05-06T23:13:45.915226Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/matrix.ts"
2025-05-06T23:13:45.915229Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/walk-ast.ts"
2025-05-06T23:13:45.915372Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/test/utils.ts"
2025-05-06T23:13:45.915578Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/ast.ts"
2025-05-06T23:13:45.915599Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/validate.ts"
2025-05-06T23:13:45.915637Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/version-control.ts"
2025-05-06T23:13:45.915647Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/parser.ts"
2025-05-06T23:13:45.915657Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/vite.config.ts"
2025-05-06T23:13:45.915680Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/types.test.ts"
2025-05-06T23:13:45.915681Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/flatten.ts"
2025-05-06T23:13:45.915701Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/parser.test.ts"
2025-05-06T23:13:45.915706Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/README.md"
2025-05-06T23:13:45.915691Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/default-map.ts"
2025-05-06T23:13:45.915701Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/spreadsheet.test.ts"
2025-05-06T23:13:45.915730Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/package.json"
2025-05-06T23:13:45.915753Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tokenizer.ts"
2025-05-06T23:13:45.915731Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/biome.json"
2025-05-06T23:13:45.915787Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/validate.test.ts"
2025-05-06T23:13:45.915822Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/tokenizer.test.ts"
2025-05-06T23:13:45.915826Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/text.ts"
2025-05-06T23:13:45.915885Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/root.tsx"
2025-05-06T23:13:45.915885Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/types.ts"
2025-05-06T23:13:45.915886Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/type-checker.ts"
2025-05-06T23:13:45.915990Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/type-checker.test.ts"
2025-05-06T23:13:45.915995Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/text.test.ts"
2025-05-06T23:13:45.915998Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/logic.ts"
2025-05-06T23:13:45.916000Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/intrinsics.ts"
2025-05-06T23:13:45.916024Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/sequence.ts"
2025-05-06T23:13:45.916056Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/function-utils.ts"
2025-05-06T23:13:45.916058Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/scripts/generate-documentation.ts"
2025-05-06T23:13:45.916070Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/expression.ts"
2025-05-06T23:13:45.916075Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/logic.test.ts"
2025-05-06T23:13:45.916063Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/date.test.ts"
2025-05-06T23:13:45.916119Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation-result.ts"
2025-05-06T23:13:45.916120Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/index.ts"
2025-05-06T23:13:45.916148Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/expression.test.ts"
2025-05-06T23:13:45.916152Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/engineering.test.ts"
2025-05-06T23:13:45.916193Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/lookup.ts"
2025-05-06T23:13:45.916219Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tmp.test.ts"
2025-05-06T23:13:45.916228Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tokenizer.test.ts"
2025-05-06T23:13:45.916245Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation.test.ts"
2025-05-06T23:13:45.916256Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/spreadsheet.ts"
2025-05-06T23:13:45.916253Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/lookup.test.ts"
2025-05-06T23:13:45.916267Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/sequence.test.ts"
2025-05-06T23:13:45.916287Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/math.test.ts"
2025-05-06T23:13:45.916286Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/version-control.test.ts"
2025-05-06T23:13:45.916317Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/intrinsics.test.ts"
2025-05-06T23:13:45.916323Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation.ts"
2025-05-06T23:13:45.916354Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/statistics.ts"
2025-05-06T23:13:45.916562Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/date.ts"
2025-05-06T23:13:45.916609Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/math.ts"
2025-05-06T23:13:45.916676Z INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.916682Z INFO extract_candidates:parse_all_blobs: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.916688Z INFO extract_candidates:parse_all_blobs:extract: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.918271Z INFO extract_candidates:parse_all_blobs:extract: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.918282Z INFO extract_candidates:parse_all_blobs: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.918286Z INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.918288Z INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: Reading 2 file(s)
2025-05-06T23:13:45.918315Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/tailwind.css"
2025-05-06T23:13:45.918504Z INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/out.css"
2025-05-06T23:13:45.918512Z INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.918519Z INFO extract_candidates:extract_css_variables: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.918522Z INFO extract_candidates:extract_css_variables:extract: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.918635Z INFO extract_candidates:extract_css_variables:extract: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.918640Z INFO extract_candidates:extract_css_variables: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.919059Z INFO extract_candidates: tailwindcss_oxide::scanner: exit
```
</details>
We also output where we are writing the file to. This looks like this
when using the CLI:
<img width="1462" alt="image"
src="https://github.com/user-attachments/assets/79c2cc95-adea-4bbd-a4f1-101de45726f5"
/>
Last but not least, this also ignores `.log` files by default
## Test plan
Ran the CLI (but you can use any tool real, since this is implemented in
Oxide) with the `DEBUG=*` flag.
The file generated, looks like the example I shared above.
|
||
|
|
449dfcf00d
|
Make upgrades faster (#17898)
This PR fixes an issue where the upgrade tests were taking too long. This PR fixes that. Essentially when updating dependencies we did this: ```sh npm install tailwindcss@latest npm install @tailwindcss/postcss@latest npm install prettier-plugin-tailwindcss@latest ``` But this is not ideal, because we are calling out to `npm` and run each dependency in isolation. With this PR, we essentially do it all in one go: ```sh npm install tailwindcss@latest @tailwindcss/postcss@latest prettier-plugin-tailwindcss@latest ``` ## Test plan Testing this locally, the results look like this: | Before | After | |--------|-------| | <img width="590" alt="image" src="https://github.com/user-attachments/assets/c899d432-78c3-4945-af73-3ef4fffa08da" /> | <img width="656" alt="image" src="https://github.com/user-attachments/assets/a448d711-dd74-44cf-9790-c8a14fc6964f" /> | In CI: | Before (with a failure) | After | | --- | --- | | <img width="224" alt="image" src="https://github.com/user-attachments/assets/f58a6bf6-fdbe-4474-aa1f-444ab51a53c9" /> | <img width="224" alt="image" src="https://github.com/user-attachments/assets/54606df5-4f69-444b-8d4c-5ce27c5d6b41" /> | [ci-all] |
||
|
|
4f8539c063
|
Fix bug replacing modifier variable shorthand syntax underscores (#17889)
Resolves #17888 **Reproduction URL:** https://play.tailwindcss.com/YvIekuzVRd Changes: * Don't use `decodeArbitraryValue` when parsing variable shorthand syntax in modifiers * replace `decodeArbitraryValue(modifier.slice(1, -1))` with `modifier.slice(1, -1)` * added test case, passing ✅ --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com> |
||
|
|
6a1df6acf6
|
Ignore @tailwind utilities inside @reference (#17836)
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
|
||
|
|
473f0241bf
|
Update h3 1.15.1 → 1.15.3 (patch) (#17873) | ||
|
|
c095071f22
|
Skip .css files when migrating templates (#17854)
This PR fixes an issue where the upgrade tool also migrates `.css` files as-if they are content files. This is not the intended behavior. ## Test plan Ran this on my personal website. Before: <img width="1316" alt="image" src="https://github.com/user-attachments/assets/2b7337c6-7b88-4811-911f-139ab2e31b3b" /> After: <img width="1046" alt="image" src="https://github.com/user-attachments/assets/55f09355-37cb-419b-9924-973cf2681c1d" /> |
||
|
|
4e4275638f
|
Design system driven upgrade migrations (#17831)
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> |
||
|
|
45cd32eed7
|
Prepare v4.1.5 release (#17830)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
a636933cd4
|
Add discrete properties to the default list of transition properties (#17812)
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> |
||
|
|
dbc8023a08
|
Do not sort and format stylesheets that didn't change (#17824)
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. |
||
|
|
9fec4ef60b
|
Update bun 1.2.8 → 1.2.11 (patch) (#17816) | ||
|
|
d2daf59524
|
Skip color-mix(…) when opacity is 100% (#17815)
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.
|
||
|
|
3a1b27e3f8
|
Pretty print variants starting with @ (#17814)
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. |
||
|
|
ba103799f7
|
Add h-lh/min-h-lh/max-h-lh utilities to match an elements line height (#17790)
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]` |
||
|
|
52000a30f0
|
PostCSS: Improve error recovery (#17754)
Closes #17295 This commit addresses an issue where the PostCSS plugin would get stuck in an error state when processing files with e.g. invalid @apply directives. This change prevents the PostCSS plugin from getting stuck in an error states particularly when the error happened inside an `@import`ed CSS files (as these were not registered as dependencies correctly before). ## Error overlays Some frameworks (e.g. Angular 19 or Next.js) handle errors inside PostCSS transforms to render a nice error overlay. This works well and gives immediate feedback that something went wrong. However, even when dependencies are registered before an error is thrown, these frameworks _will not consider changes to these dependencies anymore_ when an error occurs, as you can see in this Next.js example: https://github.com/user-attachments/assets/985c9dd7-daf8-4628-b4ad-6543ef220954 To avoid conditions where errors are not recoverable, this PR makes it so that these overlays will no longer show up in the app and only be logged to the output console. This will need follow-up upstream work before we can revisit this. ## Test plan - Tested with the repro in #17295. The error can now be recovered from. - Tested with a Next.js app where the issue in the screencast above is now no longer happening. - Added an integration test for errors in `@import`-ed files - Added a unit test for the changed `@apply` behavior. |
||
|
|
46758f7c29
|
Bump all Tailwind CSS related dependencies during upgrade (#17763)
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`. |
||
|
|
8e826b18f3
|
Ensure @tailwindcss/upgrade runs on Tailwind CSS v4 projects and is idempotent (#17717)
This PR ensures that the `@tailwindcss/upgrade` tool works on existing Tailwind CSS v4 projects. This PR also ensures that the upgrade tool is idempotent, meaning that it can be run multiple times and it should result in the same output. One awesome feature this unlocks is that you can run the upgrade tool on your codebase at any time and upgrade classes if you still have some legacy syntaxes, such as `bg-[var(--my-color)]`, in your muscle memory. One small note: If something changed in the first run, re-running will not work immediately because your git repository will not be clean and the upgrade tool requires your git repo to be clean. But once you verified and committed your changes, the upgrade tool will be idempotent. Idempotency is guaranteed by ensuring that some migrations are skipped by checking what version of Tailwind CSS you are on _before_ the version is upgraded. For the Tailwind CSS version: We will resolve `tailwindcss` itself to know the _actual_ version that is installed (the one resolved from `node_modules`). Not the one available in your package.json. Your `package.json` could be out of sync if you reverted changes but didn't run `npm install` yet. Back to Idempotency: For example, we have migrations where we change the variant order of stacked variants. If we would run these migrations every time you run the upgrade tool then we would be flip-flopping the order every run. See: https://tailwindcss.com/docs/upgrade-guide#variant-stacking-order Another example is where we rename some utilities. For example, we rename: | Before | After | | ----------- | ----------- | | `shadow` | `shadow-sm` | | `shadow-sm` | `shadow-xs` | Notice how we have `shadow-sm` in both the `before` and `after` column. If we would run the upgrade tool again, then we would eventually migrate your original `shadow` to `shadow-sm` (first run) and then to `shadow-xs` (second run). Which would result in the wrong shadow. See: https://tailwindcss.com/docs/upgrade-guide#renamed-utilities --- The order of upgrade steps changed a bit as well to make the internals are easier to work with and reason about. 1. Find CSS files 2. Link JS config files (if you are in a Tailwind CSS v3 project) 3. Migrate the JS config files (if you are in a Tailwind CSS v3 project) 4. Upgrade Tailwind CSS to v4 (or the latest version at that point) 5. Migrate the stylesheets (we used to migrate the source files first) 6. Migrate the source files This is done so that step 5 and 6 will always operate on a Tailwind CSS v4 project and we don't need to check the version number again. This is also necessary because your CSS file will now very likely contain `@import "tailwindcss";` which doesn't exist in Tailwind CSS v3. This also means that we can rely on the same internals that Tailwind CSS actually uses for locating the source files. We will use `@tailwindcss/oxide`'s scanner to find the source files (and it also keeps your custom `@source` directives into account). This PR also introduces a few actual migrations related to recent features and changes we shipped. 1. We migrate deprecated classes to their new names: | Before | After | | --------------------- | --------------------- | | `bg-left-top` | `bg-top-left` | | `bg-left-bottom` | `bg-bottom-left` | | `bg-right-top` | `bg-top-right` | | `bg-right-bottom` | `bg-bottom-right` | | `object-left-top` | `object-top-left` | | `object-left-bottom` | `object-bottom-left` | | `object-right-top` | `object-top-right` | | `object-right-bottom` | `object-bottom-right` | Introduced in: - https://github.com/tailwindlabs/tailwindcss/pull/17378 - https://github.com/tailwindlabs/tailwindcss/pull/17437 2. We migrate simple arbitrary variants to their dedicated variant: | Before | After | | ----------------------- | ------------------- | | `[&:user-valid]:flex` | `user-valid:flex` | | `[&:user-invalid]:flex` | `user-invalid:flex` | Introduced in: - https://github.com/tailwindlabs/tailwindcss/pull/12370 3. We migrate `@media` variants to their dedicated variant: | Before | After | | ----------------------------------------------------- | ------------------------- | | `[@media_print]:flex` | `print:flex` | | `[@media(prefers-reduced-motion:no-preference)]:flex` | `motion-safe:flex` | | `[@media(prefers-reduced-motion:reduce)]:flex` | `motion-reduce:flex` | | `[@media(prefers-contrast:more)]:flex` | `contrast-more:flex` | | `[@media(prefers-contrast:less)]:flex` | `contrast-less:flex` | | `[@media(orientation:portrait)]:flex` | `portrait:flex` | | `[@media(orientation:landscape)]:flex` | `landscape:flex` | | `[@media(forced-colors:active)]:flex` | `forced-colors:flex` | | `[@media(inverted-colors:inverted)]:flex` | `inverted-colors:flex` | | `[@media(pointer:none)]:flex` | `pointer-none:flex` | | `[@media(pointer:coarse)]:flex` | `pointer-coarse:flex` | | `[@media(pointer:fine)]:flex` | `pointer-fine:flex` | | `[@media(any-pointer:none)]:flex` | `any-pointer-none:flex` | | `[@media(any-pointer:coarse)]:flex` | `any-pointer-coarse:flex` | | `[@media(any-pointer:fine)]:flex` | `any-pointer-fine:flex` | | `[@media(scripting:none)]:flex` | `noscript:flex` | The new variants related to `inverted-colors`, `pointer`, `any-pointer` and `scripting` were introduced in: - https://github.com/tailwindlabs/tailwindcss/pull/11693 - https://github.com/tailwindlabs/tailwindcss/pull/16946 - https://github.com/tailwindlabs/tailwindcss/pull/11929 - https://github.com/tailwindlabs/tailwindcss/pull/17431 This also applies to the `not` case, e.g.: | Before | After | | --------------------------------------------------------- | ----------------------------- | | `[@media_not_print]:flex` | `not-print:flex` | | `[@media_not(prefers-reduced-motion:no-preference)]:flex` | `not-motion-safe:flex` | | `[@media_not(prefers-reduced-motion:reduce)]:flex` | `not-motion-reduce:flex` | | `[@media_not(prefers-contrast:more)]:flex` | `not-contrast-more:flex` | | `[@media_not(prefers-contrast:less)]:flex` | `not-contrast-less:flex` | | `[@media_not(orientation:portrait)]:flex` | `not-portrait:flex` | | `[@media_not(orientation:landscape)]:flex` | `not-landscape:flex` | | `[@media_not(forced-colors:active)]:flex` | `not-forced-colors:flex` | | `[@media_not(inverted-colors:inverted)]:flex` | `not-inverted-colors:flex` | | `[@media_not(pointer:none)]:flex` | `not-pointer-none:flex` | | `[@media_not(pointer:coarse)]:flex` | `not-pointer-coarse:flex` | | `[@media_not(pointer:fine)]:flex` | `not-pointer-fine:flex` | | `[@media_not(any-pointer:none)]:flex` | `not-any-pointer-none:flex` | | `[@media_not(any-pointer:coarse)]:flex` | `not-any-pointer-coarse:flex` | | `[@media_not(any-pointer:fine)]:flex` | `not-any-pointer-fine:flex` | | `[@media_not(scripting:none)]:flex` | `not-noscript:flex` | For each candidate, we run a set of upgrade migrations. If at the end of the migrations the original candidate is still the same as the new candidate, then we will parse & print the candidate one more time to pretty print into consistent classes. Luckily parsing is cached so there is no real downside overhead. Consistency (especially with arbitrary variants and values) will reduce your CSS file because there will be fewer "versions" of your class. Concretely, the pretty printing will apply changes such as: | Before | After | | ---------------------- | ----------------- | | `bg-[var(--my-color)]` | `bg-(--my-color)` | | `bg-[rgb(0,_0,_0)]` | `bg-[rgb(0,0,0)]` | Another big important reason for this change is that these classes on their own would have been migrated _if_ another migration was relevant for this candidate. This means that there are were some inconsistencies. E.g.: | Before | After | Reason | | ----------------------- | ---------------------- | ------------------------------------ | | `!bg-[var(--my-color)]` | `bg-(--my-color)!` | Because the `!` is in the wrong spot | | `bg-[var(--my-color)]` | `bg-[var(--my-color)]` | Because no migrations rand | As you can see, the way the `--my-color` variable is used, is different. This changes will make sure it will now always be consistent: | Before | After | | ----------------------- | ---------------------- | | `!bg-[var(--my-color)]` | `bg-(--my-color)!` | | `bg-[var(--my-color)]` | `bg-(--my-color)` | Yay! Of course, if you don't want these more cosmetic changes, you can always ignore the upgrade and revert these changes and only commit the changes you want. # Test plan - All existing tests still pass. - But I had to delete 1 test (we tested that Tailwind CSS v3 was required). - And had to mock the `version.isMajor` call to ensure we run the individual migration tests correctly. - Added new tests to test: 1. Migrating Tailwind CSS v4 projects works 1. Idempotency of the upgrade tool [ci-all] |
||
|
|
8bf06ab770
|
Handle legacy key behavior in theme-driven suggestions for @utility (#17733)
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 |
||
|
|
ee0d7525d8
|
Hide default shadow suggestions when missing theme keys (#17743)
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. |
||
|
|
aa836d3442
|
Prepare v4.1.4 release (#17669)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
6e1f53348d
|
Workaround Chrome rendering bug for skew-* utilities (#17627)
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" /> |
||
|
|
3bea760ff2
|
Add test coverage for property-specific colors (#17436)
There are several property-specific color variables available to maintain compatibility with v3. [My team and I would like to use them](https://github.com/tailwindlabs/tailwindcss/discussions/17400), but would feel more comfortable if they were formally supported by v4. This PR adds test coverage for those features. @RobinMalfait has confirmed that these features are not intended to be removed, [see conversation here →](https://discord.com/channels/486935104384532500/546706299010678784/1355213322995110171) @crswll Has opened a PR here for the related documentation - https://github.com/tailwindlabs/tailwindcss.com/pull/2178 |
||
|
|
6d8dd82c40
|
Fix shadow-inherit, inset-shadow-inherit, drop-shadow-inherit, and text-shadow-inherit (#17647)
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> |
||
|
|
c0af1e2129
|
Fix fontSize array upgrade (#17630)
Fixes #17622. Adds a specific handling case in `themeableValues()` in `packages/tailwindcss/src/compat/apply-config-to-theme.ts`. It seems like this has unique handling in v3 for an array value, whereby the second value is treated as a `line-height`. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> |
||
|
|
3ab7f12563
|
Fix container names with hyphens (#17628)
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> |
||
|
|
f66d287436
|
Fix brace expansion with range going down (#17591) | ||
|
|
3e9cf87adf
|
Make polyfill work when the theme variable resolves to another var (#17562)
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 |