6056 Commits

Author SHA1 Message Date
Philipp Spiess
2eecb4d0b6
Remove max-w-auto and max-h-auto (#16917)
Closes #16916

This PR removes `max-w-auto` and `max-h-auto` due to them not being part
of v3 and not being valid CSS:

- https://developer.mozilla.org/en-US/docs/Web/CSS/max-width#syntax
- https://developer.mozilla.org/en-US/docs/Web/CSS/max-height#syntax

<img width="886" alt="Screenshot 2025-03-03 at 14 00 39"
src="https://github.com/user-attachments/assets/2c7fa17c-975b-4e1f-ae56-99b703d6ca70"
/>

<img width="163" alt="Screenshot 2025-03-03 at 13 54 26"
src="https://github.com/user-attachments/assets/12902a14-5a1e-4d7f-8127-fc2a3833221d"
/>

I decided to keep `max-w-auto` and `max-h-auto` since these values are
valid even though these were not in v3 either:

<img width="886" alt="Screenshot 2025-03-03 at 13 55 11"
src="https://github.com/user-attachments/assets/83bf6543-06a3-429e-b39e-ef3ac3290f0b"
/>
2025-03-05 11:59:33 +01:00
depfu[bot]
fe9ab1e969
Update tsup 8.3.6 → 8.4.0 (minor) (#16949) 2025-03-05 10:59:27 +00:00
depfu[bot]
13acd24830
Update bun 1.2.3 → 1.2.4 (patch) (#16954) 2025-03-05 11:56:50 +01:00
Robin Malfait
b3c25563b7
Improve Oxide candidate extractor [0] (#16306)
This PR adds a new candidate[^candidate] extractor with 2 major goals in
mind:

1. It must be way easier to reason about and maintain.
2. It must have on-par performance or better than the current candidate
extractor.

### Problem

Candidate extraction is a bit of a wild west in Tailwind CSS and it's a
very critical step to make sure that all your classes are picked up
correctly to ensure that your website/app looks good.

One issue we run into is that Tailwind CSS is used in many different
"host" languages and frameworks with their own syntax. It's not only
used in HTML but also in JSX/TSX, Vue, Svelte, Angular, Pug, Rust, PHP,
Rails, Clojure, .NET, … the list goes on and all of these have different
syntaxes. Introducing dedicated parsers for each of these languages
would be a huge maintenance burden because there will be new languages
and frameworks coming up all the time. The best thing we can do is make
assumptions and so far we've done a pretty good job at that.

The only certainty we have is that there is at least _some_ structure to
the possible Tailwind classes used in a file. E.g.: `abc#def` is
definitely not a valid class, `hover:flex` definitely is. In a perfect
world we limit the characters that can be used and defined a formal
grammar that each candidate must follow, but that's not really an option
right now (maybe this is something we can implement in future major
versions).

The current candidate extractor we have has grown organically over time
and required patching things here and there to make it work in various
scenarios (and edge cases due to the different languages Tailwind is
used in).

While there is definitely some structure, we essentially work in 2
phases:

1. Try to extract `0..n` candidates. (This is the hard part)
2. Validate each candidate to make sure they are valid looking classes
(by validating against the few rules we have)

Another reason the current extractor is hard to reason about is that we
need it to be fast and that comes with some trade-offs to readability
and maintainability.

Unfortunately there will always be a lot of false positives, but if we
extract more classes than necessary then that's fine. It's only when we
pass the candidates to the core engine that we will know for sure if
they are valid or not. (we have some ideas to limit the amount of false
positives but that's for another time)

### Solution

Since the introduction of Tailwind CSS v4, we re-worked the internals
quite a bit and we have a dedicated internal AST structure for
candidates. For example, if you take a look at this:

```html
<div class="[@media(pointer:fine)]:data-[state=pending]:hover:text-red-500/(--my-opacity)"></div>
```

<details>

<summary>This will be parsed into the following AST:</summary>

```json
[
  {
    "kind": "functional",
    "root": "text",
    "value": {
      "kind": "named",
      "value": "red-500",
      "fraction": null
    },
    "modifier": {
      "kind": "arbitrary",
      "value": "var(--my-opacity)"
    },
    "variants": [
      {
        "kind": "static",
        "root": "hover"
      },
      {
        "kind": "functional",
        "root": "data",
        "value": {
          "kind": "arbitrary",
          "value": "state=pending"
        },
        "modifier": null
      },
      {
        "kind": "arbitrary",
        "selector": "@media(pointer:fine)",
        "relative": false
      }
    ],
    "important": false,
    "raw": "[@media(pointer:fine)]:data-[state=pending]:hover:text-red-500/(--my-opacity)"
  }
]
```

</details>

We have a lot of information here and we gave these patterns a name
internally. You'll see names like `functional`, `static`, `arbitrary`,
`modifier`, `variant`, `compound`, ...

Some of these patterns will be important for the new candidate extractor
as well:

| Name | Example | Description |
| -------------------------- | ----------------- |
---------------------------------------------------------------------------------------------------
|
| Static utility (named) | `flex` | A simple utility with no inputs
whatsoever |
| Functional utility (named) | `bg-red-500` | A utility `bg` with an
input that is named `red-500` |
| Arbitrary value | `bg-[#0088cc]` | A utility `bg` with an input that
is arbitrary, denoted by `[…]` |
| Arbitrary variable | `bg-(--my-color)` | A utility `bg` with an input
that is arbitrary and has a CSS variable shorthand, denoted by `(--…)` |
| Arbitrary property | `[color:red]` | A utility that sets a property to
a value on the fly |

A similar structure exist for modifiers, where each modifier must start
with `/`:

| Name | Example | Description |
| ------------------ | --------------------------- |
---------------------------------------- |
| Named modifier | bg-red-500`/20` | A named modifier |
| Arbitrary value | bg-red-500`/[20%]` | An arbitrary value, denoted by
`/[…]` |
| Arbitrary variable | bg-red-500`/(--my-opacity)` | An arbitrary
variable, denoted by `/(…)` |

Last but not least, we have variants. They have a very similar pattern
but they _must_ end in a `:`.

| Name | Example | Description |
| ------------------ | --------------------------- |
------------------------------------------------------------------------
|
| Named variant | `hover:` | A named variant |
| Arbitrary value | `data-[state=pending]:` | An arbitrary value,
denoted by `[…]` |
| Arbitrary variable | `supports-(--my-variable):` | An arbitrary
variable, denoted by `(…)` |
| Arbitrary variant | `[@media(pointer:fine)]:` | Similar to arbitrary
properties, this will generate a variant on the fly |

The goal with the new extractor is to encode these separate patterns in
dedicated pieces of code (we called them "machines" because they are
mostly state machine based and because I've been watching Person of
Interest but I digress).

This will allow us to focus on each pattern separately, so if there is a
bug or some new syntax we want to support we can add it to those
machines.

One nice benefit of this is that we can encode the rules and handle
validation as we go. The moment we know that some pattern is invalid, we
can bail out early.

At the time of writing this, there are a bunch of machines:

<details>

<summary>Overview of the machines</summary>

- `ArbitraryPropertyMachine`

  Extracts candidates such as `[color:red]`. Some of the rules are:

  1. There must be a property name
  2. There must be a `:`
  3. There must ba a value

There cannot be any spaces, the brackets are included, if the property
is a CSS variable, it must be a valid CSS variable (uses the
`CssVariableMachine`).

  ```
  [color:red]
  ^^^^^^^^^^^

  [--my-color:red]
  ^^^^^^^^^^^^^^^^
  ```

  Depends on the `StringMachine` and `CssVariableMachine`.

- `ArbitraryValueMachine`

Extracts arbitrary values for utilities and modifiers including the
brackets:

  ```
  bg-[#0088cc]
     ^^^^^^^^^

  bg-red-500/[20%]
             ^^^^^
  ```

  Depends on the `StringMachine`.

- `ArbitraryVariableMachine`

Extracts arbitrary variables including the parentheses. The first
argument must be a valid CSS variable, the other arguments are optional
fallback arguments.

  ```
  (--my-value)
  ^^^^^^^^^^^^

  bg-red-500/(--my-opacity)
             ^^^^^^^^^^^^^^
  ```

  Depends on the `StringMachine` and `CssVariableMachine`.

- `CandidateMachine`

Uses the variant machine and utility machine. It will make sure that 0
or more variants are directly touching and followed by a utility.

  ```
  hover:focus:flex
  ^^^^^^^^^^^^^^^^

  aria-invalid:bg-red-500/(--my-opacity)
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ```

  Depends on the `VariantMachine` and `UtilityMachine`.

- `CssVariableMachine`

Extracts CSS variables, they must start with `--` and must contain at
least one alphanumeric character or, `-`, `_` and can contain any
escaped character (except for whitespace).

  ```
  bg-(--my-color)
      ^^^^^^^^^^

  bg-red-500/(--my-opacity)
              ^^^^^^^^^^^^

  bg-(--my-color)/(--my-opacity)
      ^^^^^^^^^^   ^^^^^^^^^^^^
  ```

- `ModifierMachine`

  Extracts modifiers including the `/`

  - `/[` will delegate to the `ArbitraryValueMachine`
  - `/(` will delegate to the `ArbitraryVariableMachine`

  ```
  bg-red-500/20
            ^^^

  bg-red-500/[20%]
            ^^^^^^

  bg-red-500/(--my-opacity)
            ^^^^^^^^^^^^^^^
  ```

  Depends on the `ArbitraryValueMachine` and `ArbitraryVariableMachine`.

- `NamedUtilityMachine`

Extracts named utilities regardless of whether they are functional or
static.

  ```
  flex
  ^^^^

  px-2.5
  ^^^^^^
  ```

  This includes rules like: A `.` must be surrounded by digits.

  Depends on the `ArbitraryValueMachine` and `ArbitraryVariableMachine`.

- `NamedVariantMachine`

Extracts named variants regardless of whether they are functional or
static. This is very similar to the `NamedUtilityMachine` but with
different rules. We could combine them, but splitting things up makes it
easier to reason about.

  Another rule is that the `:` must be included.

  ```
  hover:flex
  ^^^^^^

  data-[state=pending]:flex
  ^^^^^^^^^^^^^^^^^^^^^

  supports-(--my-variable):flex
  ^^^^^^^^^^^^^^^^^^^^^^^^^
  ```

Depends on the `ArbitraryVariableMachine`, `ArbitraryValueMachine`, and
`ModifierMachine`.

- `StringMachine`

This is a low-level machine that is used by various other machines. The
only job this has is to extract strings that start with double quotes,
single quotes or backticks.

We have this because once you are in a string, we don't have to make
sure that brackets, parens and curlies are properly balanced. We have to
make sure that balancing brackets are properly handled in other
machines.

  ```
  content-["Hello_World!"]
           ^^^^^^^^^^^^^^

  bg-[url("https://example.com")]
          ^^^^^^^^^^^^^^^^^^^^^
  ```

- `UtilityMachine`

Extracts utilities, it will use the lower level `NamedUtilityMachine`,
`ArbitraryPropertyMachine` and `ModifierMachine` to extract the utility.

It will also handle important markers (including the legacy important
marker).

  ```
  flex
  ^^^^

  bg-red-500/20
  ^^^^^^^^^^^^^

  !bg-red-500/20      Legacy important marker
  ^^^^^^^^^^^^^^

  bg-red-500/20!      New important marker
  ^^^^^^^^^^^^^^

  !bg-red-500/20!     Both, but this is considered invalid
  ^^^^^^^^^^^^^^^
  ```

Depends on the `ArbitraryPropertyMachine`, `NamedUtilityMachine`, and
`ModifierMachine`.

- `VariantMachine`

Extracts variants, it will use the lower level `NamedVariantMachine` and
`ArbitraryValueMachine` to extract the variant.

  ```
  hover:focus:flex
  ^^^^^^
        ^^^^^^
  ```

  Depends on the `NamedVariantMachine` and `ArbitraryValueMachine`.

</details>

One important thing to know here is that each machine runs to
completion. They all implement a `Machine` trait that has a
`next(cursor)` method and returns a `MachineState`.

The `MachineState` looks like this:

```rs
enum MachineState {
  Idle,
  Done(Span)
}
```

Where a `Span` is just the location in the input where the candidate was
found.

```rs
struct Span {
  pub start: usize,
  pub end: usize,
}
```

#### Complexities

**Boundary characters:**

When running these machines to completion, they don't typically check
for boundary characters, the wrapping `CandidateMachine` will check for
boundary characters.

A boundary character is where we know that even though the character is
touching the candidate it will not be part of the candidate.

```html
<div class="flex"></div>
<!--       ^    ^    -->
```

The quotes are touching the candidate `flex`, but they will not be part
of the candidate itself, so this is considered a valid candidate.

**What to pick?**

Let's imagine you are parsing this input:

```html
<div class="hover:flex"></div>
```

The `UtilityMachine` will find `hover` and `flex`. The `VariantMachine`
will find `hover:`. This means that at a certain point in the
`CandidateMachine` you will see something like this:

```rs
let variant_machine_state = variant_machine.next(cursor);
//  MachineState::Done(Span { start: 12, end: 17 })        // `hover:`

let utility_machine_state = utility_machine.next(cursor);
//  MachineState::Done(Span { start: 12, end: 16 })        // `hover`
```

They are both done, but which one do we pick? In this scenario we will
always pick the variant because its range will always be 1 character
longer than the utility.

Of course there is an exception to this rule and it has to do with the
fact that Tailwind CSS can be used in different languages and
frameworks. A lot of people use `clsx` for dynamically applying classes
to their React components. E.g.:

```tsx
<div
  class={clsx({
    underline: someCondition(),
  })}
></div>
```

In this scenario, we will see `underline:` as a variant, and `underline`
as a utility. We will pick the utility in this scenario because the next
character is whitespace so this will never be a valid candidate
otherwise (variants and utilities must be touching). Another reason this
is valid, is because there wasn't a variant present prior to this
candidate.

E.g.:

```tsx
<div
  class={clsx({
    hover:underline: someCondition(),
  })}
></div>
```

This will be considered invalid, if you do want this, you should use
quotes.

E.g.:

```tsx
<div
  class={clsx({
    'hover:underline': someCondition(),
  })}
></div>
```

**Overlapping/covered spans:**

Another complexity is that the extracted spans for candidates can and
will overlap. Let's take a look at this C# example:

```csharp
public enum StackSpacing
{
  [CssClass("gap-y-4")]
  Small,

  [CssClass("gap-y-6")]
  Medium,

  [CssClass("gap-y-8")]
  Large
}
```

In this scenario, `[CssClass("gap-y-4")]` starts with a `[` so we have a
few options here:

1. It is an arbitrary property, e.g.: `[color:red]`
2. It is an arbitrary variant, e.g.: `[@media(pointer:fine)]:`

When running the parsers, both the `VariantMachine` and the
`UtilityMachine` will run to completion but end up in a
`MachineState::Idle` state.

- This is because it is not a valid variant because it didn't end with a
`:`.
- It's also not a valid arbitrary property, because it didn't include a
`:` to separate the property from the value.

Looking at the code as a human it's very clear what this is supposed to
be, but not from the individual machines perspective.

Obviously we want to extract the `gap-y-*` classes here.

To solve this problem, we will run over an additional slice of the
input, starting at the position before the machines started parsing
until the position where the machines stopped parsing.

That slice will be this one: `[CssClass("gap-y-6")]` (we already skipped
over the whitespace). Now, for every `[` character we see, will start a
new `CandidateMachine` right after the `[`'s position and run the
machines over that slice. This will now eventually extract the `gap-y-6`
class.

The next question is, what if there was a `:` (e.g.:
`[CssClass("gap-y-6")]:`), then the `VariantMachine` would complete, but
the `UtilityMachine` will not because not exists after it. We will apply
the same idea in this case.

Another issue is if we _do_ have actual overlapping ranges. E.g.: `let
classes = ['[color:red]'];`. This will extract both the `[color:red]`
and `color:red` classes. You have to use your imagination, but the last
one has the exact same structure as `hover:flex` (variant + utility).

In this case we will make sure to drop spans that are covered by other
spans.

The extracted `Span`s will be valid candidates therefore if the outer
most candidate is valid, we can throw away the inner candidate.

```
Position:                       11112222222
                                67890123456
                                ↓↓↓↓↓↓↓↓↓↓↓

Span { start: 17, end: 25 }  //  color:red
Span { start: 16, end: 26 }  // [color:red]
```

#### Exceptions

**JavaScript keys as candidates:**

We already talked about the `clsx` scenario, but there are a few more
exceptions and that has to do with different syntaxes.

**CSS class shorthand in certain templating languages:**

In Pug and Slim, you can have a syntax like this:

```pug
.flex.underline
  div Hello World
```

<details>

<summary>Generated HTML</summary>

```html
<div class="flex underline">
  <div>Hello World</div>
</div>
```

</details>

We have to make sure that in these scenarios the `.` is a valid boundary
character. For this, we introduce a pre-processing step to massage the
input a little bit to improve the extraction of the data. We have to
make sure we don't make the input smaller or longer otherwise the
positions might be off.

In this scenario, we could simply replace the `.` with a space. But of
course, there are scenarios in these languages where it's not safe to do
that.

If you want to use `px-2.5` with this syntax, then you'd write:

```pug
.flex.px-2.5
  div Hello World
```

But that's invalid because that technically means `flex`, `px-2`, and
`5` as classes.

You can use this syntax to get around that:

```pug
div(class="px-2.5")
  div Hello World
```

<details>

<summary>Generated HTML</summary>

```html
<div class="px-2.5">
  <div>Hello World</div>
</div>
```

</details>

Which means that we can't simply replace `.` with a space, but have to
parse the input. Luckily we only care about strings (and we have a
`StringMachine` for that) and ignore replacing `.` inside of strings.

**Ruby's weird string syntax:**

```ruby
%w[flex underline]
```

This is valid syntax and is shorthand for:

```ruby
["flex", "underline"]
```

Luckily this problem is solved by the running the sub-machines after
each `[` character.

### Performance

**Testing:**

Each machine has a `test_…_performance` test (that is ignored by
default) that allows you to test the throughput of that machine. If you
want to run them, you can use the following command:

```sh
cargo test test_variant_machine_performance --release -- --ignored
```

This will run the test in release mode and allows you to run the ignored
test.

> [!CAUTION]
> This test **_will_** fail, but it will print some output. E.g.:

```
tailwindcss_oxide::extractor::variant_machine::VariantMachine: Throughput: 737.75 MB/s over 0.02s
tailwindcss_oxide::extractor::variant_machine::VariantMachine:   Duration: 500ns
```

**Readability:**

One thing to note when looking at the code is that it's not always
written in the cleanest way but we had to make some sacrifices for
performance reasons.

The `input` is of type `&[u8]`, so we are already dealing with bytes.
Luckily, Rust has some nice ergonomics to easily write `b'['` instead of
`0x5b`.

A concrete example where we had to sacrifice readability is the state
machines where we check the `previous`, `current` and `next` character
to make decisions. For a named utility one of the rules is that a `.`
must be preceded by and followed by a digit. This can be written as:

```rs
match (cursor.prev, cursor.curr, cursor.next) {
  (b'0'..=b'9', b'.', b'0'..=b'9') => { /* … */ }
  _ => { /* … */ }
}
```

But this is not very fast because Rust can't optimize the match
statement very well, especially because we are dealing with tuples
containing 3 values and each value is a `u8`.

To solve this we use some nesting, once we reach `b'.'` only then will
we check for the previous and next characters. We will also early return
in most places. If the previous character is not a digit, there is no
need to check the next character.

**Classification and jump tables:**

Another optimization we did is to classify the characters into a much
smaller `enum` such that Rust _can_ optimize all `match` arms and create
some jump tables behind the scenes.

E.g.:

```rs
#[derive(Debug, Clone, Copy, PartialEq)]
enum Class {
    /// ', ", or `
    Quote,

    /// \
    Escape,

    /// Whitespace characters
    Whitespace,

    Other,
}

const CLASS_TABLE: [Class; 256] = {
    let mut table = [Class::Other; 256];

    macro_rules! set {
        ($class:expr, $($byte:expr),+ $(,)?) => {
            $(table[$byte as usize] = $class;)+
        };
    }

    set!(Class::Quote, b'"', b'\'', b'`');
    set!(Class::Escape, b'\\');
    set!(Class::Whitespace, b' ', b'\t', b'\n', b'\r', b'\x0C');

    table
};
```

There are only 4 values in this enum, so Rust can optimize this very
well. The `CLASS_TABLE` is generated at compile time and must be exactly
256 elements long to fit all `u8` values.

**Inlining**:

Last but not least, sometimes we use functions to abstract some logic.
Luckily Rust will optimize and inline most of the functions
automatically. In some scenarios, explicitly adding a
`#[inline(always)]` improves performance, sometimes it doesn't improve
it at all.

You might notice that in some functions the annotation is added and in
some it's not. Every state machine was tested on its own and whenever
the performance was better with the annotation, it was added.

### Test Plan

1. Each machine has a dedicated set of tests to try and extract the
relevant part for that machine. Most machines don't even check boundary
characters or try to extract nested candidates. So keep that in mind
when adding new tests. Extracting inside of nested `[…]` is only handled
by the outer most `extractor/mod.rs`.
2. The main `extractor/mod.rs` has dedicated tests for recent bug
reports related to missing candidates.
3. You can test each machine's performance if you want to.

There is a chance that this new parser is missing candidates even though
a lot of tests are added and existing tests have been ported.

To double check, we ran the new extractor on our own projects to make
sure we didn't miss anything obvious.

#### Tailwind UI

On Tailwind UI the diff looks like this:

<details>

<summary>diff</summary>

```diff
diff --git a/./main.css b/./pr.css
index d83b0a506..b3dd94a1d 100644
--- a/./main.css
+++ b/./pr.css
@@ -5576,9 +5576,6 @@ @layer utilities {
     --tw-saturate: saturate(0%);
     filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
   }
-  .\!filter {
-    filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,) !important;
-  }
   .filter {
     filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
   }
```

</details>

The reason `!filter` is gone, is because it was used like this:

```js
getProducts.js
23:          if (!filter) return true
```

And right now `(` and `)` are not considered valid boundary characters
for a candidate.

#### Catalyst

On Catalyst, the diff looks like this:

<details>

<summary>diff</summary>

```diff
diff --git a/./main.css b/./pr.css
index 9f8ed129..4aec992e 100644
--- a/./main.css
+++ b/./pr.css
@@ -2105,9 +2105,6 @@
   .outline-transparent {
     outline-color: transparent;
   }
-  .filter {
-    filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
-  }
   .backdrop-blur-\[6px\] {
     --tw-backdrop-blur: blur(6px);
     -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
@@ -7141,46 +7138,6 @@
   inherits: false;
   initial-value: solid;
 }
-@property --tw-blur {
-  syntax: "*";
-  inherits: false;
-}
-@property --tw-brightness {
-  syntax: "*";
-  inherits: false;
-}
-@property --tw-contrast {
-  syntax: "*";
-  inherits: false;
-}
-@property --tw-grayscale {
-  syntax: "*";
-  inherits: false;
-}
-@property --tw-hue-rotate {
-  syntax: "*";
-  inherits: false;
-}
-@property --tw-invert {
-  syntax: "*";
-  inherits: false;
-}
-@property --tw-opacity {
-  syntax: "*";
-  inherits: false;
-}
-@property --tw-saturate {
-  syntax: "*";
-  inherits: false;
-}
-@property --tw-sepia {
-  syntax: "*";
-  inherits: false;
-}
-@property --tw-drop-shadow {
-  syntax: "*";
-  inherits: false;
-}
 @property --tw-backdrop-blur {
   syntax: "*";
   inherits: false;
```

</details>

The reason for this is that `filter` was only used as a function call:

```tsx
src/app/docs/Code.tsx
31:    .filter((x) => x !== null)
```

This was tested on all templates and they all remove a very small amount
of classes that aren't used.

The script to test this looks like this:

```sh
bun --bun ~/github.com/tailwindlabs/tailwindcss/packages/@tailwindcss-cli/src/index.t -- -i ./src/styles/tailwind.css -o pr.css
bun --bun ~/github.com/tailwindlabs/tailwindcss--main/packages/@tailwindcss-cli/src/index.t -- -i ./src/styles/tailwind.css -o main.css

git diff --no-index --patch ./{main,pr}.css
```

This is using git worktrees, so the `pr` branch lives in a `tailwindcss`
folder, and the `main` branch lives in a `tailwindcss--main` folder.

---

### Fixes:

- Fixes: https://github.com/tailwindlabs/tailwindcss/issues/15616
- Fixes: https://github.com/tailwindlabs/tailwindcss/issues/16750
- Fixes: https://github.com/tailwindlabs/tailwindcss/issues/16790
- Fixes: https://github.com/tailwindlabs/tailwindcss/issues/16801
- Fixes: https://github.com/tailwindlabs/tailwindcss/issues/16880 (due
to validating the arbitrary property)

---

### Ideas for in the future

1. Right now each machine takes in a `Cursor` object. One potential
improvement we can make is to rely on the `input` on its own instead of
going via the wrapping `Cursor` object.
2. If you take a look at the AST, you'll notice that utilities and
variants have a "root", these are basically prefixes of each available
utility and/or variant. We can use this information to filter out
candidates and bail out early if we know that a certain candidate will
never produce a valid class.
3. Passthrough the `prefix` information. Everything that doesn't start
with `tw:` can be skipped.

### Design decisions that didn't make it

Once you reach this part, you can stop reading if you want to, but this
is more like a brain dump of the things we tried and didn't work out.
Wanted to include them as a reference in case we want to look back at
this issue and know _why_ certain things are implemented the way they
are.

#### One character at a time

In an earlier implementation, the state machines were pure state
machines where the `next()` function was called on every single
character of the input. This had a lot of overhead because for every
character we had to:

1. Ask the `CandidateMachine` which state it was in.
2. Check the `cursor.curr` (and potentially the `cursor.prev` and
`cursor.next`) character.
3. If we were in a state where a nested state machine was running, we
had to check its current state as well and so on.
4. Once we did all of that we could go to the next character.

In this approach, the `MachineState` looked like this instead:

```rs
enum MachineState {
  Idle,
  Parsing,
  Done(Span)
}
```

This had its own set of problems because now it's very hard to know
whether we are done or not.

```html
<div class="hover:flex"></div>
<!--            ^          -->
```

Let's look at the current position in the example above. At this point,
it's both a valid variant and valid utility, so there was a lot of
additional state we had to track to know whether we were done or not.

#### `Span` stitching

Another approach we tried was to just collect all valid variants and
utilities and throw them in a big `Vec<Span>`. This reduced the amount
of additional state to track and we could track a span the moment we saw
a `MachineState::Done(span)`.

The next thing we had to do was to make sure that:

1. Covered spans were removed. We still do this part in the current
implementation.
2. Combine all touching variant spans (where `span_a.end + 1 ==
span_b.start`).
3. For every combined variant span, find a corresponding utility span.
   - If there is no utility span, the candidate is invalid.
- If there are multiple candidate spans (this is in theory not possible
because we dropped covered spans)
- If there is a candidate _but_ it is attached to another set of spans,
then the candidate is invalid. E.g.: `flex!block`
4. All left-over utility spans are candidates without variants.

This approach was slow, and still a bit hard to reason about.

#### Matching on tuples

While matching against the `prev`, `curr` and `next` characters was very
readable and easy to reason about. It was not very fast. Unfortunately
had to abandon this approach in favor of a more optimized approach.

In a perfect world, we would still write it this way, but have some
compile time macro that would optimize this for us.

#### Matching against `b'…'` instead of classification and jump tables

Similar to the previous point, while this is better for readability,
it's not fast enough. The jump tables are much faster.

Luckily for us, each machine has it's own set of rules and context, so
it's much easier to reason about a single problem and optimize a single
machine.

[^candidate]: A candidate is what a potential Tailwind CSS class _could_
be. It's a candidate because at this stage we don't know if it will
actually produce something but it looks like it could be a valid class.
E.g.: `hover:bg-red-500` is a candidate, but it will only produce
something if `--color-red-500` is defined in your theme.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-03-05 11:55:24 +01:00
Sơn Trần-Nguyễn
781fb73b7e
doc(CHANGELOG): correct PR number for scripting (#16947)
Tiny correction of the CHANEGLOG
2025-03-04 16:45:59 +00:00
Robin Malfait
57b080c5f2
Allow !important on CSS variables (#16873)
We recently fixed an issue
(https://github.com/tailwindlabs/tailwindcss/issues/16664) so that we
could remove the `!important` on CSS variables. The reason we did that
is because Google Chrome is showing a warning in the devtools styles
panel that this is invalid. However, this _is_ valid and the code
actually works as expected.

If we look at the CSS spec for this:

> Note: Custom properties can contain a trailing `!important`, but this
is automatically removed from the property’s value by the CSS parser,
and makes the custom property "important" in the CSS cascade. In other
words, the prohibition on top-level "!" characters does not prevent
`!important` from being used, as the `!important` is removed before
syntax checking happens.
>
> — https://www.w3.org/TR/css-variables-1/#syntax


So given this input:
```css
@import "tailwindcss";

body {
  --text-color: var(--color-white) !important;
  --bg-color: var(--color-blue-950) !important;

  /* Direct usage */
  background-color: var(--bg-color);

  /* Usage inside other functions as-if the `!important` is in the middle instead of the end */
  color: color-mix(in oklab, var(--text-color) 75%, transparent);
}
```

You will notice that everything works as expected, but if you look at
the Styles panel in Chrome for the `<body>` element, you will see an
incorrect warning. (At least that's what you used to see, I updated
Chrome and everything renders fine in devtools).

Play: https://play.tailwindcss.com/BpjAJ6Uxg3?file=css

This change reverts the "fix" for:
https://github.com/tailwindlabs/tailwindcss/issues/16664. @winchesHe you
were the original person that opened the issue so this info might be
useful to you as well. Can you verify that the Play link above does work
as expected for you?

Fixes: #16810
2025-03-04 16:21:56 +00:00
Philipp Spiess
db405304f4
Don't swallow @utility declarations when @apply is used in nested rules (#16940)
Fixes #16935

This PR fixes an issue where the order of how `@apply` was resolved was
incorrect for nested rules. Consider this example:

```css
.rule {
  @apply underline;
  .nested-rule {
    @apply custom-utility;
  }
}

@utility custom-utility {
  @apply flex;
}
```

The way we topologically sort these, we end up with a list that looks
roughly like this:

```css
.rule {
  @apply underline;
  .nested-rule {
    @apply custom-utility;
  }
}

@utility custom-utility {
  @apply flex;
}

.nested-rule {
  @apply custom-utility;
}
```

As you can see here the nested rule is now part of the top-level list.
This is correct because we first have to substitute the `@apply` inside
the `@utility custom-utility` before we can apply the `custom-utility`
inside `.nested-rule`. However, because we were using a regular AST walk
and because the initial `.rule` also contains the `.nested-rule` as
child, we would first substitute the `@apply` inside the `.nested-rule`,
causing the design-system to force resolve (and cache) the wrong value
for `custom-utility`.

Because the list is already flattened, we do not need to recursively
look into child declarations when we traverse the sorted list. This PR
changes it to use a regular `for` loop instead of the `walk`.

## Test plan

- Added a regression test
- Rest of tests still green
2025-03-04 16:54:05 +01:00
Philipp Spiess
b0aa20c30e
Ensure outline-hidden behaves like outline-none in non-forced-colors mode (#16943)
Part-of #16926

I noticed that `outline-hidden` would not set `--tw-outline-style`
(contrary to `outline-none`), thus stacking it with other outline
classes won't work as expected: https://play.tailwindcss.com/Y0lPGgekYh
2025-03-04 16:36:28 +01:00
depfu[bot]
a98ebacc64
Update prettier-plugin-embed 0.4.15 → 0.5.0 (major) (#16906)
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?




#### ✳️ prettier-plugin-embed (0.4.15 → 0.5.0) ·
[Repo](https://github.com/Sec-ant/prettier-plugin-embed) ·
[Changelog](https://github.com/Sec-ant/prettier-plugin-embed/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/Sec-ant/prettier-plugin-embed/releases/tag/v0.5.0">0.5.0</a></h4>

<blockquote><h3 dir="auto">Minor Changes</h3>
<ul dir="auto">
<li>
<a
href="15dd288ebc"><tt>15dd288</tt></a>:
feat: support shorthand for CallExpression as tag</li>
</ul></blockquote>
<p><em>Does any of this look wrong? <a
href="https://depfu.com/packages/npm/prettier-plugin-embed/feedback">Please
let us know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="cbbbfde47c...488a11218e">See
the full diff on Github</a>. The new version differs by 28 commits:</p>
<ul>
<li><a
href="488a11218e"><code>chore(release):
v0.5.0 (#140)</code></a></li>
<li><a
href="7ee0ab0046"><code>docs:
add JounQin as a contributor for code (#145)</code></a></li>
<li><a
href="89d8417a21"><code>chore:
renovate ignore chevrotain</code></a></li>
<li><a
href="0935722d21"><code>chore:
use pnpm (#141)</code></a></li>
<li><a
href="0393ef18e2"><code>ci:
remove broken biome-cli-codesandbox</code></a></li>
<li><a
href="15dd288ebc"><code>feat:
support shorthand for CallExpression as tag</code></a></li>
<li><a
href="d8b1faeef5"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 2 updates</code></a></li>
<li><a
href="440316d9a8"><code>chore(deps):
bump the minor-and-patch-updates group with 8 updates</code></a></li>
<li><a
href="92330b8c4e"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 6 updates</code></a></li>
<li><a
href="844ac42831"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 4 updates</code></a></li>
<li><a
href="4d53e3a08f"><code>chore(deps):
bump the minor-and-patch-updates group with 7 updates</code></a></li>
<li><a
href="1c5b658cef"><code>chore(deps):
bump the minor-and-patch-updates group with 7 updates</code></a></li>
<li><a
href="35674aeb28"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 5 updates</code></a></li>
<li><a
href="952153d8dc"><code>chore(deps):
bump the minor-and-patch-updates group with 6 updates</code></a></li>
<li><a
href="62b4f229f4"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 4 updates</code></a></li>
<li><a
href="d7d1f91cce"><code>chore(deps):
bump the minor-and-patch-updates group with 5 updates</code></a></li>
<li><a
href="9258b67dd9"><code>chore(deps):
bump the minor-and-patch-updates group with 8 updates</code></a></li>
<li><a
href="aa02346f3f"><code>chore(deps):
bump the minor-and-patch-updates group with 8 updates</code></a></li>
<li><a
href="201cd80bca"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 2 updates</code></a></li>
<li><a
href="dd39ad0040"><code>chore(deps):
bump the minor-and-patch-updates group with 9 updates</code></a></li>
<li><a
href="d663923f48"><code>chore(deps):
bump the minor-and-patch-updates group with 7 updates</code></a></li>
<li><a
href="f17fc0d0e5"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 7 updates</code></a></li>
<li><a
href="7b4bf7fb04"><code>chore(deps):
bump the minor-and-patch-updates group with 6 updates</code></a></li>
<li><a
href="10846661c3"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 11 updates</code></a></li>
<li><a
href="2a16f76b13"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 1 update</code></a></li>
<li><a
href="6db029d50e"><code>chore:
bump deps</code></a></li>
<li><a
href="ef876ba3f8"><code>chore(deps-dev):
bump the major-updates group with 2 updates</code></a></li>
<li><a
href="e8d629826a"><code>chore(deps-dev):
bump the minor-and-patch-updates group with 2 updates</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-03-03 13:22:51 +01:00
Adam Wathan
595b88f271
Support bare col-* and row-* utilities (#15183)
Resolves #15170.

This PR adds support for bare integer values to the `col-*` and `row-*`
utilities:

```css
.col-5 {
  grid-column: 5;
}
.row-6 {
  grid-row: 6;
}
```

These properties are shorthands for
`grid-column-start`/`grid-column-end` and
`grid-row-start`/`grid-row-end`, so using a bare integer value ends up
being a shortcut for:

```css
.col-5 {
  grid-column-start: 5;
  grid-column-end: auto;
}
```

…which makes these basically work like an alternative to `col-start-*`
and `row-start-*`.

These support negative values like `-col-6` as well, which also
technically extends to arbitrary values like `-col-[6/span_2]` now even
though that is a junk value. I've decided not to guard against that
though and just consider it user error to keep the implementation
simple.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-02-27 23:58:24 +01:00
Philipp Spiess
66ef77ce76
Emit @keyframes in prefixed setup (#16850)
Closes #16829

We were only adding keyframes used in variables starting with
`--animate` (without adding a potential prefix).

## Test plan

- Added a unit test
2025-02-27 17:35:25 +01:00
Luke Warlow
5532d48e8e
Add scripting variants (#11929)
This adds two variants for the
[`scripting`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/scripting)
media query. `noscript` for when JavaScript is disabled and `scripting`
for when it's enabled.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-02-26 18:43:24 +01:00
Philipp Spiess
e938c58778
Ensure not-* does not remove :is(…) (#16825)
Resolves #16821

This PR removes a special case in the `not-*` variant compound that
removed `:is(…)` if it was the only part of the inversed selector list.
While in-theory this makes sense, `:is(…)` accepts a _forgiving_
selector list where as `:not(…)` does not. See the [last point
here](https://developer.mozilla.org/en-US/docs/Web/CSS/:not#description).

This is an issue specifically in combinations with variants that have
selectors that are not supported by all browsers yet, for example
`:open`.

It seems to be the most expected to simply keep the `:is(…)` here in any
case.

## Test plan
- Ensured the repro form #16821 now also works in browsers that do not
support `:open` (Safari and Firefox at the time of writing this):
<img width="484" alt="Screenshot 2025-02-26 at 15 36 22"
src="https://github.com/user-attachments/assets/f3391693-895b-4e44-8566-95e2960ec4e3"
/>
2025-02-26 18:03:11 +01:00
Luke Warlow
124b82bc79
Add details-content variant (#15319)
This matches the new `::details-content` pseudo element.

See
https://developer.chrome.com/blog/styling-details#the_details-content_pseudo
for more info.

Currently shipped in Chromium, in development in WebKit and no
development yet in Firefox but they're
[supportive](https://github.com/mozilla/standards-positions/issues/1027).

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-02-26 11:36:32 +01:00
Benoît Rouleau
ab9df29551
Fix link in CHANGELOG.md (#16806)
The proper PR for this changelog entry is #16800.
2025-02-25 22:12:14 +01:00
Philipp Spiess
a8a2a43f6a Pin pnpm version in freebsd build 2025-02-25 18:20:14 +01:00
Philipp Spiess
6fffcfd377 Run pnpm install first in freebsd build 2025-02-25 17:50:27 +01:00
Philipp Spiess
a7d8371c71 Require build-freebsd too 2025-02-25 17:44:05 +01:00
Philipp Spiess
85d7375b59
Prepare v4.0.9 release (#16804)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
v4.0.9
2025-02-25 17:33:28 +01:00
Philipp Spiess
b56f12e6e7
Ensure nested functions in selectors used with JavaScript plugins are not truncated (#16802)
Fixes #16799

This was caused by a wrong condition in the CSS value parser that put
child function arguments into the parent function by accident.

## Test plan

- Added a unit test to guard against regressions
- Validated against the repro:  
<img width="914" alt="Screenshot 2025-02-25 at 16 31 14"
src="https://github.com/user-attachments/assets/f5fdf2e7-9c1b-4b04-89a8-1fa78a27f0f5"
/>
2025-02-25 17:00:55 +01:00
Philipp Spiess
294952f170
Handle BOM (#16800)
Resolves #15662 
Resolves #15467

## Test plan

Added integration tests for upgrade tooling (which already worked
surprisingly?) and CLI.
2025-02-25 16:07:16 +01:00
Jordan Pittman
662c6862ac
Make JS APIs available to plugins and configs in the Standalone CLI (#15934)
This PR ensures we bundle the relevant JS APIs in the Standalone CLI
like `tailwindcss`, `tailwindcss/plugin`, `tailwindcss/colors`, etc…

Before, when loading plugins or configs, imports for those resources
would fail.

Fixes #15235

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-02-25 13:21:46 +01:00
Philipp Spiess
ef57e6ea4d
Fix z-*! utilities (#16795)
Closes #16794
2025-02-25 12:20:11 +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
59e003e6d1
Vite: Don't crash with virtual module dependencies (#16780)
Fixes #16732

If we can not get the mtime from a file, chances are that the resource
is a virtual module. This is perfectly legit and we can fall back to
what we did before the changes in `4.0.8` (which is to rebuild the root
every time a change contains a dependency like that).

## Test plan

Added a test to mimic the setup from the repor in #16732. Also ensured
the repro now passes:

<img width="1278" alt="Screenshot 2025-02-24 at 17 29 38"
src="https://github.com/user-attachments/assets/d111273d-579f-44c2-82f5-aa32d6a1879a"
/>

Note that importing virtual modules directly in CSS does not work as the
resolver we use does not resolve against the Vite runtime it seems. This
is unrelated to the regression added in `4.0.8` though and something to
look into in the future.
2025-02-25 11:29:58 +01:00
depfu[bot]
37ea0c3493
Update @types/react 19.0.8 → 19.0.10 (patch) (#16775) 2025-02-24 13:21:52 +01:00
depfu[bot]
a893de22ba
Update @types/react-dom 19.0.3 → 19.0.4 (patch) (#16773) 2025-02-24 13:06:25 +01:00
depfu[bot]
604be562e3
Update eslint 9.19.0 → 9.20.1 (minor) (#16726)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.19.0 → 9.20.1) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.20.1">9.20.1</a></h4>

<blockquote><h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="274f054f19"><code
class="notranslate">274f054</code></a> fix: fix <code
class="notranslate">RuleContext</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19417">#19417</a>)
(Francesco Trotta)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="fe3ccb2ff4"><code
class="notranslate">fe3ccb2</code></a> docs: allow typing in search box
while dropdown is open (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19424">#19424</a>)
(Amaresh S M)</li>
<li>
<a
href="93c78a5c58"><code
class="notranslate">93c78a5</code></a> docs: Add instructions for pnpm
compat (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19422">#19422</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="b476a930bb"><code
class="notranslate">b476a93</code></a> docs: Fix Keyboard Navigation for
Search Results (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19416">#19416</a>)
(Amaresh S M)</li>
<li>
<a
href="ccb60c0b14"><code
class="notranslate">ccb60c0</code></a> docs: Update README (GitHub
Actions Bot)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.20.0">9.20.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="e89a54a309"><code
class="notranslate">e89a54a</code></a> feat: change behavior of inactive
flags (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19386">#19386</a>)
(Milos Djermanovic)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="91d4d9f620"><code
class="notranslate">91d4d9f</code></a> fix: Bring types in sync with
@eslint/core (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19157">#19157</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="fa25c7a79e"><code
class="notranslate">fa25c7a</code></a> fix: Emit warning when empty
config file is used (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19399">#19399</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="31a9fd03d2"><code
class="notranslate">31a9fd0</code></a> fix: Clearer error message for
wrong plugin format (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19380">#19380</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="61d99e38f2"><code
class="notranslate">61d99e3</code></a> fix: Better error message for
unserializable parser (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19384">#19384</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="db1b9a66e3"><code
class="notranslate">db1b9a6</code></a> fix: Ensure module scope is
checked for references in <code
class="notranslate">consistent-this</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19383">#19383</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="8bcd820f37"><code
class="notranslate">8bcd820</code></a> fix: <code
class="notranslate">arrow-body-style</code> crash with single-token body
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19379">#19379</a>)
(Milos Djermanovic)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="b7012c85f3"><code
class="notranslate">b7012c8</code></a> docs: rewrite examples with var
using let and const (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19407">#19407</a>)
(Mueez Javaid Hashmi)</li>
<li>
<a
href="64063765af"><code
class="notranslate">6406376</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="350f2b9349"><code
class="notranslate">350f2b9</code></a> docs: rewrite some examples with
var using let and const (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19404">#19404</a>)
(Mueez Javaid Hashmi)</li>
<li>
<a
href="93c325a7a8"><code
class="notranslate">93c325a</code></a> docs: rewrite examples with var
using let and const (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19398">#19398</a>)
(Mueez Javaid Hashmi)</li>
<li>
<a
href="56ff4048e0"><code
class="notranslate">56ff404</code></a> docs: replace var with let or
const in rules docs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19396">#19396</a>)
(Daniel Harbrueger)</li>
<li>
<a
href="4053226996"><code
class="notranslate">4053226</code></a> docs: change <code
class="notranslate">sourceType</code> in <code
class="notranslate">no-eval</code> examples (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19393">#19393</a>)
(Milos Djermanovic)</li>
<li>
<a
href="1324af0279"><code
class="notranslate">1324af0</code></a> docs: replace var with let and
const in rules docs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19392">#19392</a>)
(Daniel Harbrueger)</li>
<li>
<a
href="8b87e007bb"><code
class="notranslate">8b87e00</code></a> docs: replace <code
class="notranslate">var</code> with <code
class="notranslate">const</code> and <code
class="notranslate">let</code> in rules (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19389">#19389</a>)
(Tanuj Kanti)</li>
<li>
<a
href="758c66bc8d"><code
class="notranslate">758c66b</code></a> docs: Explain what frozen rules
mean (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19382">#19382</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="0ef8bb859c"><code
class="notranslate">0ef8bb8</code></a> docs: additional checks for rule
examples (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19358">#19358</a>)
(Milos Djermanovic)</li>
<li>
<a
href="58ab2f69d2"><code
class="notranslate">58ab2f6</code></a> docs: fix order of installation
steps in getting started (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19326">#19326</a>)
(Tanuj Kanti)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="979097a3b4"><code
class="notranslate">979097a</code></a> chore: upgrade @eslint/js@9.20.0
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19412">#19412</a>)
(Francesco Trotta)</li>
<li>
<a
href="031734efcb"><code
class="notranslate">031734e</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="d4c47c3738"><code
class="notranslate">d4c47c3</code></a> test: avoid empty config warning
in test output (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19408">#19408</a>)
(Milos Djermanovic)</li>
<li>
<a
href="67dd82ab88"><code
class="notranslate">67dd82a</code></a> chore: update dependency
@eslint/json to ^0.10.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19387">#19387</a>)
(renovate[bot])</li>
<li>
<a
href="15ac0e1824"><code
class="notranslate">15ac0e1</code></a> chore: add permissions: read-all
to stale.yml workflow (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19374">#19374</a>)
(Josh Goldberg )</li>
</ul></blockquote>
<p><em>Does any of this look wrong? <a
href="https://depfu.com/packages/npm/eslint/feedback">Please let us
know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="208e0b199f...07b2ffd3c5">See
the full diff on Github</a>. The new version differs by 32 commits:</p>
<ul>
<li><a
href="07b2ffd3c5"><code>9.20.1</code></a></li>
<li><a
href="01ff142d55"><code>Build:
changelog update for 9.20.1</code></a></li>
<li><a
href="fe3ccb2ff4"><code>docs:
allow typing in search box while dropdown is open
(#19424)</code></a></li>
<li><a
href="274f054f19"><code>fix:
fix `RuleContext` type (#19417)</code></a></li>
<li><a
href="93c78a5c58"><code>docs:
Add instructions for pnpm compat (#19422)</code></a></li>
<li><a
href="b476a930bb"><code>docs:
Fix Keyboard Navigation for Search Results (#19416)</code></a></li>
<li><a
href="ccb60c0b14"><code>docs:
Update README</code></a></li>
<li><a
href="7e78b50dac"><code>9.20.0</code></a></li>
<li><a
href="588b664536"><code>Build:
changelog update for 9.20.0</code></a></li>
<li><a
href="979097a3b4"><code>chore:
upgrade @eslint/js@9.20.0 (#19412)</code></a></li>
<li><a
href="031734efcb"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="91d4d9f620"><code>fix:
Bring types in sync with @eslint/core (#19157)</code></a></li>
<li><a
href="b7012c85f3"><code>docs:
rewrite examples with var using let and const (#19407)</code></a></li>
<li><a
href="d4c47c3738"><code>test:
avoid empty config warning in test output (#19408)</code></a></li>
<li><a
href="e89a54a309"><code>feat:
change behavior of inactive flags (#19386)</code></a></li>
<li><a
href="fa25c7a79e"><code>fix:
Emit warning when empty config file is used (#19399)</code></a></li>
<li><a
href="64063765af"><code>docs:
Update README</code></a></li>
<li><a
href="350f2b9349"><code>docs:
rewrite some examples with var using let and const
(#19404)</code></a></li>
<li><a
href="93c325a7a8"><code>docs:
rewrite examples with var using let and const (#19398)</code></a></li>
<li><a
href="56ff4048e0"><code>docs:
replace var with let or const in rules docs (#19396)</code></a></li>
<li><a
href="4053226996"><code>docs:
change `sourceType` in `no-eval` examples (#19393)</code></a></li>
<li><a
href="1324af0279"><code>docs:
replace var with let and const in rules docs (#19392)</code></a></li>
<li><a
href="8b87e007bb"><code>docs:
replace `var` with `const` and `let` in rules (#19389)</code></a></li>
<li><a
href="31a9fd03d2"><code>fix:
Clearer error message for wrong plugin format (#19380)</code></a></li>
<li><a
href="61d99e38f2"><code>fix:
Better error message for unserializable parser (#19384)</code></a></li>
<li><a
href="758c66bc8d"><code>docs:
Explain what frozen rules mean (#19382)</code></a></li>
<li><a
href="67dd82ab88"><code>chore:
update dependency @eslint/json to ^0.10.0 (#19387)</code></a></li>
<li><a
href="db1b9a66e3"><code>fix:
Ensure module scope is checked for references in `consistent-this`
(#19383)</code></a></li>
<li><a
href="8bcd820f37"><code>fix:
`arrow-body-style` crash with single-token body (#19379)</code></a></li>
<li><a
href="15ac0e1824"><code>chore:
add permissions: read-all to stale.yml workflow (#19374)</code></a></li>
<li><a
href="0ef8bb859c"><code>docs:
additional checks for rule examples (#19358)</code></a></li>
<li><a
href="58ab2f69d2"><code>docs:
fix order of installation steps in getting started
(#19326)</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-02-21 21:06:10 +01:00
Luke Warlow
751eb747d4
Add inverted-colors variant (#11693)
Add a variant for the
[`inverted-colors`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/inverted-colors)
media query.

This has been supported in Safari for a while. I'm also implementing
support in Chrome atm.

I've decided to only add the inverted-colors: inverted variant because
the `none` state isn't really useful.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-02-21 18:03:52 +01:00
SerKo
62d3e74694
Add wrap-anywhere utility (#12128)
Related discussion #12127

```css
.wrap-anywhere {
  overflow-wrap: anywhere;
}
```

### Updated 2024-11-30

The new changes remove the original `@supports` because I agree that
developers should decide for themselves whether to maintain backward
compatibility. Also updated in line with the new changes in the `next`
branch.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-02-21 16:49:05 +01:00
Philipp Spiess
419b3dc473
Prepare v4.0.8 release (#16713)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create an issue to first
discuss any significant new features. This includes things like adding
new utilities, creating new at-rules, or adding new component examples
to the documentation.


https://github.com/tailwindcss/tailwindcss/blob/master/.github/CONTRIBUTING.md

-->

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
v4.0.8
2025-02-21 16:06:37 +01:00
Robin Malfait
b47b6d2290
Remove double translate-z-px values (#16718)
This PR fixes an issue where if you used `translate-z-px` or
`-translate-z-px` that the utility was generated twice:
```css
.translate-z-px {
  --tw-translate-z: 1px;
  translate: var(--tw-translate-x) var(--tw-translate-y) var(--tw-translate-z);
}
.translate-z-px {
  --tw-translate-z: 1px;
  translate: var(--tw-translate-x) var(--tw-translate-y) var(--tw-translate-z);
}
@property --tw-translate-x {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}
@property --tw-translate-y {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}
@property --tw-translate-z {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}
```

With this PR, it will only generate once:
```css
.translate-z-px {
  --tw-translate-z: 1px;
  translate: var(--tw-translate-x) var(--tw-translate-y) var(--tw-translate-z);
}
@property --tw-translate-x {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}
@property --tw-translate-y {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}
@property --tw-translate-z {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}
```
2025-02-21 14:08:46 +00:00
Robin Malfait
113142a0e4
Use amount of properties when sorting (#16715)
Right now we sort the nodes based on a pre-defined sort order based on
the properties that are being used. The property sort order is defined
in a list we maintain.

We also have to make sure that the property count is taken into account
such that if all the "sorts" are the same, that we fallback to the
property count. Most amount of properties should be first such that we
can override it with more specific utilities that have fewer properties.

However, if a property doesn't exist, then it wouldn't be included in a
list of properties therefore the total count was off.

This PR fixes that by counting all the used properties. If a property
already exists it is counted twice. E.g.:
```css
.foo {
  color: red;

  &:hover {
    color: blue;
  }
}
```

In this case, we have 2 properties, not 1 even though it's the same
`color` property.

## Test plan:

1. Updated the tests that are now sorted correctly
2. Added an integration test to make sure that `prose-invert` is defined
after the `prose-stone` classes when using the `@tailwindcss/typography`
plugin where this problem originated from.

Note how in this play (https://play.tailwindcss.com/wt3LYDaljN) the
`prose-invert` comes _before_ the `prose-stone` which means that you
can't apply the `prose-invert` classes to invert `prose-stone`.
2025-02-21 15:02:07 +01:00
Adam Wathan
f8d7623ea5
Preserve custom properties in keyframes (#16376)
Closes #16374

Ensure we don't remove custom properties from within `@keyframe`
declarations.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-02-21 10:55:30 +01:00
Philipp Spiess
7bece4de7c
Re-enable: Only expose used CSS variables (#16676)
This PR re-enables the changes necessary to remove unused theme
variables and keyframes form your CSS.

This change was initially landed as #16211 and then later reverted in
#16403 because we found some unexpected interactions with using `@apply`
and CSS variables in multi-root setups like CSS modules or Vue inline
`<style>` blocks that were no longer seeing their required variables
defined.

This issue is fixed by now ensuring that theme variables that are
defined within an `@reference "…"` boundary will still be emitted in the
generated CSS when used (as this would otherwise not generate a valid
stylesheet).

So given the following input CSS:

```css
@reference "tailwindcss";
.text-red {
  @apply text-red-500;
}
```

We will now compile this to:

```css
@layer theme {
  :root, :host {
    --text-red-500: oklch(0.637 0.237 25.331);
  }
}
.text-red {
  color: var(--text-red-500);
}
```

This PR also improves the initial implementation to not mark theme
variables as used if they are only used to define other theme variables.
For example:

```css
@theme {
  --font-sans:
    ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
    'Noto Color Emoji';
  --font-mono:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
    monospace;

  --default-font-family: var(--font-sans);
  --default-mono-font-family: var(--font-mono);
}

.default-font-family {
  font-family: var(--default-font-family);
}
```

This would be reduced to the following now as `--font-mono` is only used
to define another variable and never used outside the theme block:

```css
:root, :host {
  --font-sans:
    ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
    'Noto Color Emoji';
  --default-font-family: var(--font-sans);
}

.default-font-family {
  font-family: var(--default-font-family);
}
```

## Test plan

- See updated unit and integration tests
- Validated it works end-to-end by using a SvelteKit example
2025-02-21 10:45:22 +01:00
depfu[bot]
dd7d8fd33a
Update turbo 2.3.4 → 2.4.2 (minor) (#16698)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ turbo (2.3.4 → 2.4.2) ·
[Repo](https://github.com/turborepo/turbo)





Sorry, we couldn't find anything useful about this release.











---
![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>
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-02-21 10:21:08 +01:00
depfu[bot]
ec0049a3c2
Update @playwright/test 1.50.0 → 1.50.1 (patch) (#16702) 2025-02-21 10:13:03 +01:00
Philipp Spiess
1d56525fa0
Fix integration tests for Windows (#16693)
The Nuxt preview server always starts on port 3000 even if that port is
taken. With the added tests in #16631 there is now a higher chance these
ports are already taken since e.g. react router prefers to start at port
3000 and so do other servers.
This PR changes this so that we assign a random port inside the test
instead.

## Test plan

- Ensure Windows CI is green again
2025-02-20 17:50:50 +01:00
Robin Malfait
3f270d2d9b
Allow theme(…) options when using @import (#16514)
This PR improves the developer experience when trying to use `theme(…)`
options on an import.

Today, if you want to use Tailwind CSS, you can import it as:

```css
@import "tailwindcss";
```

But if you want to use any of the `theme(…)` options, like the `static`
theme option, then you had to use this instead:

```css
@layer theme, base, components, utilities;

@import 'tailwindcss/theme' layer(theme) theme(static);
@import 'tailwindcss/preflight' layer(base);
@import 'tailwindcss/utilities' layer(utilities);
```
In this scenario you have to be careful, because the `layer(…)` _must_
be the first option after an import (according to the spec). So if you
use `@import 'tailwindcss/theme' theme(static) layer(theme);` then
that's not going to work either.

This PR solves that by allowing you to use the `theme(…)` options
directly on the `@import` statement:

```css
@import 'tailwindcss' theme(static);
```

The only edge case is when you want to use `theme(reference)`. A typical
use case is for projects with `<style>` blocks where you want to
_reference_ the CSS variables from the theme.

If you use `@import 'tailwindcss' theme(reference);`, then all `@theme`
blocks will be references and you can reference theme values. This is
good. The bad part is that `@import 'tailwindcss';` also includes
preflight CSS. This means that we will import the preflight CSS for
every `<style>` block. This is probably not what you want.

The solution is to use `@reference 'tailwindcss';` instead which strips
all of that information and only gives you access to CSS variables.

This PR also makes sure that if you use `theme(reference)` on an import
that we still throw an error and suggest you use `@reference` instead.
This is not a breaking change because right now if you use `@import`
with `theme(…)` options it will already throw an error.

### Test plan:

1. Added dedicated tests to make sure we don't throw anymore.
2. Added test to make sure that we _do_ throw when using
`theme(reference)` on an import.
2025-02-20 15:57:16 +00:00
Philipp Spiess
88b762b539
Vite: Remove module-graph scanner (#16631)
Alternative to #16425

Fixes #16585
Fixes #16389
Fixes #16252
Fixes #15794
Fixes #16646
Fixes #16358

This PR changes the Vite plugin to use the file-system to discover
potential class names instead of relying on the module-graph. This comes
after a lot of testing and various issue reports where builds that span
different Vite instances were missing class names.

Because we now scan for candidates using the file-system, we can also
remove a lot of the bookkeeping necessary to make production builds and
development builds work as we no longer have to change the resulting
stylesheet based on the `transform` callbacks of other files that might
happen later.

This change comes at a small performance penalty that is noticeable
especially on very large projects with many files to scan. However, we
offset that change by fixing an issue that I found in the current Vite
integration that did a needless rebuild of the whole Tailwind root
whenever any source file changed. Because of how impactful this change
is, I expect many normal to medium sized projects to actually see a
performance improvement after these changes. Furthermore we do plan to
continue to use the module-graph to further improve the performance in
dev mode.

## Test plan

- Added new integration tests with cases found across the issues above.
- Manual testing by adding a local version of the Vite plugin to repos
from the issue list above and the [tailwindcss
playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00
Robin Malfait
b9af722d13
Fix remove !important on CSS variable declarations (#16668)
This PR fixes an issue where `!important` is added to declarations that
define CSS variables. The `!important` should only be added to the other
declarations.

Before:
```css
.ease-out\! {
  --tw-ease: var(--ease-out) !important;
  transition-timing-function: var(--ease-out) !important;
}
```

After:
```css
.ease-out\! {
  --tw-ease: var(--ease-out);
  transition-timing-function: var(--ease-out) !important;
}
```

Fixes: https://github.com/tailwindlabs/tailwindcss/issues/16664
2025-02-19 17:20:09 +00:00
depfu[bot]
61af484ff4
Update all of nextjs 15.1.6 → 15.1.7 (patch) (#16654) 2025-02-19 13:16:11 +01:00
Philipp Spiess
541c3d2331
Prepare v4.0.7 release (#16629)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
v4.0.7
2025-02-18 16:51:20 +01:00
Philipp Spiess
23723e1e69
Statically link Visual Studio redistributables on Windows builds (#16602)
This PR statically links the C runtime on Windows builds so we no longer
require installing Visual Studio redistributables.

## Test plan

Relying on CI here to not break anything for Windows.
2025-02-18 13:45:04 +01:00
Philipp Spiess
ec1d7d4b85
Upgrade: Use latest tag for packages (#16620) 2025-02-18 11:57:51 +01:00
depfu[bot]
7ba4659fbe
Update postcss 8.5.1 → 8.5.2 (patch) (#16611) 2025-02-18 11:57:18 +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
f0141084c2
Pin exact versions of tailwindcss and @tailwindcss/* (#16623)
This PR fixes an issue where installing a specific version of
`@tailwindcss/postcss` and `tailwindcss` could still result in a version
mismatch. This is because we were relying on `^4.0.6` for example
instead of `4.0.6`.

This PR now pins all these versions to prevent this:
```
❯ pnpm why tailwindcss
devDependencies:
@tailwindcss/postcss 4.0.5
├─┬ @tailwindcss/node 4.0.6
│ └── tailwindcss 4.0.6
└── tailwindcss 4.0.5
```
2025-02-18 11:44:12 +01:00
Freeman
1c905f2adb
minor: better type safety and best practice to escape function (#16579) 2025-02-17 13:49:17 +01:00
depfu[bot]
8b1307614a
Update postcss-selector-parser 7.0.0 → 7.1.0 (minor) (#16558) 2025-02-17 12:07:13 +01:00