756 Commits

Author SHA1 Message Date
Philipp Spiess
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
2025-05-13 14:20:40 +02:00
Philipp Spiess
4fba87bc90
Upgrade lightningcss to 1.30.0 (#17979) 2025-05-12 15:49:18 +02:00
Philipp Spiess
ba944ca3b1
Upgrade bun to 1.2.13 (#17981) 2025-05-12 15:17:19 +02:00
Tommy D. Rossi
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
2025-05-12 11:09:40 +00:00
depfu[bot]
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="90644fe0be...ab2ce25762">See
the full diff on Github</a>. The new version differs by 2 commits:</p>
<ul>
<li><a
href="ab2ce25762"><code>1.6.0
(#98)</code></a></li>
<li><a
href="86902f7c97"><code>feat:
add `trimWhitespace` option (#97)</code></a></li>
</ul>
</details>












---
![Depfu
Status](https://depfu.com/badges/edd6acd35d74c8d41cbb540c30442adf/stats.svg)

[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>
2025-05-10 13:21:48 +02:00
Jordan Pittman
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.
2025-05-09 20:12:43 +00:00
Robin Malfait
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"
/>
2025-05-09 20:04:48 +00:00
Robin Malfait
2d139984da
Prepare v4.1.6 release (#17951)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-05-09 13:39:18 +00:00
Jordan Pittman
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)
2025-05-08 16:29:49 -04:00
Philipp Spiess
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
2025-05-08 18:26:07 +02:00
Robin Malfait
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.
2025-05-07 10:19:54 -04:00
Robin Malfait
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]
2025-05-06 20:02:13 +02:00
Brandon McConnell
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>
2025-05-06 09:32:47 -04:00
Jordan Pittman
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
2025-05-05 11:21:55 -04:00
depfu[bot]
473f0241bf
Update h3 1.15.1 → 1.15.3 (patch) (#17873) 2025-05-05 10:30:10 +00:00
Robin Malfait
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"
/>
2025-05-03 00:48:45 +02:00
Robin Malfait
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>
2025-05-02 23:18:06 +02:00
Philipp Spiess
45cd32eed7
Prepare v4.1.5 release (#17830)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-04-30 16:57:44 +02:00
Philipp Spiess
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>
2025-04-30 14:30:58 +02:00
Robin Malfait
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.
2025-04-29 17:07:35 +00:00
depfu[bot]
9fec4ef60b
Update bun 1.2.8 → 1.2.11 (patch) (#17816) 2025-04-29 17:58:01 +02:00
Robin Malfait
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.
2025-04-28 13:30:01 -04:00
Robin Malfait
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.
2025-04-28 13:25:04 -04:00
Jordan Pittman
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]`
2025-04-26 15:14:45 -04:00
Philipp Spiess
52000a30f0
PostCSS: Improve error recovery (#17754)
Closes #17295

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

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

## Error overlays

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


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

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

## Test plan

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

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

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

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

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

Back to Idempotency:

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

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

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

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

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

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

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

---

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

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

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

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

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

1. We migrate deprecated classes to their new names:

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

   Introduced in:

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

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

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

   Introduced in:

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

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

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

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

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

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

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

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

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

Concretely, the pretty printing will apply changes such as:

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

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

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

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

Yay!

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

# Test plan

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

[ci-all]
2025-04-22 11:10:46 -04:00
Jordan Pittman
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
2025-04-22 10:02:28 -04:00
Jordan Pittman
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.
2025-04-22 09:30:19 -04:00
Philipp Spiess
aa836d3442
Prepare v4.1.4 release (#17669)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-04-14 17:32:30 +02:00
Philipp Spiess
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"
/>
2025-04-11 16:38:46 +02:00
Scott Bedard
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
&rarr;](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
2025-04-11 13:40:10 +02:00
Justin Wong
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>
2025-04-11 13:36:18 +02:00
Justin Wong
c0af1e2129
Fix fontSize array upgrade (#17630)
Fixes #17622.

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

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-11 13:23:54 +02:00
Justin Wong
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>
2025-04-11 12:28:25 +02:00
Robin Malfait
f66d287436
Fix brace expansion with range going down (#17591) 2025-04-07 15:48:19 +02:00
Philipp Spiess
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
2025-04-07 11:42:02 +02:00
Philipp Spiess
811e97d61a
Fix polyfill in combination with unused CSS variable removal (#17555)
This PR fixes an issue we noticed while investigating #17553, where the
unused CSS variable removal didn't work properly when the theme variable
it tried to remove was modified by a polyfill rule.

The way the bookkeeping for the unused CSS variable worked was that it
tired to find the declaration inside it's parent after the traversal.
However, the `color-mix(…)` polyfill has since then made changes to the
declaration so it can't find it's position correctly anymore and will
thus instead delete the last declaration of the node (this caused
unrelated CSS variables to be eliminated while the ones with
`color-mix(…)` were unexpectedly kept).

To fix this, we decided to apply the polyfills after any eventual
deletions. This also ensures that no `@supports` query for the variables
are created and simplifies the code a bit since all polyfills are now
colocated.

## Test plan

- Added a unit test for the example we discovered in #17553
- Luckily the conditions of this seemed rare enough so that it doesn't
cause any other of our tests to update.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-07 09:18:47 +00:00
Robin Malfait
5a77c9dfc4
Prepare v4.1.3 release (#17563)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-04-04 19:54:23 +02:00
Philipp Spiess
e085977844
PostCSS: Fix Turbopack 'one-revision-behind' bug (#17554)
Closes #17508

This PR fixes another issue we found that caused dev builds with Next.js
and Turbopack to resolve the CSS file that was saved one revision before
the latest update.

When debugging this we noticed that the PostCSS entry is called twice
for every one update when changing the input CSS file directly. That was
caused by the input file itself being added as a _dependency_ so you
would first get the callback that a _dependency_ has updated (at which
point we look at the file system and figure out we need a full-rebuild
because the input.css file has changed) and then another callback for
when the _input file_ has updated. The problem with the second callback
was that the file-system was already scanned for updates and since this
includes the `mtimes` for the input file, we seemingly thought that the
input file did not change. However, the issue is that the first callback
actually came with an outdated PostCSS input AST...

We found that this problem arises when you register the input CSS as a
dependency of itself. This is not expected and we actually guard against
this in the PostCSS client. However, we found that the input `from`
argument is _a relative path when using Next.js with Turbopack_ so that
check was not working as expected.

## Test plan

Added the change to the repro from #17508 and it seems to work fine now.


https://github.com/user-attachments/assets/2acb0078-f961-4498-be1a-b1c72d5ceda1

Also added a unit test to ensure we document that the input file path
can be a relative path.

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-04 14:58:50 +02:00
Robin Malfait
2fd7c8d967
Show warning when using unsupported bare value data type (#17464)
This PR will show a warning if you are using a bare value data type that
is not supported.

Let's say you want to create a new utility that allows `color` to be a
bare value data type like this:
```css
@utility paint-* {
  paint: --value([color], color);
}
```

This means that this would enable new syntax that we don't support yet.
E.g.: `paint-#0088cc`.

The only supported data types for bare values are:

- `number` — `2.5`
- `integer` — `2`
- `ratio` — `1/2`
- `percentage` — `50%`

All other data types are not supported in this position. This PR will
now show a warning:
~~~
Unsupported bare value data type: "color".
Only valid data types are: "number", "integer", "ratio", "percentage".

```css
--value([color],color)
                ^^^^^
```
~~~
Once we have better sourcemap / location tracking support, this warning
will point to the exact spot, but for now, only a re-print of the AST
can be used.


If you _do_ want to use other data types, then you will have to use
arbitrary value syntax with `[…]` instead.


```css
@utility paint-* {
  paint: --value([color]);
}
```

This will allow for `paint-[#0088cc]` for example.

Note: this is not a behavioral change, we already didn't support other
data types, but we silently ignored them. This means that we have to do
more parsing at runtime when evaluating the utility.

With this change, a warning is shown when registering the `@utility`,
not when using it.
2025-04-04 12:20:46 +00:00
Philipp Spiess
3f434a6f00
Vite: Don't register the current CSS file as a dependency on itself (#17533)
Closes #17512

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

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

## Test plan

- Tested with the repro provided in #17512
- Added a minimal integration test based on that reproduction that I
also validated will _fail_, if the fix is reverted.
2025-04-03 18:50:16 +02:00
Robin Malfait
4c99367b7b
Prepare release v4.1.2 (#17530)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-04-03 15:36:42 +00:00
Philipp Spiess
5a9d1f4d5c
Fix test that relies on mtimes (#17529)
Fixes a timing issue added to a new unit test on `main`. Going to wait
for the `CI / Linux` unit tests to pass 3 times before merging this.
2025-04-03 15:24:26 +00:00
Philipp Spiess
60b0da90ce
Polyfill: Fall back to first color value when color-mix(…) contains unresolvable var(…) (#17513)
This PR further improves the `color-mix(…)` polyfill to create a
reasonable fallback if dynamic values that can not statically be
resolved are used. This refers to either the use of `currentcolor` or
any variables that are not static theme variables.

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

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

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

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

## Test plan

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

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

Fixes: #17508

## Test plan

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

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-03 17:07:38 +02:00
Jordan Pittman
e45302b910
Fix drop shadow filters with multiple shadows (#17515)
It seems that I broke support for multiple drop-shadow filters when
`@theme inline` was used in v4.1. This PR fixes that by segmenting the
drop shadow value on top-level commas and wrapping each segment with
`drop-shadow(…)` like we did in v4.0.
2025-04-03 14:37:56 +00:00
Teddy Bradford
3e41e9ffe6
Replace currentColor with currentcolor (lowercase) (#17510)
Replaces `currentColor` with `currentcolor` (lowercase) to match what's
defined in [CSS Color Module Level
4](https://www.w3.org/TR/css-color-4/#currentcolor-color) and
[MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword)
(see: https://github.com/mdn/content/pull/16592).

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-03 16:09:12 +02:00
depfu[bot]
f8b9aa983a
Update @types/bun 1.2.4 → 1.2.8 (patch) (#17528) 2025-04-03 16:05:41 +02:00
depfu[bot]
e5b2b0f12c
Update bun 1.2.4 → 1.2.8 (patch) (#17527) 2025-04-03 15:53:27 +02:00