6106 Commits

Author SHA1 Message Date
Benoît Rouleau
ebfde316e1
Add safe alignment utilities (#14607)
Fixes #7929 and #12916

This PR adds [safe alignment
utilities](https://www.stefanjudis.com/today-i-learned/safe-unsafe-alignment-in-css-flexbox/)
to Tailwind 4. I opted to include them for every justify/align
content/items/self property, but only for the `end` and `center` values.
I know that it doesn't make sense for `start` (as the point of safe
alignment is to fall back to `start` when it overflows), but I'm not
sure about `space-between`, `space-around`, or other values. I certainly
never encountered a situation where I needed `safe` with those.
2025-03-17 14:09:15 +01:00
Bendegúz Hajnal
1d2d50ee66
Add items-baseline-last utility (#13888)
This PR adds the `items-first-baseline` and `items-last-baseline`
utility classes that can control
[`baseline-position`](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items#formal_syntax)
for the [align-items
property](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items#baseline).

Browser support:
- MDN:
https://developer.mozilla.org/en-US/docs/Web/CSS/align-items#browser_compatibility
- Can I use `first baseline`:
https://caniuse.com/?search=align%20items%3A%20first%20baseline
- Can I use `last baseline`:
https://caniuse.com/?search=align%20items%3A%20last%20baseline

The feature were requested multiple times:

- https://github.com/tailwindlabs/tailwindcss/discussions/13518
- https://github.com/tailwindlabs/tailwindcss/discussions/12406
- https://github.com/tailwindlabs/tailwindcss/discussions/11623
- https://github.com/tailwindlabs/tailwindcss/discussions/4855

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-03-17 13:51:13 +01:00
Philipp Spiess
d6d913ec39
Don't use color-mix(…) on currentColor (#17247)
Closes #17194

This PR works around a crash when rendering opacity on `currentColor`
(as used by the placeholder styles in preflight) on Safari 16.4 and
Safari 16.5. Unfortunately it seems that the [`color-mix(…)` function is
not compatible with `currentColor` for these versions of
Safari](https://stackoverflow.com/questions/76436497/the-color-mix-property-involving-currentcolor-causes-safari-to-crash).
We tried a few different ways to work around this without success:

- Using an `@supports` media query to target these Safari versions and
overwriting the placeholder still makes these browsers crash.
- Changing the way we apply opacity to `currentColor` in core doesn't
seem to work for non-placeholder values:
https://github.com/tailwindlabs/tailwindcss/issues/17194#issuecomment-2728949181
However, a wrong opacity is still better than a complete browser crash.

The work-around of using the `oklab(…)` function does seem to work for
`::placeholder` styles in preflight though according to our testing so
this PR applies this change to preflight.

## Test plan

- See https://play.tailwindcss.com/WSsSTLHu8h?file=css 
- Tested on Chrome/Safari 16.4/Safari 18.3/Firefox

<img width="564" alt="Screenshot 2025-03-17 at 11 32 47"
src="https://github.com/user-attachments/assets/cfd0db71-f39a-4bc0-bade-cea70afe50ae"
/>
2025-03-17 13:23:02 +01:00
Robin Malfait
448949318e
Fix inferring data type of border-[…] with multiple values (#17248)
This PR fixes an issue where arbitrary values such as
`border-[12px_4px]` were incorrectly producing `border-color` instead of
`border-width` values.

To solve it, I extended the `<line-width>` check to make sure that every
part of the value is a valid `<line-width>`.

In order for a `line-width` to be valid, every part should be one of:

1. A keyword: `thin`, `medium`, `thick`
2. A length: `12px`
3. A number: `0`

Fixes: #17221

# Test plan

1. Added test to verify this works
2. All existing tests pass
2025-03-17 12:35:55 +01:00
depfu[bot]
1a88518d80
Update autoprefixer 10.4.20 → 10.4.21 (patch) (#17229) 2025-03-17 09:59:18 +00:00
depfu[bot]
6b2b26287c
Update eslint 9.20.1 → 9.22.0 (minor) (#17207) 2025-03-17 10:55:59 +01:00
depfu[bot]
48957c5411
Update @playwright/test 1.50.1 → 1.51.0 (minor) (#17184) 2025-03-14 17:39:35 +01:00
Jordan Pittman
50562a9c94
Fix -bg-conic-* utilities (#17174)
The negative versions were missing the `deg` in `<number>deg` so they
didn’t work whoops
2025-03-13 10:13:56 -04:00
Robin Malfait
3c5903c1ee
Prepare v4.0.14 release (#17173) v4.0.14 2025-03-13 12:36:34 +01:00
Robin Malfait
221855b195
Ensure candidate extraction works as expected in Clojure/ClojureScript (#17087)
This PR adds a Clojure/ClojureScript pre processor to make sure that
candidate extraction works as expected.


| Before | After |
| --- | --- |
| <img width="908" alt="image"
src="https://github.com/user-attachments/assets/98aba8b6-0c44-47c6-b87c-ecf955e5e007"
/> | <img width="908" alt="image"
src="https://github.com/user-attachments/assets/7a5ec3eb-1630-4b60-80bd-c07bc2381d3b"
/> |

You can see that the classes preceded by `:` are not properly extracted
in the before case, but they are in the after case. We do extract a few
more cases now like `:class` and `:className` itself, but at least we
also retrieve all the `flex-*` classes.

We could also always ignore `:class` and `:className` literals:
<img width="908" alt="image"
src="https://github.com/user-attachments/assets/f5a67cae-25d6-4811-b777-f72fdb5ef450"
/>
2025-03-13 11:31:27 +01:00
Philipp Spiess
74ccde4672
Upgrade lightningcss to 1.29.2 in Standalone (#17169)
Closes #17162
Closes #17163
Closes #17164
Closes #17165
Closes #17166
Closes #17167

Noticed that there was a second place pulling in the lightningcss
version numbers that wasn't covered by the previous upgrade PR.
Thankfully the APIs of the node bindings are still compatible.

To avoid this mistake in the future I also exported these sub-packages
as a workspace dependency so all the version strings appear right next
to each other.
2025-03-13 11:18:06 +01:00
depfu[bot]
26f91d2a43
Update turbo 2.4.2 → 2.4.4 (patch) (#17157) 2025-03-13 10:50:06 +01:00
Robin Malfait
cedd54fecf
Fix variants with <digit>.</digit> are extracted correctly (#17153)
This PR fixes an issue where if you use a number with a decimal in a
variant then it wasn't picked up correctly.

E.g.:
```
<div class="2xl:flex 1.5xl:flex"></div>
            ^^^^^^^^                        Picked up
                     ^^^^^^^^^^             Not picket up
```

This PR fixes that behavior by applying the same rules for utilities
where a `.` is valid if it is surrounded by numbers.

# Test plan

1. Added test to ensure this is picked up
2. Existing tests pass
3. Ran the extractor on a real example with the following results:

| Before | After |
| --- | --- |
| <img width="821" alt="image"
src="https://github.com/user-attachments/assets/a77ed5e4-6848-4fe3-8cbf-cf61ff8db41d"
/> | <img width="821" alt="image"
src="https://github.com/user-attachments/assets/61aca66a-e38d-4b61-bf86-e6286a89a3d9"
/> |

They are crossed out just because it's not a default value we know in
the system, but you can see that the `1.` part is also extracted now.

Fixes: #17148
2025-03-12 21:04:11 +00:00
Philipp Spiess
215f4f348b
Add @source inline(…) (#17147)
This PR adds a new experimental feature that can be used to force-inline
utilities based on an input string. The idea is that all utilities
matching the source string will be included in your CSS:

```css
/* input.css */
@source inline('underline');

/* output.css */
.underline {
   text-decoration: underline;
}
```

Additionally, the source input is brace-expanded, meaning you can use
one line to inline a whole namespace easily:
```css
/* input.css */
@source inline('{hover:,}bg-red-{50,{100..900..100},950}');

/* output.css */
.bg-red-50 {
  background-color: var(--color-red-50);
}
.bg-red-100 {
  background-color: var(--color-red-100);
}
.bg-red-200 {
  background-color: var(--color-red-200);
}
.bg-red-300 {
  background-color: var(--color-red-300);
}
.bg-red-400 {
  background-color: var(--color-red-400);
}
.bg-red-500 {
  background-color: var(--color-red-500);
}
.bg-red-600 {
  background-color: var(--color-red-600);
}
.bg-red-700 {
  background-color: var(--color-red-700);
}
.bg-red-800 {
  background-color: var(--color-red-800);
}
.bg-red-900 {
  background-color: var(--color-red-900);
}
.bg-red-950 {
  background-color: var(--color-red-950);
}
@media (hover: hover) {
  .hover\\:bg-red-50:hover {
    background-color: var(--color-red-50);
  }
  .hover\\:bg-red-100:hover {
    background-color: var(--color-red-100);
  }
  .hover\\:bg-red-200:hover {
    background-color: var(--color-red-200);
  }
  .hover\\:bg-red-300:hover {
    background-color: var(--color-red-300);
  }
  .hover\\:bg-red-400:hover {
    background-color: var(--color-red-400);
  }
  .hover\\:bg-red-500:hover {
    background-color: var(--color-red-500);
  }
  .hover\\:bg-red-600:hover {
    background-color: var(--color-red-600);
  }
  .hover\\:bg-red-700:hover {
    background-color: var(--color-red-700);
  }
  .hover\\:bg-red-800:hover {
    background-color: var(--color-red-800);
  }
  .hover\\:bg-red-900:hover {
    background-color: var(--color-red-900);
  }
  .hover\\:bg-red-950:hover {
    background-color: var(--color-red-950);
  }
}

```

This feature is also compatible with the `not` keyword that we're about
to add to `@source "…"` in a follow-up PR. This can be used to set up an
ignore list purely in CSS. The following code snippet, for example, will
ensure that the `.container` utility is never created:

```css
@theme {
  --breakpoint-sm: 40rem;
  --breakpoint-md: 48rem;
  --breakpoint-lg: 64rem;
  --breakpoint-xl: 80rem;
  --breakpoint-2xl: 96rem;
}
@source not inline("container");
@tailwind utilities;
```

## Test plan

- See added unit tests
- The new brace expansion library was also benchmarked against the
popular `braces` library:
  ```
  clk: ~3.96 GHz
  cpu: Apple M4 Max
  runtime: bun 1.1.34 (arm64-darwin)

benchmark avg (min … max) p75 / p99 (min … top 1%)
-------------------------------------------
-------------------------------
braces 31.05 ms/iter 32.35 ms █ █
(28.14 ms … 36.35 ms) 35.14 ms ██ █
( 0.00 b … 116.45 mb) 18.71 mb ██████▁▁▁██▁█▁██▁█▁▁█

./brace-expansion 19.34 ms/iter 21.69 ms █
(12.53 ms … 26.63 ms) 25.53 ms ▅ ▅ █ █
( 0.00 b … 114.13 mb) 11.86 mb █▁▅▁██▁▅█▅█▅▁█▅█▅▅▁▅█

┌ ┐
╷┌────┬─┐ ╷
braces ├┤ │ ├─────┤
╵└────┴─┘ ╵
                              ╷        ┌────┬───┐       ╷
            ./brace-expansion ├────────┤    │   ├───────┤
                              ╵        └────┴───┘       ╵
└ ┘
12.53 ms 23.84 ms 35.14 ms
  ```

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-03-12 13:55:33 +00:00
Robin Malfait
ca408d0612
Do not extract candidates containing JS string interpolation pattern ${ (#17142)
This PR fixes an issue where often people run into issues where they try
to use string interpolation and it doesn't work. Even worse, it could
result in crashes because we will actually generate CSS. This fix only
filters out candidates with a pattern like `${`. If this occurs in a
string position it is fine.

Another solution would be to add a pre processor for JS/TS (and all
thousand file extension combinations) but the problem is that you can
also write JS in HTML files so we would have to pre process HTML as well
which would not be ideal.

# Test plan

1. Added tests to prove this works in arbitrary values, arbitrary
variables in both utilities and variants.
2. Existing tests pass.
3. Some screenshots with before / after situations:

Given this input:
```ts
let color = '#0088cc';
let opacity = 0.8;
let name = 'variable-name';
let classes = [
  // Arbitrary Properties
  `[color:${color}]`
  `[${property}:value]`,
  `[--img:url('https://example.com?q=${name}')]`, // WONT WORK BUT VALID CSS

  // Arbitrary Values
  `bg-[${color}]`,

  // Arbitrary Variables
  `bg-(--my-${color})`,
  `bg-(--my-color,${color})`,

  // Arbitrary Modifier
  `bg-red-500/[${opacity}]`,
  `bg-red-500/(--my-${name})`,
  `bg-red-500/(--my-opacity,${opacity})`,

  // Arbitrary Variant
  `data-[state=${name}]:flex`,
  `supports-(--my-${name}):flex`,
  `[@media(width>=${value})]:flex`,
];
```

This is the result:

| Before | After |
| --- | --- |
| <img width="908" alt="image"
src="https://github.com/user-attachments/assets/c64d1b16-d39d-48a6-a098-bc4477cb4b0a"
/> | <img width="908" alt="image"
src="https://github.com/user-attachments/assets/d71aaf62-5e13-4174-82bb-690eb81aaeaf"
/> |

Fixes: #17054
Fixes: #15853
2025-03-12 12:09:13 +01:00
Robin Malfait
4455048c0b
Prepare release 4.0.13 (#17132) v4.0.13 2025-03-11 17:58:53 +01:00
Robin Malfait
37062928f9
Improve candidate extraction when candidates contain . characters (#17113)
This PR fixes an issue where some classes weren't properly extracted due
to some incorrect assumptions in the pre processors.

Templating languages such as Haml, Slim and Pug allow you to write
classes in a shorter way that are not properly contained inside of
strings. E.g.:
```slim
p.flex.px-2
```

These candidates are not properly extracted because there are no
bounding characters like quotes. To solve this, we pre-process it and
replace `.` with ` ` characters. This results in something like:
```
p flex px-2
```

However, this has some challenges on its own. Candidates like `px-2.5`
cannot be written in this shorthand form, instead they need to be in
strings. Now we _cannot_ replace the `.` because otherwise we would
change `px-2.5` to `px-2 5` which is wrong.

The next problem is that we need to know when they are in a "string".
This has another set of problems because these templating languages
allow you to write normal text that will eventually be the contents of
the HTML tags.

```haml
.text-red-500.text-3xl
  | This text can't should be red
                 ^ Wait, is this the start of a string now???
```

In this example, if we consider the `'` the start of a string, when it's
clearly not, how would we know it's for _sure_ not a string?

This ended up as a bit of a rabbit hole, but we came up with another
approach entirely if we think about the original problem we want to
solve which is when do we change `.` to ` ` characters.

One of the rules in our current extractor is that a `.` has to be
between 2 numbers. Which works great in a scenario like: `px-2.5`.
However, if you look at Haml or Slim syntax, this is also allowed:

```slim
p.bg-red-500.2xl:flex
           ^^^ Uh oh...
```

In this scenario, a `.` is surrounded by numbers so we shouldn't replace
it with a space. But as you can see, we clearly do... so we need another
heuristic in this case.

Luckily, one of the rules in Tailwind CSS is that a utility cannot start
with a number, but a variant _can_. This means that if we see a scenario
like `<digit>.<digit>` then we can just check if the value after the `.`
is a valid variant or not.

In this case it is a valid variant so we _do_ want to replace the `.`
with a ` ` even though we do have the `<digit>.<digit>` format.

🥴

# Test plan

1. Added additional tests.
2. Existing tests still pass

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-03-11 17:53:16 +01:00
Robin Malfait
22746e6a6b
drop mise.toml 2025-03-11 17:49:30 +01:00
Philipp Spiess
9d7f25316e
Don't extract links as arbitrary properties (#17129)
Closes #17128

This PR prevents extraction of links inside square brackets as valid
candidate:

```
[https://example/]
```

We do this by throwing out arbitrary properties when the value starts
with a slash (`/`).

## Test plan

- Added unit test

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-03-11 16:40:24 +00:00
Philipp Spiess
785cadeb21
Upgrade lightningcss (#17043)
Part of #15897
2025-03-11 17:39:30 +01:00
Robin Malfait
9ddeb091d7
Add JSON pre-processor (#17125)
This PR adds a small JSON pre processor to improve parsing JSON files.

Due to the sheer amount of potential `[` and `]` brackets, it could be
that parsing JSON files are way slower than they need to be.

We saw this while debugging this issue:
https://github.com/tailwindlabs/tailwindcss/issues/17092


# Test plan

1. Added test to verify the pre processing works
2. Existing tests still pass
2025-03-11 12:15:48 +00:00
Philipp Spiess
8bc633bb17
Add .node and .wasm to known binary file list (#17123)
Closes #17092

After a lot of spelunking we found one specific reason for the very slow
builds in the repro from #17092: Turns our we are needlessly scanning
the binary `.node` extension for class names 😬. This PR adds `.wasm` and
`.node` to the list of known binary extensions.

## Test plan

 - Check out the repro from `#17092`
 - Delete the `.gitignore` file
 - Observe that builds are very slow (`527.79s`)
 - Add a _pnpm override_ to load local versions of Oxide
 - `pnpm build` now completes in ~50s
2025-03-11 13:05:51 +01:00
depfu[bot]
5e2633bb6d
Update all of nextjs 15.1.7 → 15.2.1 (minor) (#17118) 2025-03-11 12:06:10 +01:00
depfu[bot]
a57cd2dce8
Update postcss 8.5.2 → 8.5.3 (patch) (#17116) 2025-03-11 10:58:22 +01:00
Robin Malfait
de145c5b06
Refactor: use compile time type state pattern (#17083)
This PR implements the state machines using the type state pattern at
compile time (via generic types) instead of a runtime state variable.
There is no runtime check to see what state we are in, instead we
transition to the new state when it's necessary.

This has some nice performance improvements for some of the state
machines, e.g.:

```diff
- ArbitraryVariableMachine: Throughput: 744.92 MB/s
+ ArbitraryVariableMachine: Throughput:   1.21 GB/s
```

We also don't have to store the current state because each machine runs
to completion. It's during execution that we can move to a new state if
necessary.


Unfortunately the diff is a tiny bit annoying to read, but essentially
this is what happened:

### The `enum` is split up in it's individual states as structs:
```rs
enum State {
  A,
  B,
  C,
}
```
Becomes:
```rs
struct A;
struct B;
struct C;
```

### Generics

The current machine will receive a generic `State` that we can default
to the `IdleState`. Then we use `PhantomData` to "use" the type because
the generic type is otherwise not used as a concrete value, it's just a
marker.

```rs
struct MyMachine {}
```
Becomes:
```rs
struct MyMachine<State = Idle> {
  _state: std::marker::PhantomData<State>
}
```

### Split 

Next, the `next` function used to match on the current state, but now
each match arm is moved to a dedicated implementation instead:
```rs
impl Machine for MyMachine {
  fn next(&mut self) -> MachineState {
    match self.state {
      State::A => { /* … */ },
      State::B => { /* … */ },
      State::C => { /* … */ },
    }
  }
}
``` 
Becomes:
```rs
impl Machine for MyMachine<A> {
  fn next(&mut self) -> MachineState {
    /* … */
  }
}
impl Machine for MyMachine<B> {
  fn next(&mut self) -> MachineState {
    /* … */
  }
}
impl Machine for MyMachine<C> {
  fn next(&mut self) -> MachineState {
    /* … */
  }
}
```

It's a bit more verbose, but now each state is implemented in its own
block. This also removes 2 levels of nesting which is a nice benefit.
2025-03-10 14:08:39 -04:00
Robin Malfait
cc3e852791
Treat starting single quote as verbatim text in Slim (#17085)
This PR fixes an issue in Slim templates where a single quote `'` at the
start of the line (excluding white space) is considered a line indicator
for verbatim text. It is not considered a string in this scenario.

So something like this:
```slim
div
  'Foo'
```

Will compile to:
```html
<div>Foo'</div>
```

Fixes: #17081
2025-03-10 11:45:26 +01:00
Robin Malfait
bc5a8c3683
Ensure classes between > and < are properly extracted (#17094)
This PR fixes an issue where candidates inside `>…<` were not always
correctly extracted. This happens in XML-like languages where the
classes are inside of these boundaries.

E.g.:
```html
<!-- Fluid template language -->
<f:variable name="bgStyle">
  <f:switch expression="{data.layout}">
    <f:case value="0">from-blue-900 to-cyan-200</f:case>
    <!--              ^^^^^^^^^^^^^^^^^^^^^^^^^      -->
    <f:case value="1">from-cyan-600 to-teal-200</f:case>
    <f:defaultCase>from-blue-300 to-cyan-100</f:defaultCase>
  </f:switch>
</f:variable>
```


Fixes: https://github.com/tailwindlabs/tailwindcss/issues/17088

# Test plan

1. Added a new test
2. Existing tests pass
2025-03-10 11:28:08 +01:00
Robin Malfait
7005ad7e00
Add Haml pre processor (#17051)
This PR ensures we extract candidates from Haml files.

Fixes: #17050
2025-03-07 22:32:15 +01:00
Robin Malfait
2f28e5fbcb
Prepare v4.0.12 release (#17033)
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
v4.0.12
2025-03-07 12:38:53 +01:00
Robin Malfait
f498e4a97c
Ensure extracting candidates from JS embedded in a PHP string works as expected (#17031)
This PR fixes an issue where candidates are not properly extractor when
they end in `\`. This can happen if you embed a programming language
like JS inside another language like PHP where you need to escape some
strings.

Here is an example of Livewire flux:
```blade
@php
if ($sidebarIsStashable) {
    $attributes = $attributes->merge([
        'x-init' => '$el.classList.add(\'-translate-x-full\'); $el.classList.add(\'transition-transform\')',
        //                                                ^                                            ^
    ]);
}
@endphp

<div x-data {{ $attributes->class('border-r w-64 p-4 min-h-dvh max-h-dvh top-0 fixed left-0') }}>
    {{ $slot }}
</div>
```
Where the `\'` is causing some issues.

Another solution might be to add a custom pre processor for blade files
where we drop the escaped characters, but that felt overkill for now
because some escapes are still valid.

Fixes: #17023

# Test plan

1. Added a test to cover this case.
2. Existing tests still pass
2025-03-07 12:19:01 +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
Robin Malfait
d18fed1dca
Add razor/cshtml pre processing (#17027)
This PR fixes an issue in Razor template files where `@sm:flex` doesn't
work and `@@sm:flex` is required.

In Tailwind CSS v3, some people used a custom transform to replace `@@`
with just `@`. But in Tailwind CSS v4 we don't have this.

However, we can add a pre processor for `.cshtml` and `.razor` files.

Fixes: #17022
2025-03-07 12:02:07 +01:00
depfu[bot]
bd1e540776
Update h3 1.15.0 → 1.15.1 (patch) (#17010) 2025-03-07 12:00:44 +01:00
Robin Malfait
d0a97467f4
Improve boundary classification (#17005)
This PR cleans up the boundary character checking by using similar
classification techniques as we used for other classification problems.

For starters, this moves the boundary related items to its own file,
next we setup the classification enum.

Last but not least, we removed `}` as an _after_ boundary character, and
instead handle that situation in the Ruby pre processor where we need
it. This means the `%w{flex}` will still work in Ruby files.

---

This PR is a followup for
https://github.com/tailwindlabs/tailwindcss/pull/17001, the main goal is
to clean up some of the boundary character checking code. The other big
improvement is performance. Changing the boundary character checking to
use a classification instead results in:

Took the best score of 10 runs each:
```diff
- CandidateMachine: Throughput: 311.96 MB/s
+ CandidateMachine: Throughput: 333.52 MB/s
```

So a ~20MB/s improvement.

# Test plan

1. Existing tests should pass. Due to the removal of `}` as an after
boundary character, some tests are updated.
2. Added new tests to ensure the Ruby pre processor still works as
expected.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-03-07 00:00:54 +00:00
Robin Malfait
57e91a671a
Ensure } and { are valid boundary characters (#17001) 2025-03-06 20:40:03 +01:00
Robin Malfait
85c6e04f44
Ensure strings in Pug and Slim templates are handled correctly (#17000)
This PR fixes an issue where strings in the Pug and Slim pre-processor
were handled using the `string_machine`. However, the `string_machine`
is not for strings inside of Tailwind CSS classes which means that
whitespace is invalid.

This means that parts of the code that _are_ inside strings will not be
inside strings and parts of the code that are not inside strings will be
part of a potential string. This is a bit confusing to wrap your head
around, but here is a visual representation of the problem:

```
.join(' ')
        ^  3. start of new string, which means that the `)` _could_ be part of a string if a new `'` occurs later.
       ^   2. whitespace is not allowed, stop string
      ^    1. start of string
```

Fixes: #16998

# Test plan

1. Added new test
2. Existing tests still pass
3. Added a simple test helper to make sure that we can extract the
correct candidates _after_ pre-processing
2025-03-06 17:24:00 +01:00
Philipp Spiess
3d0606b82d
Fix sorting of classes that have undefined declarations (#16995)
Closes #16973

Declaration values of `undefined` are an implementation detail and
skipped when printing the CSS. Thus, these should not count towards the
sort order at a..

## Test plan

- See added regression test
2025-03-06 15:20:12 +01:00
Philipp Spiess
b676da8ace
Prepare v4.0.11 release (#16987) v4.0.11 2025-03-06 11:09:39 +00:00
Philipp Spiess
bff387bf6b
Ensure arbitrary variables with data types are extracted correctly (#16986)
Closes #16983

This PR fixes an issue where the arbitrary variable machine did not
extract data type correctly.

## Test plan

- Added regression test
2025-03-06 12:05:33 +01:00
Robin Malfait
af132fbedb
Fix Slim attributes (#16985)
This PR fixes an issue in Slim templates where the start of attributes
causes some candidates to be missing.


```slim
.text-xl.text-red-600[
  data-foo="bar"
]
  | This line should be red
```

Because of the `[` attached to the `text-red-600`, the `text-red-600`
was not extracted because `[` is not a valid boundary character.

To solve this, we copied the Pug pre processor and created a dedicated
Slim pre processor. Next, we ensure that we replace `[` with ` ` in this
scenario (by also making sure that we don't replace `[` where it's
important).

Additionally, we noticed that `.` was also replaced inside of arbitrary
values such as URLs. This has been fixed for both Pug and Slim.

Fixes: #16975

# Test plan

1. Added failing tests
2. Existing tests still pass
2025-03-06 11:59:14 +01:00
Philipp Spiess
617b7abb81
Oxide: Extract arbitrary container queries (#16984)
Closes #16982

Handle the case of variants looking like this: `@[32rem]:flex`.

## Test plan

Added regression tests

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-03-06 11:55:42 +01:00
Hyunmin Woo (Hanul)
4a0236471e
Ensure classes containing number followed by dash or underscore are extracted correctly (#16980)
Fixes #16978, and also added support for dash.
Classes like `text-theme1-primary` or `text-theme1_primary` should be
treated as valid.

If this approach is not aligned with the project’s direction or there
are any concerns, please feel free to close or edit this PR 😃

<br/>

### As is

Classes conatining number followed by dash or underscore (e.g.
`bg-theme1-primary`, `text-title1_strong`) are ignored, and utility
classes are not generated.

### To be

Classes conatining number followed by dash or underscore (e.g.
`bg-theme1-primary`, `text-title1_strong`) are treated as valid
tailwindcss classes

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-03-06 11:44:43 +01:00
Robin Malfait
9c59b07fb3
Ensure -- is allowed inside candidates (#16972)
This PR fixes an issue where named utilities that contain double dashes
`--` are not extracted correctly.

Some people use `--` in the middle of the utility to create some form of
namespaced utility.

Given this input:
```js
let x = 'foo--bar'
```

The extracted candidates before this change:
```js
[ "let", "x", "--bar" ]
```

The extracted candidates after this change:
```js
[ "let", "x", "foo--bar", "--bar" ]
```

The reason `--bar` is still extracted in both cases is because of the
CSS variable machine. We could improve its extraction by checking its
boundary characters but that's a different issue.

For now, the important thing is that `foo--bar` was extracted.

# Test plan

1. Added new test
2. Existing tests pass
2025-03-05 22:46:43 +01:00
Philipp Spiess
1638b16fee
Prepare v4.0.10 release (#16963)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
v4.0.10
2025-03-05 18:32:15 +01:00
Philipp Spiess
a8b64f3465
Vite: Work around crash when .svg file contains # or ? (#16957)
Closes #16877

This PR works around #16877 by not registering `.svg` files containing a
`#` or `?` as a watch dependency for now.

## Test plan

- Add a file to the Vite playground called `src/c#.svg`
- Observe Vite no longer prints errors
2025-03-05 14:55:07 +01:00
Robin Malfait
4c110014f1
Improve internal DX around byte classification [1] (#16864)
This PR improves the internal DX when working with `u8` classification
into a smaller enum. This is done by implementing a `ClassifyBytes` proc
derive macro. The benefit of this is that the DX is much better and
everything you will see here is done at compile time.

Before:
```rs
#[derive(Debug, Clone, Copy, PartialEq)]
enum Class {
    ValidStart,
    ValidInside,
    OpenBracket,
    OpenParen,
    Slash,
    Other,
}

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

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

    macro_rules! set_range {
        ($class:expr, $start:literal ..= $end:literal) => {
            let mut i = $start;
            while i <= $end {
                table[i as usize] = $class;
                i += 1;
            }
        };
    }

    set_range!(Class::ValidStart, b'a'..=b'z');
    set_range!(Class::ValidStart, b'A'..=b'Z');
    set_range!(Class::ValidStart, b'0'..=b'9');

    set!(Class::OpenBracket, b'[');
    set!(Class::OpenParen, b'(');

    set!(Class::Slash, b'/');

    set!(Class::ValidInside, b'-', b'_', b'.');

    table
};
```

After:
```rs
#[derive(Debug, Clone, Copy, PartialEq, ClassifyBytes)]
enum Class {
    #[bytes_range(b'a'..=b'z', b'A'..=b'Z', b'0'..=b'9')]
    ValidStart,

    #[bytes(b'-', b'_', b'.')]
    ValidInside,

    #[bytes(b'[')]
    OpenBracket,

    #[bytes(b'(')]
    OpenParen,

    #[bytes(b'/')]
    Slash,

    #[fallback]
    Other,
}
```

Before we were generating a `CLASS_TABLE` that we could access directly,
but now it will be part of the `Class`. This means that the usage has to
change:

```diff
- CLASS_TABLE[cursor.curr as usize]
+ Class::TABLE[cursor.curr as usize]
```

This is slightly worse UX, and this is where another change comes in. We
implemented the `From<u8> for #enum_name` trait inside of the
`ClassifyBytes` derive macro. This allows us to use `.into()` on any
`u8` as long as we are comparing it to a `Class` instance. In our
scenario:

```diff
- Class::TABLE[cursor.curr as usize]
+ cursor.curr.into()
```

Usage wise, this looks something like this:
```diff
        while cursor.pos < len {
-           match Class::TABLE[cursor.curr as usize] {
+           match cursor.curr.into() {
-               Class::Escape => match Class::Table[cursor.next as usize] {
+               Class::Escape => match cursor.next.into() {
                    // An escaped whitespace character is not allowed
                    Class::Whitespace => return MachineState::Idle,

                    // An escaped character, skip ahead to the next character
                    _ => cursor.advance(),
                },

                // End of the string
                Class::Quote if cursor.curr == end_char => return self.done(start_pos, cursor),

                // Any kind of whitespace is not allowed
                Class::Whitespace => return MachineState::Idle,

                // Everything else is valid
                _ => {}
            };

            cursor.advance()
        }

        MachineState::Idle
    }
}
```


If you manually look at the `Class::TABLE` in your editor for example,
you can see that it is properly generated at compile time.

Given this input:
```rs
#[derive(Clone, Copy, ClassifyBytes)]
enum Class {
    #[bytes_range(b'a'..=b'z')]
    AlphaLower,

    #[bytes_range(b'A'..=b'Z')]
    AlphaUpper,

    #[bytes(b'@')]
    At,

    #[bytes(b':')]
    Colon,

    #[bytes(b'-')]
    Dash,

    #[bytes(b'.')]
    Dot,

    #[bytes(b'\0')]
    End,

    #[bytes(b'!')]
    Exclamation,

    #[bytes_range(b'0'..=b'9')]
    Number,

    #[bytes(b'[')]
    OpenBracket,

    #[bytes(b']')]
    CloseBracket,

    #[bytes(b'(')]
    OpenParen,

    #[bytes(b'%')]
    Percent,

    #[bytes(b'"', b'\'', b'`')]
    Quote,

    #[bytes(b'/')]
    Slash,

    #[bytes(b'_')]
    Underscore,

    #[bytes(b' ', b'\t', b'\n', b'\r', b'\x0C')]
    Whitespace,

    #[fallback]
    Other,
}
```

This is the result:
<img width="1244" alt="image"
src="https://github.com/user-attachments/assets/6ffd6ad3-0b2f-4381-a24c-593e4c72080e"
/>
2025-03-05 14:00:07 +01:00
depfu[bot]
0b36dd51c3
Update @types/bun 1.2.3 → 1.2.4 (patch) (#16955) 2025-03-05 12:30:46 +00:00
Philipp Spiess
50cce50fa4 FreeBSD: rustup toolchain install 2025-03-05 12:48:24 +01:00
Philipp Spiess
9381de498f Don't use beta version for FreeBSD 2025-03-05 12:25:09 +01:00
Philipp Spiess
1c2ad57f03
Add pointer-* variants (#16946)
Adds new variants under a feature flag:

- `pointer-none`
- `pointer-coarse`
- `pointer-fine`
- `any-pointer-none`
- `any-pointer-coarse`
- `any-pointer-fine`
2025-03-05 11:05:17 +00:00