34 Commits

Author SHA1 Message Date
Jordan Pittman
ff9f183368
Fix source map paths in CI (#17938) 2025-05-08 22:56:49 +02: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
d3846a4570
Update test to retry assertion on empty file and don't include forward-slash in the assertion (#17821)
To fix the CI issues we have, it turns out there are two issues:

1. The file-read was not retried, making it possible for all platforms
(but mostly Windows because it's the slowest) to sometimes not have the
file created yet before making the assertion.
2. A forward-slash in the assertion message path would always be a
backslash when run on Windows, thus the Windows test would never pass.

## Test plan

 - [ci-all] and have the test pass two times.
2025-04-29 18:16:58 +02:00
Philipp Spiess
af1d4aa683 Temporarily disable broken Windows test 2025-04-28 10:53:46 +02:00
Philipp Spiess
62ca1ec42d Fix Windows tests 2025-04-25 13:02:18 +02:00
Philipp Spiess
52000a30f0
PostCSS: Improve error recovery (#17754)
Closes #17295

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

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

## Error overlays

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


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

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

## Test plan

- Tested with the repro in #17295. The error can now be recovered from.
- Tested with a Next.js app where the issue in the screencast above is
now no longer happening.
- Added an integration test for errors in `@import`-ed files
- Added a unit test for the changed `@apply` behavior.
2025-04-25 12:02:10 +02:00
Philipp Spiess
4200a1ecc4
Fix slow incremental builds (especially on Windows) (#17511)
This PR fixes slow rebuilds on Windows where rebuilds go from ~2s to
~20ms.

Fixes: #16911
Fixes: #17522

## Test plan

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

Before:


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

After:


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

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

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

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

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


[ci-all]

---------

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

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

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


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

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

## Test plan

- Updated snapshot tests
- Deployed a new version of the website with the patch applied to ensure
it works across browsers:
https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/.
Tested on: Safari on iOS 15.5, Safari on iOS 16.0, Firefox 127, Firefox
128, Chrome 110, Chrome latest, Safari latest, Firefox latest
2025-04-02 18:16:28 +02:00
Philipp Spiess
156afc6d67
Improve compatibility with Safari 15 (#17435)
This PR improves the compatibility with Tailwind CSS v4 with unsupported
browsers with the goal to greatly improve compatibility with Safari 15.

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

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

## A simplified example

Given this example CSS input:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

```css
@reference "tailwindcss";

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

Would turn to the following file:

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

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

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

## Testing on tailwindcss.com

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

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

## Test plan

- Tested on tailwindcss.com via a preview build:
https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/
- Updated tests
- Ensure we also test on Chrome 111, Safari 16.4, Firefox 128 to
make sure we have no regressions. Also tested on Safari 16.4, 15.5, 18.0
2025-04-01 13:33:22 +02:00
Robin Malfait
53801091a0
Watch CSS module files for changes (#17467)
This PR is a follow-up PR for:
https://github.com/tailwindlabs/tailwindcss/pull/17433

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

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

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

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

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

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

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

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

## Test plan

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

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

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

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

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

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

High level, this is what happens after the now:

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

So, consider the following setup:

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

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

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

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

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

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

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

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

## Behavioral changes

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

## Combining with other features

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

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

## Test plan

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

[ci-all]

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-03-25 15:54:41 +01:00
Philipp Spiess
225f3233b6
Enable URL rewriting for PostCSS (#16965)
Fixes #16636 

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

## Test plan

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

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-03-07 12:18:10 +01:00
Philipp Spiess
b38948337d
Make @reference emit variable fallbacks instead of CSS variable declarations (#16774)
Fixes #16725

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

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

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

## Test plan

- Updated snapshots everywhere (see diff)
- New Next.js CSS modules integration test
2025-02-25 11:36:43 +01:00
Philipp Spiess
08972f294f
Scan Next.js dynamic route segments with manual @source rules (#16457)
Part of #16287

## Test plan

Added unit and integration tests

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-02-18 11:51:58 +01:00
Robin Malfait
4035ab0b76
Implement @variant (#15663)
This PR replaces `@variant` with `@custom-variant` for registering
custom variants via your CSS.

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

E.g.:

```css
.btn {
  background: white;

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

Compiles to:

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

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

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

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

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

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

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

---

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

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

<img width="1284" alt="Screenshot 2025-01-14 at 11 36 44"
src="https://github.com/user-attachments/assets/ae070781-35c1-4165-be51-baa63f28db5b"
/>
2025-01-14 15:51:44 +01:00
Robin Malfait
2a29c29441
Improve integration tests (stability + performance) (#15125)
This PR improves the integration tests in two ways:
1. Make the integration tests more reliable and thus less flakey
2. Make the integration tests faster (by introducing concurrency)

Tried a lot of different things to make sure that these tests are fast
and stable.

---

The biggest issue we noticed is that some tests are flakey, these are
tests with long running dev-mode processes where watchers are being used
and/or dev servers are created.
To solve this, all the tests that spawn a process look at stdout/stderr
and wait for a message from the process to know whether we can start
making changes.

For example, in case of an Astro project, you get a `watching for file
changes` message. In case of Nuxt project you can wait for an `server
warmed up in` and in case of Next.js there is a `Ready in` message.

These depend on the tools being used, so this is hardcoded per test
instead of a magically automatic solution.

These messages allow us to wait until all the initial necessary work,
internal watchers and/or dev servers are setup before we start making
changes to the files and/or request CSS stylesheets before the server(s)
are ready.

---

Another improvement is how we setup the dev servers. Before, we used to
try and get a free port on the system and use a `--port` flag or a
`PORT` environment variable. Instead of doing this (which is slow), we
rely on the process itself to show a URL with a port. Basically all
tools will try to find a free port if the default port is in use. We can
then use the stdout/stderr messages to get the URL and the port to use.

To reduce the amount of potential conflicts in ports, we used to run
every test and every file sequentially to basically guarantee that ports
are free. With this new approach where we rely on the process, I noticed
that we don't really run into this issue again (I reran the tests
multiple times and they were always stable)

<img width="316" alt="image"
src="https://github.com/user-attachments/assets/b75ddab4-f919-4995-85d0-f212b603e5c2"
/>
Note: these tests run Linux, Windows and macOS in this branch just for
testing purposes. Once this is done, we will only run Linux tests on PRs
and run all 3 of them on the `next` branch.

We do make the tests concurrent by default now, which in theory means
that there could be conflicts (which in practice means that the process
has to do a few more tries to find a free port). To reduce these
conflicts, we split up the integration tests such that Vite, PostCSS,
CLI, … tests all run in a separate job in the GitHub actions workflow.

<img width="312" alt="image"
src="https://github.com/user-attachments/assets/fe9a58a1-98eb-4d9b-8845-a7c8a7af5766"
/>

Comparing this branch against the `next` branch, this is what CI looks
like right now:

| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="594" alt="image"
src="https://github.com/user-attachments/assets/540d21eb-ab03-42e8-9f6f-b3a071fc7635"
/> | <img width="672" alt="image"
src="https://github.com/user-attachments/assets/8ef2e891-08a1-464b-9954-4153174ebce7"
/> |

There also was a point in time where I introduced sequential tests such
that all spawned processes still run after each other, but so far I
didn't run into issues if we keep them concurrent so I dropped that
code.

Some small changes I made to make things more reliable:
1. When relying on stdout/stderr messages, we split lines on `\n` and we
strip all the ANSI escapes which allows us to not worry about special
ANSI characters when finding the URL or a specific message to wait for.
2. Once a test is done, we `child.kill()` the spawned process. If that
doesn't work, for whatever reason, we run a `child.kill('SIGKILL')` to
force kill the process. This could technically lead to some memory or
files not being cleaned up properly, but once CI is done, everything is
thrown away anyway.
3. As you can see in the screenshots, I used some nicer names for the
workflows.

| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="276" alt="image"
src="https://github.com/user-attachments/assets/e574bb53-e21b-4619-9cdb-515431b255b9"
/> | <img width="179" alt="image"
src="https://github.com/user-attachments/assets/8bc75119-fb91-4500-a1d0-bd09f74c93ad"
/> |

They also look a bit nicer in the PR overview as well:
<img width="929" alt="image"
src="https://github.com/user-attachments/assets/04fc71fc-74b0-4e7c-9047-2aada664efef"
/>

The very last commit just filters out Windows and macOS tests again for
PRs (but they are executed on the `next` branch.

---

### Nest steps

I think for now we are in a pretty good state, but there are some things
we can do to further improve everything (mainly make things faster) but
aren't necessary. I also ran into issue while trying it so there is more
work to do.

1. More splits — instead of having a Vite folder and PostCSS folder, we
can go a step further and have folders for Next.js, Astro, Nuxt, Remix,
…
2. Caching — right now we have to run the build step for every OS on
every "job". We can re-use the work here by introducing a setup job that
the other jobs rely on. @thecrypticace and I tried it already, but were
running into some Bun specific Standalone CLI issues when doing that.
3. Remote caching — we could re-enable remote caching such that the
`build` step can be full turbo (e.g.: after a PR is merged in `next` and
we run everything again)
2024-12-12 13:48:56 +01:00
Robin Malfait
fe9fc9abba
Use resolveJsId when resolving tailwindcss/package.json (#15041)
This PR uses the `enhanced-resolve` instead of
`createRequire(…).resolve` which improves the usability when running the
upgrade tool locally using Bun.

While testing, we also noticed that it is not possible to use a
`cjs`-only plugin inside of an `esm` project. It was also not possible
to use an `esm`-only plugin inside of a `cjs` project.

# Test plan

We added integration tests in both the CLI (the CLI is an mjs project)
and in the PostCSS (where we can configure a `cjs` and `esm` PostCSS
config) integration tests where we created an `esm` and `cjs` based
project with 4 plugins (`cjs`-only, `esm`-only, and TypeScript based
plugins: `cts`-only and `mts`-only).
2024-11-19 18:39:49 +01:00
Adam Wathan
7175605c61
Remove fallbacks from theme var(...) calls (#14881)
This PR changes how we render `var(...)` calls for theme values,
removing the fallback values we were previously including.

```diff
  .text-white {
-   color: var(--color-white, #fff);
+   color: var(--color-white);
  }
```

We previously included the fallbacks only so you could see the value in
dev tools but this feels like a bad reason to bloat the CSS. I'd rather
just convince the Chrome team to surface this stuff better in dev tools
in the first place.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-11-05 15:44:21 -05:00
Jordan Pittman
3b2ca85138
Fix new file detection in PostCSS plugin (#14829)
We broke this at some point — probably when we tried to optimize
rebuilds in PostCSS by not performing a full auto-source detection scan.

This PR addresses this problem by:
1. Storing a list of found directories
2. Comparing their mod times on every scan
3. If the mod time has changed we scan the directory for new files which
we then store and scan
2024-10-30 15:56:55 +01:00
Robin Malfait
d68a780f98
Auto source detection improvements (#14820)
This PR introduces a new `source(…)` argument and improves on the
existing `@source`. The goal of this PR is to make the automatic source
detection configurable, let's dig in.

By default, we will perform automatic source detection starting at the
current working directory. Auto source detection will find plain text
files (no binaries, images, ...) and will ignore git-ignored files.

If you want to start from a different directory, you can use the new
`source(…)` next to the `@import "tailwindcss/utilities"
layer(utilities) source(…)`.

E.g.:

```css
/* ./src/styles/index.css */
@import 'tailwindcss/utilities' layer(utilities) source('../../');
```

Most people won't split their source files, and will just use the simple
`@import "tailwindcss";`, because of this reason, you can use
`source(…)` on the import as well:

E.g.:

```css
/* ./src/styles/index.css */
@import 'tailwindcss' source('../../');
```

Sometimes, you want to rely on auto source detection, but also want to
look in another directory for source files. In this case, yuo can use
the `@source` directive:

```css
/* ./src/index.css */
@import 'tailwindcss';

/* Look for `blade.php` files in `../resources/views` */
@source '../resources/views/**/*.blade.php';
```

However, you don't need to specify the extension, instead you can just
point the directory and all the same automatic source detection rules
will apply.

```css
/* ./src/index.css */
@import 'tailwindcss';

@source '../resources/views';
```

If, for whatever reason, you want to disable the default source
detection feature entirely, and only want to rely on very specific glob
patterns you define, then you can disable it via `source(none)`.

```css
/* Completely disable the default auto source detection */
@import 'tailwindcss' source(none);

/* Only look at .blade.php files, nothing else  */
@source "../resources/views/**/*.blade.php";
```

Note: even with `source(none)`, if your `@source` points to a directory,
then auto source detection will still be performed in that directory. If
you don't want that, then you can simply add explicit files in the globs
as seen in the previous example.

```css
/* Completely disable the default auto source detection */
@import 'tailwindcss' source(none);

/* Run auto source detection in `../resources/views` */
@source "../resources/views";
```

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-29 20:33:34 +00:00
Philipp Spiess
35cd2ff1ee
Resolve third-party plugins with exports in their package.json (#14775)
This PR fixes an issue when trying to resolve plugins with `exports` in
their `package.json`, like `@headlessui/tailwindcss`. The missing
`conditionNames` in the enhanced resolver config would cause it to not
properly look up the name.

## Test Plan

I added a test using the `postcss` setup (the existing plugin tests are
inside the CLI setup but the CLI can only ever run in Module JS mode).

To ensure the tests are resolving to the right environment (CJS vs MJS),
I added logging of the `import.meta.url` value to the resolver code.
When run, this was the output:

![Screenshot 2024-10-24 at
15.28.10.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/0Y77ilPI2WoJfMLFiAEw/c0197934-7b61-47c4-bda5-de037b31d43a.png)

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-10-24 11:34:19 -04:00
Robin Malfait
35b84cc313
Improve @tailwindcss/postcss performance for initial builds (#14565)
This PR improves the performance of the `@tailwindcss/postcss` plugin.
Before this change we created 2 compiler instances instead of a single
one. On a project where a `tailwindcss.config.ts` file is used, this
means that the timings look like this:

```
[@tailwindcss/postcss] Setup compiler: 137.525ms
⋮
[@tailwindcss/postcss] Setup compiler: 43.95ms
```

This means that with this small change, we can easily shave of ~50ms for
initial PostCSS builds.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-10-03 16:21:54 +02:00
Philipp Spiess
3f85b74611
Add integration tests for multi-root builds (#14564)
When your Vite or postcss project has multiple Tailwind CSS roots with
different configs, they should not influence each other (with the
exception of the same candidates being used).
2024-10-01 17:00:20 +02:00
Robin Malfait
30fbc2c707
Fix rebuilds when editing imported CSS files (#14561) 2024-10-01 11:52:12 +00:00
Philipp Spiess
79794744a9
Resolve @import in core (#14446)
This PR brings `@import` resolution into Tailwind CSS core. This means
that our clients (PostCSS, Vite, and CLI) no longer need to depend on
`postcss` and `postcss-import` to resolve `@import`. Furthermore this
simplifies the handling of relative paths for `@source`, `@plugin`, or
`@config` in transitive CSS files (where the relative root should always
be relative to the CSS file that contains the directive). This PR also
fixes a plugin resolution bug where non-relative imports (e.g. directly
importing node modules like `@plugin '@tailwindcss/typography';`) would
not work in CSS files that are based in a different npm package.

### Resolving `@import`

The core of the `@import` resolution is inside
`packages/tailwindcss/src/at-import.ts`. There, to keep things
performant, we do a two-step process to resolve imports. Imagine the
following input CSS file:

```css
@import "tailwindcss/theme.css";
@import "tailwindcss/utilities.css";
```

Since our AST walks are synchronous, we will do a first traversal where
we start a loading request for each `@import` directive. Once all loads
are started, we will await the promise and do a second walk where we
actually replace the AST nodes with their resolved stylesheets. All of
this is recursive, so that `@import`-ed files can again `@import` other
files.

The core `@import` resolver also includes extensive test cases for
[various combinations of media query and supports conditionals as well
als layered
imports](https://developer.mozilla.org/en-US/docs/Web/CSS/@import).

When the same file is imported multiple times, the AST nodes are
duplicated but duplicate I/O is avoided on a per-file basis, so this
will only load one file, but include the `@theme` rules twice:

```css
@import "tailwindcss/theme.css";
@import "tailwindcss/theme.css";
```

### Adding a new `context` node to the AST

One limitation we had when working with the `postcss-import` plugin was
the need to do an additional traversal to rewrite relative `@source`,
`@plugin`, and `@config` directives. This was needed because we want
these paths to be relative to the CSS file that defines the directive
but when flattening a CSS file, this information is no longer part of
the stringifed CSS representation. We worked around this by rewriting
the content of these directives to be relative to the input CSS file,
which resulted in added complexity and caused a lot of issues with
Windows paths in the beginning.

Now that we are doing the `@import` resolution in core, we can use a
different data structure to persist this information. This PR adds a new
`context` node so that we can store arbitrary context like this inside
the Ast directly. This allows us to share information with the sub tree
_while doing the Ast walk_.

Here's an example of how the new `context` node can be used to share
information with subtrees:

```ts
const ast = [
  rule('.foo', [decl('color', 'red')]),
  context({ value: 'a' }, [
    rule('.bar', [
      decl('color', 'blue'),
      context({ value: 'b' }, [
        rule('.baz', [decl('color', 'green')]),
      ]),
    ]),
  ]),
]

walk(ast, (node, { context }) => {
  if (node.kind !== 'declaration') return
  switch (node.value) {
    case 'red':   assert(context.value === undefined)
    case 'blue':  assert(context.value === 'a')
    case 'green': assert(context.value === 'b')
  }
})
```

In core, we use this new Ast node specifically to persist the `base`
path of the current CSS file. We put the input CSS file `base` at the
root of the Ast and then overwrite the `base` on every `@import`
substitution.

### Removing the dependency on `postcss-import`

Now that we support `@import` resolution in core, our clients no longer
need a dependency on `postcss-import`. Furthermore, most dependencies
also don't need to know about `postcss` at all anymore (except the
PostCSS client, of course!).

This also means that our workaround for rewriting `@source`, the
`postcss-fix-relative-paths` plugin, can now go away as a shared
dependency between all of our clients. Note that we still have it for
the PostCSS plugin only, where it's possible that users already have
`postcss-import` running _before_ the `@tailwindcss/postcss` plugin.

Here's an example of the changes to the dependencies for our Vite client
 :

<img width="854" alt="Screenshot 2024-09-19 at 16 59 45"
src="https://github.com/user-attachments/assets/ae1f9d5f-d93a-4de9-9244-61af3aff1237">

### Performance

Since our Vite and CLI clients now no longer need to use `postcss` at
all, we have also measured a significant improvement to the initial
build times. For a small test setup that contains only a hand full of
files (nothing super-complex), we measured an improvement in the
**3.5x** range:

<img width="1334" alt="Screenshot 2024-09-19 at 14 52 49"
src="https://github.com/user-attachments/assets/06071fb0-7f2a-4de6-8ec8-f202d2cc78e5">

The code for this is in the commit history if you want to reproduce the
results. The test was based on the Vite client.

### Caveats

One thing to note is that we previously relied on finding specific
symbols in the input CSS to _bail out of Tailwind processing
completely_. E.g. if a file does not contain a `@tailwind` or `@apply`
directive, it can never be a Tailwind file.

Since we no longer have a string representation of the flattened CSS
file, we can no longer do this check. However, the current
implementation was already inconsistent with differences on the allowed
symbol list between our clients. Ideally, Tailwind CSS should figure out
wether a CSS file is a Tailwind CSS file. This, however, is left as an
improvement for a future API since it goes hand-in-hand with our planned
API changes for the core `tailwindcss` package.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-09-23 17:05:55 +02:00
Philipp Spiess
63390c97b2
Throw a useful error when tailwindcss is used as a PostCSS plugin (#14378)
While upgrading a project to Tailwind CSS v4, I forgot to remove the
`tailwindcss` import from the PostCSS config. As a result of this, I was
greeted with the following message:

```
node:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^

[Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /Users/philipp/dev/project): [TypeError] Invalid PostCSS Plugin found at: plugins[0]

(@/Users/philipp/dev/project/postcss.config.js)
TypeError: Invalid PostCSS Plugin found at: plugins[0]
```

I don't think this was particularly helpful, so I’m proposing we add a
default function export to the `tailwindcss` package so when it's used
inside PostCSS, we can control the error message. So I changed it to
something along these lines:

```
It looks like you're trying to use the \`tailwindcss\` package as a PostCSS plugin. This is no longer possible since Tailwind CSS v4.

If you want to continue to use Tailwind CSS with PostCSS, please install \`@tailwindcss/postcss\` and change your PostCSS config file.
    at w (/Users/philipp/dev/project/node_modules/tailwindcss/node_modules/tailwindcss/dist/lib.js:1:21233)
    at Object.<anonymous> (/Users/philipp/dev/project/node_modules/tailwindcss/postcss.config.cjs:3:13)
    at Module._compile (node:internal/modules/cjs/loader:1358:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
    at Module.load (node:internal/modules/cjs/loader:1208:32)
    at Module._load (node:internal/modules/cjs/loader:1024:12)
    at cjsLoader (node:internal/modules/esm/translators:348:17)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:297:7)
    at ModuleJob.run (node:internal/modules/esm/module_job:222:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)
```

This is also a good place to link to the migration guides once we have
them 🙂

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-11 17:02:24 +02:00
Philipp Spiess
f028eae75e
Integration tests: Move all file writes into retry block (#14350)
There are still instances in which CI is flaky after #14332. This PR
applies the same fix (that is, moving the file write into the retrying
block) to all `retryAssertion` callbacks.
2024-09-06 10:48:49 +02:00
Philipp Spiess
a1d56d8e24
Ensure content globs defined in @config files are relative to that file (#14314)
When you configure custom content globs inside an `@config` file, we
want to tread these globs as being relative to that config file and not
the CSS file that requires the content file. A config can be used by
multiple CSS configs.

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-09-03 16:54:08 +02:00
Jordan Pittman
52012d90d7
Support loading config files via @config (#14239)
In Tailwind v4 the CSS file is the main entry point to your project and
is generally configured via `@theme`. However, given that all v3
projects were configured via a `tailwind.config.js` file we definitely
need to support those. This PR adds support for loading existing
Tailwind config files by adding an `@config` directive to the CSS —
similar to how v3 supported multiple config files except that this is
now _required_ to use a config file.

You can load a config file like so:

```
@import "tailwindcss";
@config "./path/to/tailwind.config.js";
```

A few notes:
- Both CommonJS and ESM config files are supported (loaded directly via
`import()` in Node)
- This is not yet supported in Intellisense or Prettier — should
hopefully land next week
- TypeScript is **not yet** supported in the config file — this will be
handled in a future PR.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-09-02 18:03:16 +02:00
Philipp Spiess
84ebe19da2
Vite: Retain candidates between input CSS updates (#14228)
This PR fixes an issue introduced with the changed candidate cache
behavior in #14187.

Prior to #14187, candidates were cached globally within an instance of
Oxide. This meant that once a candidate was discovered, it would not
reset until you either manually cleared the cache or restarted the Oxide
process. With the changes in #14187 however, the cache was scoped to the
instance of the `Scanner` class with the intention of making the caching
behavior more easy to understand and to avoid a global cache.

This, however, had an unforeseen side-effect in our Vite extension.
Vite, in dev mode, discovers files _lazily_. So when a developer goes to
`/index.html` the first time, we will scan the `/index.html` file for
Tailwind candidates and then build a CSS file with those candidate. When
they go to `/about.html` later, we will _append_ the candidates from the
new file and so forth.

The problem now arises when the dev server detects changes to the input
CSS file. This requires us to do a re-scan of that CSS file which, after
#14187, caused the candidate cache to be gone. This is usually fine
since we would just scan files again for the changed candidate list but
in the Vite case we would only get the input CSS file change _but no
subsequent change events for all other files, including those currently
rendered in the browser_). This caused updates to the CSS file to remove
all candidates from the CSS file again.

Ideally, we can separate between two concepts: The candidate cache and
the CSS input file scan. An instance of the `Scanner` could re-parse the
input CSS file without having to throw away previous candidates. This,
however, would have another issue with the current Vite extension where
we do not properly retain instances of the `Scanner` class anyways. To
properly improve the cache behavior, we will have to fix the Vite
`Scanner` retaining behavior first. Unfortunately this means that for
the short term, we have to add some manual bookkeeping to the Vite
client and retain the candidate cache between builds ourselves.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-21 12:54:42 +02:00
Philipp Spiess
e10b786437
Add Next.js integration test (#14163)
Using the [new integration test
setup](https://github.com/tailwindlabs/tailwindcss/pull/14089), this PR
adds a test for a V4 Next.js setup using the Postcss plugin. It's
testing both a full build and the dev mode (non-turbo for now).

Because of webpack, tests are quite slow which is worrisome since we
probably need to add many more integrations in the future. One idea I
have is that we separate tests in two buckets: _essential_ tests that
run often and are fast and advanced suites that we only run on CI via
custom, non-blocking, jobs.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-16 15:45:52 +02:00
Philipp Spiess
921b4b673b
Use import to load plugins (#14132)
Alternative to #14110

This PR changes the way how we load plugins to be compatible with ES6
async `import`s. This allows us to load plugins even inside the browser
but it comes at a downside: We now have to change the `compile` API to
return a `Promise`...

So most of this PR is rewriting all of the call sites of `compile` to
expect a promise instead of the object.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-08-08 11:49:06 -04:00
Robin Malfait
541d84a3bb
Add @source support (#14078)
This PR is an umbrella PR where we will add support for the new
`@source` directive. This will allow you to add explicit content glob
patterns if you want to look for Tailwind classes in other files that
are not automatically detected yet.

Right now this is an addition to the existing auto content detection
that is automatically enabled in the `@tailwindcss/postcss` and
`@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use
the auto content detection, but uses the module graph instead.

From an API perspective there is not a lot going on. There are only a
few things that you have to know when using the `@source` directive, and
you probably already know the rules:

1. You can use multiple `@source` directives if you want.
2. The `@source` accepts a glob pattern so that you can match multiple
files at once
3. The pattern is relative to the current file you are in
4. The pattern includes all files it is matching, even git ignored files
1. The motivation for this is so that you can explicitly point to a
`node_modules` folder if you want to look at `node_modules` for whatever
reason.
6. Right now we don't support negative globs (starting with a `!`) yet,
that will be available in the near future.

Usage example:

```css
/* ./src/input.css */
@import "tailwindcss";
@source "../laravel/resources/views/**/*.blade.php";
@source "../../packages/monorepo-package/**/*.js";
```

It looks like the PR introduced a lot of changes, but this is a side
effect of all the other plumbing work we had to do to make this work.
For example:

1. We added dedicated integration tests that run on Linux and Windows in
CI (just to make sure that all the `path` logic is correct)
2. We Have to make sure that the glob patterns are always correct even
if you are using `@import` in your CSS and use `@source` in an imported
file. This is because we receive the flattened CSS contents where all
`@import`s are inlined.
3. We have to make sure that we also listen for changes in the files
that match any of these patterns and trigger a rebuild.

PRs:

- [x] https://github.com/tailwindlabs/tailwindcss/pull/14063
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14085
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14079
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14067
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14076
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14080
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14127
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14135

Once all the PRs are merged, then this umbrella PR can be merged. 

> [!IMPORTANT]  
> Make sure to merge this without rebasing such that each individual PR
ends up on the main branch.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-08-07 16:38:44 +02:00