6127 Commits

Author SHA1 Message Date
Philipp Spiess
fac8f7df62
Vite: Emit build dependencies on partial rebuilds (#17347)
Closes #17339

This PR fixes an issue that caused changes to `@import`-ed CSS files to
no longer rebuild the stylesheet after a change was made to a template
file.

The change in the template file causes a fast-path in the Vite plugin
now after changes in 4.0.8: _partial rebuilds_. For that branch we do
not need to re-evaluate your input CSS since we know only the candidate
list changed. However, we still need to emit all build dependencies as
via `addWatchFile(…)`, otherwise Vite will not correctly register
updates for these dependencies anymore.

## Test plan

- Updated the kitchen-sink Vite update tests to ensure that an
`@import`-ed CSS file can be updated even after a partial rebuild.
- Ensure this works in our Vite playground
2025-03-24 11:54:55 +01:00
Philipp Spiess
42f68bb359
Fix changelog (#17348)
This change was added in #17320 which was not released yet, see commit
order on `main`:
https://github.com/tailwindlabs/tailwindcss/commits/main/
2025-03-24 11:39:22 +01:00
depfu[bot]
1c481b81c7
Update all of nextjs 15.2.1 → 15.2.3 (patch) (#17337) 2025-03-24 11:26:05 +01:00
Robin Malfait
5426baf358
Fix class extraction followed by ( in Pug (#17320)
This PR fixes an issue where a class shorthand in Pug followed by a `(`
is not properly extracted.

```html
<template lang="pug">
.text-sky-600.bg-neutral-900(title="A tooltip") This div has an HTML attribute.
</template>
```

The `text-sky-600` is extracted, but the `bg-neutral-900` is not.

Fixes: #17313

# Test plan

1. Added test to cover this case
2. Existing tests pass (after a few small adjustments due to _more_
extracted candidates, but definitely not _less_)
3. Verified against the original issue (top is before, bottom is this
PR)
<img width="1307" alt="image"
src="https://github.com/user-attachments/assets/68a0529f-63ad-477d-a342-e3f91c5a1690"
/>

We had this exact same bug in Slim
(https://github.com/tailwindlabs/tailwindcss/pull/17278). Since Pug,
Slim and Haml are the only pre processors we have right now with this
dot-separated class notation I also double checked the Haml
pre-processor if this is an issue or not (and it's already covered
there).

<img width="1263" alt="image"
src="https://github.com/user-attachments/assets/c658168b-d124-46c9-9ec0-9697151a57bf"
/>
2025-03-21 15:43:37 +01:00
Jordan Pittman
91c0d56d0f Revert "Temporarily revert changes to `@utility"
This reverts commit 1aab04cebff39a5a1b89ff22c6655cf1f549b7a1.
2025-03-20 17:00:23 -04:00
Jordan Pittman
498b06f2d9 Fix the other two workflows v4.0.15 2025-03-20 16:38:19 -04:00
Jordan Pittman
1aab04cebf Temporarily revert changes to `@utility
The github release workflow is currently broken on the 4.0.15 tag for whatever reason and we need to get the release out
2025-03-20 16:21:48 -04:00
Jordan Pittman
508746b996 Remove fetch-tags from release workflow
The current checkout action can encounter a bug where, if this workflow was triggered by a tag, this breaks
2025-03-20 15:55:21 -04:00
Robin Malfait
250c843341
Add suggestions when --spacing(--value(integer, number)) is used (#17308)
This PR adds suggestions to CSS based functional utilities when the
`--spacing(…)` function is used.

Given this CSS:

```css
@import "tailwindcss";

@theme {
  --spacing: 0.25rem;
  --spacing-custom: 123px;
}

@utility with-custom-spacing-* {
  size: --value(--spacing);
}

@utility with-integer-spacing-* {
  size: --spacing(--value(integer));
}

@utility with-number-spacing-* {
  size: --spacing(--value(number));
}
```
    
And this HTML:
    
```html
<div class="with-custom-spacing-custom"></div>
<div class="with-custom-spacing-0"></div>
<div class="with-custom-spacing-0.5"></div>
<div class="with-custom-spacing-1"></div>
<div class="with-custom-spacing-1.5"></div>

<div class="with-integer-spacing-custom"></div>
<div class="with-integer-spacing-0"></div>
<div class="with-integer-spacing-0.5"></div>
<div class="with-integer-spacing-1"></div>
<div class="with-integer-spacing-1.5"></div>

<div class="with-number-spacing-custom"></div>
<div class="with-number-spacing-0"></div>
<div class="with-number-spacing-0.5"></div>
<div class="with-number-spacing-1"></div>
<div class="with-number-spacing-1.5"></div>
```
    
Play: https://play.tailwindcss.com/tYDaSNiNtS


Then you will see the following suggestions:

```json
[
  "with-custom-spacing-custom",

  "with-integer-spacing-0",
  "with-integer-spacing-1",
  "with-integer-spacing-2",
  "with-integer-spacing-3",
  "with-integer-spacing-4",
  "with-integer-spacing-5",
  "with-integer-spacing-6",
  "with-integer-spacing-7",
  "with-integer-spacing-8",
  "with-integer-spacing-9",
  "with-integer-spacing-10",
  "with-integer-spacing-11",
  "with-integer-spacing-12",
  "with-integer-spacing-14",
  "with-integer-spacing-16",
  "with-integer-spacing-20",
  "with-integer-spacing-24",
  "with-integer-spacing-28",
  "with-integer-spacing-32",
  "with-integer-spacing-36",
  "with-integer-spacing-40",
  "with-integer-spacing-44",
  "with-integer-spacing-48",
  "with-integer-spacing-52",
  "with-integer-spacing-56",
  "with-integer-spacing-60",
  "with-integer-spacing-64",
  "with-integer-spacing-72",
  "with-integer-spacing-80",
  "with-integer-spacing-96",

  "with-number-spacing-0",
  "with-number-spacing-0.5",
  "with-number-spacing-1",
  "with-number-spacing-1.5",
  "with-number-spacing-2",
  "with-number-spacing-2.5",
  "with-number-spacing-3",
  "with-number-spacing-3.5",
  "with-number-spacing-4",
  "with-number-spacing-5",
  "with-number-spacing-6",
  "with-number-spacing-7",
  "with-number-spacing-8",
  "with-number-spacing-9",
  "with-number-spacing-10",
  "with-number-spacing-11",
  "with-number-spacing-12",
  "with-number-spacing-14",
  "with-number-spacing-16",
  "with-number-spacing-20",
  "with-number-spacing-24",
  "with-number-spacing-28",
  "with-number-spacing-32",
  "with-number-spacing-36",
  "with-number-spacing-40",
  "with-number-spacing-44",
  "with-number-spacing-48",
  "with-number-spacing-52",
  "with-number-spacing-56",
  "with-number-spacing-60",
  "with-number-spacing-64",
  "with-number-spacing-72",
  "with-number-spacing-80",
  "with-number-spacing-96"
]
```

This is because `--spacing(--value(number))` will include all default
spacing scale suggestions we use. And `--pacing(--value(integer))` will
include the same list but without the floating point numbers.

Follow up PR for: https://github.com/tailwindlabs/tailwindcss/pull/17304
2025-03-20 19:06:07 +00:00
Robin Malfait
a3316f2ef4
Add support for literal values in --value('…') and --modifier('…') (#17304)
This PR adds support for literal values inside the `--value('…')` and
`--modifier('…')` functions. This allows you to safelist some known
values you want to use:

E.g.:

```css
@utility tab-* {
  tab-size: --value('revert', 'initial');
}
```

This allows you to use `tab-revert` and `tab-initial` for example.
2025-03-20 19:02:02 +00:00
Philipp Spiess
4c57d9f734
Prepare v4.0.15 release (#17302)
<!--

👋 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

-->
2025-03-20 17:51:30 +01:00
Philipp Spiess
40a76e3380
Revert "Don't use color-mix(…) on currentColor (#17247)" and work around Preflight crash (#17306)
Closes #17194.

This reverts commit d6d913ec39e2a4cc0a70e9d21c484c6ed95d40ae.

The initial fix does breaks older versions of Chrome (where text won't
render with a color for the placeholder at all anymore) and the usage of
the _relative colors_ features also means it'll require a much newer
version of Safari/Firefox/Chrome to work correctly. The implementation
was also wrong as it always set alpha to the specific percent instead of
applying it additively (note that this can be fixed with `calc(alpha *
opacity)` though).

Instead we decided to fix this by adding a `@supports` query to
Preflight that only targets browsers that aren't affected by the crash.
We currently use the following workaround:

```css
/*
  Set the default placeholder color to a semi-transparent version of the current text color in browsers that do not
  crash when using `color-mix(…)` with `currentColor`. (https://github.com/tailwindlabs/tailwindcss/issues/17194)
*/

@supports (not (-webkit-appearance: -apple-pay-button)) /* Not Safari */ or
  (contain-intrinsic-size: 1px) /* Safari 17+ */ {
  ::placeholder {
    color: color-mix(in oklab, currentColor 50%, transparent);
  }
}
```

## Test plan

When testing the `color-mix(currentColor)` vs `oklab(from currentColor
…)` we created the following support matrix. I'm extending it with _our
fix_ which is the fix ended up using:

| Browser | Version | `color-mix(… currentColor …)` | `oklab(from
currentColor …)` | `@supports { color-mix(…) }` |
| ------- | ------- | ----------------------------- |
---------------------------- | ------- |
| Chrome | 111 |  |  |  |
| Chrome | 116 |  |  |  |
| Chrome | 131+ |  |  |  |
| Safari | 16.4 | 💥 |  |  |
| Safari | 16.6+ |  |  |  |
| Safari | 18+ |  |  |  |
| Firefox | 128 |  |  |  |
| Firefox | 133 |  |  |  |

Note that on Safari 16, this change makes it so that the browser does
not crash yet it still won't work either. That's because now the browser
will fall back to the default placeholder color instead. We used the
following play to test the fix: https://play.tailwindcss.com/RF1RYbZLKY
2025-03-20 17:43:32 +01:00
Philipp Spiess
3f313b4eb2
Ensure that the CSS file rebuilds if a new CSS variable is used from templates (#17301)
Fixes #17288

This PR fixes an issue where changes in template files that _only mark a
new CSS variable as used_ was not causing the CSS file to rebuilds.

This also fixes a flaky integration test that was caused by a missing
`await` line on the final assertion (causing it to sometimes run the
assertion in time while the test runner was still running).

## Test plan

Turns out we already had an integration test for this but it wasn't
correctly running due to the missing await.
2025-03-20 12:52:02 +01:00
Philipp Spiess
ca7b10e7d3
Make --theme(…) return CSS variables (#17036)
Closes #16945

This PR changes the `--theme(…)` function now return CSS `var(…)`
definitions unless used in places where `var(…)` is not valid CSS (e.g.
in `@media (width >= theme(--breakpoint-md))`):

```css
/* input */
@theme {
  --color-red: red;
}
.red {
  color: --theme(--color-red);
}

/* output */
:root, :host {
  --color-red: red;
}
.red {
  color: var(--color-red);
}
```

Furthermore, this adds an `--theme(… inline)` option to the `--theme(…)`
function to force the resolution to be inline, e.g.:

```css
/* input */
@theme {
  --color-red: red;
}
.red {
  color: --theme(--color-red inline);
}

/* output */
.red {
  color: red;
}
```

This PR also changes preflight and the default theme to use this new
`--theme(…)` function to ensure variables are prefixed correctly.

## Test plan

- Added unit tests and a test that pulls in the whole preflight under a
prefix theme.
2025-03-20 12:51:22 +01:00
Philipp Spiess
a1acaeeee0
Export PluginUtils from tailwindcss/plugin (#17299)
Closes #17293

We used to export this in v3 so might as well bring it back for the
`tailwindcss/plugin` export list.
2025-03-20 12:19:45 +01:00
Philipp Spiess
503bad4e75
Use bun-baseline for all x64 builds (#17267)
Closes #17259

This PR now also updates the MacOS x64 build to use `bun-baseline`,
meaning all x64 builds now use baseline for the improved hardware
compatibility.

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-03-20 12:16:04 +01:00
Robin Malfait
f369e22172
Fix class extraction followed by ( in Slim (#17278)
This PR fixes an issue where using the class shorthand in Slim
templates, followed by an `(` results in the last class being ignored.

E.g.:

```slim
body.border-t-4.p-8(class="#{body_classes}" data-hotwire-native="#{hotwire_native_app?}" data-controller="update-time-zone")
```

This is because we will eventually extract `p-8` but it's followed by an
invalid boundary character `(`.

To solve this, we make sure to replace the `(` with a space. We already
do a similar thing when the classes are followed by an `[`.

One caveat, we _can_ have `(` in our classes, like `bg-(--my-color)`.
But in my testing this is not something that can be used in the
shorthand version.

E.g.:
```slim
div.bg-(--my-color)
```

Compiles to:
```html
<div --my-color="" class="bg-"></div>
```

So I didn't add any special handling for this. Even when trying to
escape the `(`, `-` and `)` characters, it still doesn't work. E.g.:

```slim
div.bg-\(--my-color\)
```

Compiles to:
```html
<div class="bg-">\(--my-color\)</div>
```

# Test plan

1. Added test for the issue
2. Existing tests pass
3. Verified via the extractor tool:

| Before | After |
| --- | --- |
| <img width="958" alt="image"
src="https://github.com/user-attachments/assets/f72c420e-5429-424f-a01d-12f724062bf2"
/> | <img width="958" alt="image"
src="https://github.com/user-attachments/assets/b0cc8f2f-97a8-4fca-8813-3bb1da8d99a8"
/> |

---

Fixes: #17277
2025-03-19 09:50:30 -04:00
Robin Malfait
d698c10836
Ensure multiple --value(…) or --modifier(…) calls don't delete subsequent declarations (#17273)
This PR fixes a bug in the handling of `@utility`. Essentially if you
had a declaration where you used a `--modifier(…)` _and_ a `--value(…)`
and both caused the declaration to be removed, the declaration after the
current one would be removed as well.

This happened because 2 reasons:

1. Once we removed the declaration when we handled `--modifier(…)`, we
didn't stop the walk and kept going.
2. The `replaceWith(…)` code allows you to call the function multiple
times but immediately mutates the AST. This means that if you call it
multiple times that you are potentially removing / updating nodes
followed by the current one.

E.g.:

```css
@utility mask-r-* {
  --mask-right: linear-gradient(to left, transparent, black --value(percentage));
  --mask-right: linear-gradient(
    to left,
    transparent calc(var(--spacing) * --modifier(integer)),
    black calc(var(--spacing) * --value(integer))
  );
  mask-image: var(--mask-linear), var(--mask-radial), var(--mask-conic);
}
```

If this is used as `mask-r-10%`, then the first definition of
`--mask-right` is kept, but the second definition of `--mask-right` is
deleted because both `--modifier(integer)` and `--value(integer)` do not
result in a valid value.

However, the `mask-image` declaration was also removed because the
`replaceWith(…)` function was called twice. Once for
`--modifier(integer)` and once for `--value(integer)`.

# Test plan

1. Added a test to cover this case.
2. Existing tests pass.
3. Added a hard error if we call `replaceWith(…)` multiple times.
2025-03-19 13:25:53 +00:00
Devon Govett
cec7f0557b
Fix segmentation fault when loading @tailwindcss/oxide in a Worker thread (#17276)
When Tailwind is loaded in a Node Worker thread, it currently causes a
segmentation fault on Linux when the thread exits. This is due to a
longstanding issue in Rust that affects all native modules:
https://github.com/rust-lang/rust/issues/91979. I reported this years
ago but unfortunately it is still not fixed, and seems to have gotten
worse in Rust 1.83.0 and later. Looks like Tailwind recently updated
Rust versions and this issue started appearing when run in tools like
Parcel that use worker threads.

The workaround is to prevent the native module from ever being unloaded.
One way to do that is to always load the native module in the main
thread in addition to workers, but this is hard to enforce.
@Brooooooklyn found another method, which is to use a linker option for
this. I tested this on an Ubuntu system and verified it fixed the issue.
You can test with the following script.

```js
// test.js
const {Worker} = require('worker_threads');
new Worker('./worker.js');

// worker.js
require('@tailwindcss/oxide');
```

Without this change, a segmentation fault will occur.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-03-18 16:28:20 -04:00
Martijn Cuppens
1564bf092b
Remove redundant line-height from body (#15212)
It was added in #2729 to override line heights set on the body by
modern-normalize. However, it appears that modern-normalize never
included any line-height definitions—only a font-family rule was
present.

Ref:
https://github.com/sindresorhus/modern-normalize/blob/v1.1.0/modern-normalize.css

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-03-18 12:23:49 +01:00
Robin Malfait
d7c81164da
Pre process <template lang="…"> in Vue files (#17252)
This PR fixes an issue where `<template lang="…">…</template>` in Vue
files should be handled as-if it's the language specified in the `lang`
attribute.

To do this, we added a new Vue pre processor and run the content through
the same pre processor logic as we do for other languages.

Fixes: #17211

# Test plan

1. Added a test to verify this works
2. Existing tests still work

Visually verified against the reproduction in the issue:

| Before | After |
| --- | --- |
| <img width="1273" alt="image"
src="https://github.com/user-attachments/assets/d1accdeb-97cf-48ef-83fb-978832b3e599"
/> | <img width="1273" alt="image"
src="https://github.com/user-attachments/assets/ab7ec19c-b6c4-43be-8845-096ff4e58808"
/> |

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-03-17 13:54:37 +00:00
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