Commit graph

647 commits

Author SHA1 Message Date
Kirk Ouimet
d15d92ca60
Allow trailing dash in functional utility names (#19696)
## Problem

Tailwind 4.2.0 introduced stricter `@utility` name validation (#19524)
that rejects functional utility names where the root ends with a dash
after stripping the `-*` suffix. This breaks a valid and useful naming
pattern where a double dash separates the CSS property from a value
scale:

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

This produces: `border--0`, `border--1`, `border--2`, etc.

The error message is:

> `@utility border--*` defines an invalid utility name. Utilities should
be alphanumeric and start with a lowercase letter.

## Why this pattern matters

The double-dash convention creates a clear visual grammar in class
names. The first segment names the CSS property, and the double dash
separates it from the semantic scale value. In a dense className string
like `border border--0 background--0 content--4`, the scale values (0,
0, 4) are immediately scannable, distinct from the single-dash property
names around them.

This pattern is actively used in production design systems for semantic
color scales (background, content, border, shadow) with values from
0-10.

## Why the restriction is unnecessary

The validation comment states the concern is that `border--*` could
match the bare class `border-` when using default values. However, this
edge case is already handled:

1. **`findRoots` in `candidate.ts`** (line 887) already rejects empty
values: `if (root[1] === '') break`
2. **The Oxide scanner** already extracts double-dash candidates
correctly, as confirmed by existing tests: `("items--center",
vec!["items--center"])`

The candidate parser and scanner both handle this case. The validation
was an overcorrection.

## Changes

- Removed the trailing-dash check from `isValidFunctionalUtilityName` in
`utilities.ts`
- Updated the existing unit test from `['foo--*', false]` to `['foo--*',
true]`
- Added an integration test proving `@utility border--*` compiles
correctly with theme values

## Test results

All 4121 tests pass across the tailwindcss package, including the new
integration test.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-19 14:40:37 +00:00
Robin Malfait
1b16411919
4.2.0 (#19695)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2026-02-18 15:45:06 +01:00
Pavan Shinde
6118f4f6a7
Fix/misc docs and tests (#19652)
## Summary
This PR makes a few small consistency fixes:

- Fix outdated GitHub links that still point to the old
`tailwindcss/tailwindcss` org and update them to
`tailwindlabs/tailwindcss` (README + contributing docs + PR template).
- Fix punctuation in the contributing guide (“i.e., …”).
- Update two test titles to use “cannot” instead of “can not” for
consistency.

No behavior changes.

## Test plan
- Link check (manual): clicked the updated GitHub links in `README.md`,
`.github/CONTRIBUTING.md`, and `.github/PULL_REQUEST_TEMPLATE.md` to
confirm they resolve correctly.
- (Optional) `pnpm test` — not required since changes are
docs/test-title-only.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-18 12:23:48 +00:00
Cameron
5a4a7eba3a
fix(canonicalize): prevent collapse cache pollution across calls (#19675)
## Summary

fixes
https://github.com/schoero/eslint-plugin-better-tailwindcss/issues/321

This PR fixes an order-sensitive canonicalization bug. This bug caused
issues when running eslint-plugin-better-tailwindcss as the order in
which files are linted in is not consistent. This caused, in some
scenarios, `canonicalizeCandidates(..., { collapse: true,
logicalToPhysical: true, rem: 16 })` to stop collapsing valid
combinations (for example `h-4 + w-4 -> size-4`) after unrelated prior
calls.

To reproduce this issue:
```
# checkout this branch
$ git checkout c/fix-canonicalizeCandidates
# Revert the fix to the current `main` branch
$ git checkout main ./packages/tailwindcss/src/canonicalize-candidates.ts
# Run the tests
$ pnpm run test
```

This should produce a failure like so:

```

 FAIL   tailwindcss  src/canonicalize-candidates.test.ts > regressions > collapse canonicalization is not affected by previous calls
AssertionError: expected [ 'underline', 'h-4', 'w-4' ] to deeply equal [ 'underline', 'size-4' ]

- Expected
+ Received

  [
    "underline",
-   "size-4",
+   "h-4",
+   "w-4",
  ]

 ❯ src/canonicalize-candidates.test.ts:1167:66
    1165|     designSystem.canonicalizeCandidates(['underline', 'mb-4'], options)
    1166| 
    1167|     expect(designSystem.canonicalizeCandidates(target, options)).toEqual(['underline', 'size-4'])
       |                                                                  ^
    1168|   })
    1169| })
```

```
# reset all changes on this branch
git reset --hard
# run the tests again (they should now pass)
pnpm run test
```

The cause of this bug is that the canonicalization caches used
`DefaultMap` in places where lookups were expected to be read-only.
`DefaultMap.get` inserts missing entried, which mutated shared cache
state during intermediate lookups and made later canonicalization
results depend on prior calls.

By replacing the use of `DefaultMap` with a plain `Map`, it avoids
inserting into the map on lookup paths. I've polyfilled
[`Map#getOrInsert`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/getOrInsert)
as it is not widely available yet, and used that where appropriate.


## Test plan

I wrote a test that fails on `main` branch, I then fixed the issue, and
validated that the test now passes.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-18 12:16:59 +00:00
Adam Wathan
d0a5612872
Add mauve, olive, mist, and taupe color palettes (#19627)
## Summary

This PR adds four new neutral color palettes to Tailwind CSS: `mauve`,
`olive`, `mist`, and `taupe`. Each palette includes 11 shades (50, 100,
200, 300, 400, 500, 600, 700, 800, 900, 950) defined using the OKLch
color space for perceptually uniform color transitions.

These new palettes expand the available neutral color options beyond the
existing stone, slate, gray, zinc, and neutral palettes, providing
designers with more nuanced choices for different design systems and
brand aesthetics.

The changes include:
- Added color definitions to `packages/tailwindcss/src/compat/colors.ts`
- Added corresponding CSS custom properties to
`packages/tailwindcss/theme.css`

## Test plan

To verify these changes:
1. Build the project and ensure no compilation errors occur
2. Verify that the new color palettes are available in the Tailwind
config
3. Test that utilities like `bg-mauve-500`, `text-olive-600`,
`border-mist-300`, and `ring-taupe-400` work correctly
4. Confirm that the colors render with proper OKLch values in the
browser

https://claude.ai/code/session_01QhC1ZYkW3pCd55WbbqYBNV

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-18 11:52:35 +01:00
Pavan Shinde
d9fff9f595
docs: update package README CI badge to main (#19692)
## Summary
Update the GitHub Actions CI status badge URLs in `packages/*/README.md`
to track `branch=main` instead of `branch=next`. This matches the root
README, keeps the badges consistent across the repo, and ensures the
displayed CI status reflects the default branch.

## Test plan
Docs-only change.
Verified the updated badge URLs in the modified `packages/*/README.md`
files use `ci.yml?branch=main`.
2026-02-18 11:52:07 +01:00
Rózsa Zoltán
ed52d3e6c9
feat: handle backslash in @utility name (#19626)
Resolves #19607

<!--

👋 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 a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


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

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

I believe there is no obstacle to simply ignoring backslashes in the
name. This way, various validators - which are not aware of Tailwind
CSS's specific syntax (which allows the `/` character to be used
directly in utility names) - can still be bypassed using backslashes.
For example, instead of `@utility push-1/2`, one could use `@utility
push-1\/2`, while the end result would be identical.

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

I took a previous utility test as a baseline and extended it with
backslashes, and I expect the same result in the output as in the
original test case:

*
https://github.com/rozsazoltan/tailwindcss/blob/main/packages/tailwindcss/src/utilities.test.ts#L28553-L28567
(the original test case I started from)
*
https://github.com/rozsazoltan/tailwindcss/blob/feat/handle-blackslash-in-utility-name/packages/tailwindcss/src/index.test.ts#L4640-L4654
(the current PR's test)

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-17 21:12:20 +00:00
Robin Malfait
6eb3b32434
Allow multiples of .25 in aspect-* fractions (#19688)
This PR reduces the restrictions of the `aspect-*` utilities when
dealing with fractional values.

Up until now, the numbers had to be positive integers, so `aspect-1/2`
was valid, but `aspect-8.5/11` was not.

This PR allows for any multiple of `.25` as a valid value, so
`aspect-8.5/11` is now valid, but `aspect-8.3/11` is not, this will
still require `aspect-[8.3/11]` arbitrary value syntax to be valid.

This behavior of allowing multiples of `.25` is consistent with other
utilities that handle bare values such as `w-2.5`.

## Test plan

1. Existing tests pass
2. Added a test for `aspect-8.5/11`

Fixes: #19663
Closes: #19680, #19669
2026-02-17 19:32:31 +01:00
Pavan Shinde
8ed67bf551
Fix Tailwind CSS package README GitHub links (#19644)
<!--

👋 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 a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


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

-->

## Summary
These package READMEs referenced the wrong GitHub org
(tailwindcss/tailwindcss) and outdated branches (master/next) for common
project links.
Update them to point at tailwindlabs/tailwindcss on main for releases,
license, discussions, and contributing docs.
<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan
Docs-only change: No test required.
<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->
2026-02-07 19:26:38 -05:00
Robin Malfait
1638f35c3a
Bump dependencies (#19608)
This PR bumps a bunch of dependencies. This also moves a few
dependencies that we use in multiple packages to the pnpm catalog.

Closes: #19603, #19604, #19576, #19575, #19573, #19565, #19547, #19546,
#19545, #19609, #19581, #19620, #19619

- #19603
- #19604
- #19576
- #19575
- #19573
- #19565
- #19547
- #19546
- #19545
- #19609
- #19581
- https://github.com/tailwindlabs/tailwindcss/pull/19620 
- #19620
- #19619

## Test Plan

All tests in CI should still pass. [ci-all]
2026-02-04 12:38:50 +01:00
Robin Malfait
df96ea5eba
Fix infinite loop when using @variant inside @custom-variant that points to another @custom-variant (#19633)
This PR fixes an infinite loop when you use a `@variant` inside of a
`@custom-variant`, where the `@variant` used is another
`@custom-variant`.

The issue stems from the fact that a `@custom-variant` can use a `@slot`
that we have to replace with the proper AST nodes. However in this
setup, the AST nodes will include a `@slot` node as well, which causes
us to replace the `@slot` again, and so on, causing an infinite loop.

```css
@custom-variant a {
  @slot;
}

@custom-variant b {
  @variant a {
    @slot;
  }
}
```

The solution here is to replace the `@slot` nodes and then skip walking
the nodes that were just inserted. This does mean that we end up with a
`@slot` node in the final AST but that's not a real issue because that
will get replaced later when handling the next `@custom-variant`.

## Test plan

1. Existing tests still pass
2. Added a regression test to ensure that the infinite loop does not
happen anymore
3. Added additional tests to ensure that the behavior is correct

Thanks @wongjn for your initial debugging help and providing a test case
as well!

Fixes: #19618
2026-02-02 14:42:31 +01:00
Adam Wathan
d52c94ff5f
Simplify logical sizing utility theme namespaces (#19625)
Update inline-size and block-size utilities to only read from --spacing
and --container theme keys, removing backwards-compat references to
--width, --height, --min-width, --min-height, --max-width, and
--max-height.

Since these are new utilities with no backwards compatibility concerns,
the simpler approach is preferred:
- inline/min-inline/max-inline: --spacing, --container
- block/min-block/max-block: --spacing only

https://claude.ai/code/session_01WhrjmutxsLP753VUtFy24S

<!--

👋 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 a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


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

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-30 10:40:51 -05:00
Adam Wathan
53205f5dc5
Add font-features-* utility for font-feature-settings (#19623)
Add a new arbitrary-value-only utility `font-features-*` that sets the
`font-feature-settings` CSS property. This utility only accepts
arbitrary
values (e.g., `font-features-["smcp"]`,
`font-features-[var(--features)]`).

The utility is sorted directly after `font-family` in the property
order.

https://claude.ai/code/session_01EAccbTHJ9dTUJ53ttq2jc4

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-30 14:21:16 +00:00
Adam Wathan
5395c7e764
Add logical inset utilities (inset-s, inset-e, inset-bs, inset-be) (#19613)
## Summary

This PR adds support for logical inset utilities that map to CSS logical
properties:

- `inset-s` → `inset-inline-start`
- `inset-e` → `inset-inline-end`
- `inset-bs` → `inset-block-start`
- `inset-be` → `inset-block-end`

These utilities complement the existing `inset-x` (inline) and `inset-y`
(block) utilities, providing more granular control over positioning in a
direction-aware manner. This aligns with CSS logical properties and
improves support for internationalization (RTL/LTR languages).

### Changes

1. **property-order.ts**: Added `inset-block-start` and
`inset-block-end` to the property ordering list to ensure consistent
cascade ordering
2. **utilities.ts**: Added four new utility mappings for the logical
inset properties
3. **utilities.test.ts**: Added comprehensive test coverage for all four
new utilities, including:
- Valid class generation with various value types (custom spacing,
percentages, arbitrary values)
   - Negative value support
   - Invalid class rejection (malformed syntax, invalid modifiers)

## Test plan

All changes are covered by unit tests in `utilities.test.ts`:
- `inset-s` test: 8 valid classes + 13 invalid class assertions
- `inset-e` test: 8 valid classes + 13 invalid class assertions  
- `inset-bs` test: 8 valid classes + 13 invalid class assertions
- `inset-be` test: 8 valid classes + 13 invalid class assertions

Each test verifies correct CSS output generation and proper rejection of
malformed utilities.

https://claude.ai/code/session_01JcYXVAMawRuntKatjku1WZ

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-30 14:13:56 +00:00
Adam Wathan
3d1e654c02
Add logical sizing utilities (inline-size, block-size) (#19612)
## Summary

Adds new utilities for CSS logical properties that provide
writing-direction-aware alternatives to width and height:

- `inline-*` for `inline-size` (logical equivalent of width)
- `min-inline-*` for `min-inline-size` (logical equivalent of min-width)
- `max-inline-*` for `max-inline-size` (logical equivalent of max-width)
- `block-*` for `block-size` (logical equivalent of height)
- `min-block-*` for `min-block-size` (logical equivalent of min-height)
- `max-block-*` for `max-block-size` (logical equivalent of max-height)

## New Utilities

### inline-size utilities

| Class | CSS Property |
|-------|-------------|
| `inline-auto` | `inline-size: auto` |
| `inline-full` | `inline-size: 100%` |
| `inline-min` | `inline-size: min-content` |
| `inline-max` | `inline-size: max-content` |
| `inline-fit` | `inline-size: fit-content` |
| `inline-screen` | `inline-size: 100vw` |
| `inline-svw` | `inline-size: 100svw` |
| `inline-lvw` | `inline-size: 100lvw` |
| `inline-dvw` | `inline-size: 100dvw` |
| `inline-{spacing}` | `inline-size: {value}` (e.g., `inline-4`,
`inline-px`) |
| `inline-{fraction}` | `inline-size: {percent}` (e.g., `inline-1/2`,
`inline-3/4`) |
| `inline-[{value}]` | `inline-size: {value}` (arbitrary values) |

### min-inline-size utilities

| Class | CSS Property |
|-------|-------------|
| `min-inline-auto` | `min-inline-size: auto` |
| `min-inline-full` | `min-inline-size: 100%` |
| `min-inline-min` | `min-inline-size: min-content` |
| `min-inline-max` | `min-inline-size: max-content` |
| `min-inline-fit` | `min-inline-size: fit-content` |
| `min-inline-screen` | `min-inline-size: 100vw` |
| `min-inline-svw` | `min-inline-size: 100svw` |
| `min-inline-lvw` | `min-inline-size: 100lvw` |
| `min-inline-dvw` | `min-inline-size: 100dvw` |
| `min-inline-{spacing}` | `min-inline-size: {value}` |
| `min-inline-[{value}]` | `min-inline-size: {value}` (arbitrary values)
|

### max-inline-size utilities

| Class | CSS Property |
|-------|-------------|
| `max-inline-none` | `max-inline-size: none` |
| `max-inline-full` | `max-inline-size: 100%` |
| `max-inline-min` | `max-inline-size: min-content` |
| `max-inline-max` | `max-inline-size: max-content` |
| `max-inline-fit` | `max-inline-size: fit-content` |
| `max-inline-screen` | `max-inline-size: 100vw` |
| `max-inline-svw` | `max-inline-size: 100svw` |
| `max-inline-lvw` | `max-inline-size: 100lvw` |
| `max-inline-dvw` | `max-inline-size: 100dvw` |
| `max-inline-{spacing}` | `max-inline-size: {value}` |
| `max-inline-[{value}]` | `max-inline-size: {value}` (arbitrary values)
|

### block-size utilities

| Class | CSS Property |
|-------|-------------|
| `block-auto` | `block-size: auto` |
| `block-full` | `block-size: 100%` |
| `block-min` | `block-size: min-content` |
| `block-max` | `block-size: max-content` |
| `block-fit` | `block-size: fit-content` |
| `block-screen` | `block-size: 100vh` |
| `block-lh` | `block-size: 1lh` |
| `block-svh` | `block-size: 100svh` |
| `block-lvh` | `block-size: 100lvh` |
| `block-dvh` | `block-size: 100dvh` |
| `block-{spacing}` | `block-size: {value}` (e.g., `block-4`,
`block-px`) |
| `block-{fraction}` | `block-size: {percent}` (e.g., `block-1/2`,
`block-3/4`) |
| `block-[{value}]` | `block-size: {value}` (arbitrary values) |

### min-block-size utilities

| Class | CSS Property |
|-------|-------------|
| `min-block-auto` | `min-block-size: auto` |
| `min-block-full` | `min-block-size: 100%` |
| `min-block-min` | `min-block-size: min-content` |
| `min-block-max` | `min-block-size: max-content` |
| `min-block-fit` | `min-block-size: fit-content` |
| `min-block-screen` | `min-block-size: 100vh` |
| `min-block-lh` | `min-block-size: 1lh` |
| `min-block-svh` | `min-block-size: 100svh` |
| `min-block-lvh` | `min-block-size: 100lvh` |
| `min-block-dvh` | `min-block-size: 100dvh` |
| `min-block-{spacing}` | `min-block-size: {value}` |
| `min-block-[{value}]` | `min-block-size: {value}` (arbitrary values) |

### max-block-size utilities

| Class | CSS Property |
|-------|-------------|
| `max-block-none` | `max-block-size: none` |
| `max-block-full` | `max-block-size: 100%` |
| `max-block-min` | `max-block-size: min-content` |
| `max-block-max` | `max-block-size: max-content` |
| `max-block-fit` | `max-block-size: fit-content` |
| `max-block-screen` | `max-block-size: 100vh` |
| `max-block-lh` | `max-block-size: 1lh` |
| `max-block-svh` | `max-block-size: 100svh` |
| `max-block-lvh` | `max-block-size: 100lvh` |
| `max-block-dvh` | `max-block-size: 100dvh` |
| `max-block-{spacing}` | `max-block-size: {value}` |
| `max-block-[{value}]` | `max-block-size: {value}` (arbitrary values) |

## Test plan

- [x] Added comprehensive unit tests for all new utilities
- [ ] Verify utilities work correctly in RTL layouts
- [ ] Test with vertical writing modes

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-30 13:46:37 +00:00
Adam Wathan
991072fcac
Add logical block-start/block-end border, margin, and padding utilities (#19601)
## Summary

This PR adds support for logical block-start and block-end CSS
properties in Tailwind CSS utilities. These properties are part of the
CSS Logical Properties specification and provide writing-mode-aware
alternatives to physical top/bottom properties.

The following new utilities are added:
- **Border utilities**: `border-bs-*` and `border-be-*` (for
`border-block-start` and `border-block-end`)
- **Margin utilities**: `mbs-*` and `mbe-*` (for `margin-block-start`
and `margin-block-end`)
- **Scroll margin utilities**: `scroll-mbs-*` and `scroll-mbe-*` (for
`scroll-margin-block-start` and `scroll-margin-block-end`)
- **Scroll padding utilities**: `scroll-pbs-*` and `scroll-pbe-*` (for
`scroll-padding-block-start` and `scroll-padding-block-end`)
- **Padding utilities**: `pbs-*` and `pbe-*` (for `padding-block-start`
and `padding-block-end`)

These utilities follow the same patterns as their existing
inline-start/inline-end counterparts and support all standard modifiers
(arbitrary values, negative values, opacity modifiers, etc.).

## Changes

1. **utilities.ts**: Added new utility definitions for all
block-start/block-end properties
2. **property-order.ts**: Updated CSS property ordering to include the
new logical properties in the correct cascade order
3. **utilities.test.ts**: Added comprehensive test cases for all new
utilities
4. **utilities.test.ts.snap**: Updated snapshots showing generated CSS
for the new utilities

## Test plan

All changes are covered by existing test infrastructure:
- Unit tests verify correct CSS generation for each utility variant
- Snapshot tests validate the complete output for border utilities
- Invalid modifier combinations are tested to ensure proper error
handling
- Tests cover standard values, arbitrary values, negative values, and
opacity modifiers

https://claude.ai/code/session_01V89HxEtppGVvMtuPbYrayM

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-30 14:37:51 +01:00
Robin Malfait
566eea4ff9
Improve walk performance (#19614)
This PR improves the walk performance by a little bit, but also reduces
memory usage. It's a tiny change and all the APIs remain the same. Not
the most important PR, but we had this idea on the backburner so we
decided to just implement it.

Thanks @thecrypticace!

## Test plan

- All the existing tests should pass.

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2026-01-29 15:49:31 +01:00
Robin Malfait
f82ac39ff2
Improve @utility name validation (#19524)
This PR improves the validation of allowed `@utility …` names.

Each `@utility` name should be a valid Tailwind CSS class, so new
syntaxes should not be allowed, e.g. `foo/bar/baz` would be invalid.

We already enforce this behavior but not consistently. The Oxide scanner
that scans all your source files for potential Tailwind CSS classes does
enforce all of these rules already. So if you used `@utility foo/bar/baz
{}`, the Oxide scanner would not pick up `foo/bar/baz` as a valid class
name, so for that reason it's not a breaking change.

Where we didn't enforce it is in places where you use the
development-only CDN or Tailwind Play. That's because those environments
don't use the Oxide at all, and get the classes from the DOM directly
and pass it to Tailwind's compiler.

This PR moves some of these validation rules into Tailwind's core when
defining custom `@utility` utilities.

Fixes: #19505

### Test plan

1. Existing tests still pass
2. Added a regression test for the linked issue
3. Added new tests with valid / invalid `@utility` names

I also confirmed with Oxide to know which classes were actually valid
and which ones are invalid.

Given this input CSS:
```css
@utility foo { color: red }
@utility foo_ { color: red }     /* This one looks invalid to me, but it works today */
                                 /* and I don't want to introduce unnecessary breaking changes. */
@utility foo-1.5 { color: red }
@utility foo-123 { color: red }
@utility -foo { color: red }
@utility foo-bar { color: red }
@utility foo_bar { color: red }
@utility foo-50% { color: red }
@utility foo-1/2 { color: red }
```

And this HTML:
```html
<!-- Extracted: -->
<div class="foo foo_ foo-123 -foo foo-bar foo_bar foo-50% foo-1/2 foo-1.5"></div>

<!-- Not Extracted: -->
<div class="Foo -Foo foo-1/ foo- foo-p% foo-1..5 foo.bar foo..bar "></div>
```

Then all classes in the `Extracted` section are found. One funny thing
in the not extracted section is that the `bar` in `foo.bar` and
`foo..bar` is also extracted. Feels like a potential bug, but out of
scope for this PR.

<img width="766" height="164" alt="image"
src="https://github.com/user-attachments/assets/fafbaa35-2730-4f61-9b15-6690b02ec686"
/>
2026-01-06 12:54:43 +01:00
depfu[bot]
8d5e955058
Update dedent 1.7.0 → 1.7.1 (patch) (#19484)
Here is everything you need to know about this upgrade. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ dedent (1.7.0 → 1.7.1) · [Repo](https://github.com/dmnd/dedent)
· [Changelog](https://github.com/dmnd/dedent/blob/main/CHANGELOG.md)
















---
![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-12-24 10:31:02 -05:00
xibeiyoumian
d979daacc6
chore: fix some typos in comments (#19475)
<!--

👋 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 a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


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

-->

## Summary

fix some typos in comments

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

Signed-off-by: xibeiyoumian <xibeiyoumian@outlook.com>
2025-12-22 13:09:19 +00:00
Justin Wong
7fcdd84e56
Allow whitespace around @source inline() arg (#19461)
## Summary

Inspired by #19460, relaxes whitespace syntax around `@source
inline(…):`

### Before

```css
/* ❌ Error: `@source` paths must be quoted. */
@source inline( "underline" );
@source inline(
  "underline"
);
```

### After

```css
/* ✅ Generates the class names as normal. */
@source inline( "underline" );
@source inline(
  "underline"
);
```

## Test plan

Added tests to `packages/tailwindcss/src/index.test.ts`.
2025-12-18 10:51:00 -05:00
Rasso Hilber
72dea0cfea
Clarify the replaceAlpha function comment (#19457)
## Summary

Updated the comment of the `replaceAlpha` function to clarify the
function's purpose.

## Test plan

n/a
2025-12-17 22:15:42 -05:00
Rasso Hilber
dd1ca3c709
Do not wrap color-mix in a @supports rule if one already exists (#19450)
Related issue: #19445 

## Summary

Fixes an issue where tailwindcss wraps `color-mix` inside a `@supports`
block even if the original code already checks for support.

Uncompiled Code:

```css
@utility foo {
  @supports (color: color-mix(in lab, red, red)) {
    background: color-mix(in lab, var(--color-1), var(--color-2));
  }
}
```

### Compiled code: Current behavior

https://play.tailwindcss.com/KSvR7wefdh?file=css

```css
@layer utilities {
  .foo {
    @supports (color: color-mix(in lab, red, red)) {
      background: var(--color-1);
      @supports (color: color-mix(in lab, red, red)) {
        background: color-mix(in lab, var(--color-1), var(--color-2));
      }
    }
}
```

### Compiled code: Fixed behavior

```css
@layer utilities {
  @supports (color: color-mix(in lab, red, red)) {
    .foo {
      background: color-mix(in lab, var(--color-1), var(--color-2));
    }
}
```

## Test plan

I have tested this by writing a new test that was at first failing. Now
it passes.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-12-16 16:57:36 -05:00
Jordan Pittman
9b32f7cb2f
Release v4.1.18 (#19431) 2025-12-11 10:55:25 -05:00
Jordan Pittman
820d90797c
Expose candidatesToAst to the language server (#19405)
This will be used to improve performance and potentially enable future
features that require generated CSS source locations.

Note: This is still 100% internal API. You can only access this via
`__unstable__loadDesignSystem` for a reason. We may chance the structure
of the arguments and/or return values as needed.
2025-12-08 10:54:42 -05:00
Jordan Pittman
478e959097
Don’t emit color-mix fallback rules inside @keyframes (#19419)
Fixes #19417
2025-12-08 09:40:16 -05:00
Jordan Pittman
a5f4644507
Validate named values in candidate parser (#19397)
Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1506
2025-12-03 15:06:25 -05:00
Robin Malfait
229121dd14
Canonicalization: combine text-* and leading-* classes (#19396)
This PR improves the canonicalization when using `text-*` and
`leading-*` utilities together.

When using classes such as:
```html
<div class="text-sm leading-7"></div>
```

Then the canonical way of writing this is:
```html
<div class="text-sm/7"></div>
```

Similarly, if you already have a modifier applied, and add a new
line-height utility. It will also combine them into the canonical form:
```html
<div class="text-sm/6 leading-7"></div>
```
becomes:
```html
<div class="text-sm/7"></div>
```

This is because the final CSS output of `text-sm/6 leading-7` is:
```css
/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */
.text-sm\/6 {
  font-size: var(--text-sm, 0.875rem);
  line-height: calc(var(--spacing, 0.25rem) * 6);
}
.leading-7 {
  --tw-leading: calc(var(--spacing, 0.25rem) * 7);
  line-height: calc(var(--spacing, 0.25rem) * 7);
}
@property --tw-leading {
  syntax: "*";
  inherits: false;
}
```

Where the `line-height` of the `leading-7` class wins over the
`line-height` of the `text-sm/6` class.

### Implementation

#### On the fly pre-computation

Right now, we are not using any AST based transformations yet and
instead rely on a pre-computed list. However, with arbitrary values we
don't have pre-computed values for `text-sm/123` for example.

What we do instead is if we see a utility that sets `line-height` and
other utilities set `font-size` then we pre-compute those computations
on the fly.

We will prefer named font-sizes (such as `sm`, `lg`, etc). We will also
prefer bare values for line-height (such as `7`) over arbitrary values
(such as `[123px]`).

#### Canonicalization of the CSS AST

Another thing we had to do is to make sure that when multiple
declarations of the same property exist, that we only keep the last one.
In the real world, multiple declarations of the same value is typically
used for fallback values (e.g.: `background-color: #fff;
background-color: oklab(255 255 255 / 1);`).

But for our use case, I believe we can safely remove the earlier
declarations to make the most modern and thus the last declaration win.

#### Trying combinations based on `property` only

One small change we had to make is that we try combinations of utilities
based on property only instead of property _and_ value. This is
important for cases such as `text-sm/6 leading-7`. These 2 classes will
set a `lin-height` of `24px` and `28px` respectively so they will never
match.

However, once combined together, there will be 2 line-height values, and
the last one wins. The signature of `text-sm/6 leading-7` becomes:
```css
.x {
  font-size: 14px;          /* From text-sm/6 */
  line-height: 24px;        /* From text-sm/6 */
  line-height: 28px;        /* From leading-7 */
}
```

↓↓↓↓↓↓↓↓↓

```css
.x {
  font-size: 14px;          /* From text-sm/6 */
  line-height: 28px;        /* From leading-7 */
}
```

This now shows that just `text-sm/7` is the canonical form. Because it
produces the same final CSS output.


## Test plan

1. All existing tests pass
2. Added a bunch of new tests where we combine `text-*` and `leading-*`
utilities with named, bare and arbitrary values. Even with existing
modifiers on the text utilities.

<img width="1010" height="1099" alt="image"
src="https://github.com/user-attachments/assets/d2775692-a442-4604-8371-21dacf16ebfc"
/>
2025-12-01 16:01:24 +01:00
Jordan Pittman
243615e3f2
Handle backwards compatibility for content theme from JS configs (#19381)
Fixes #19343

This PR makes it so the `content-*` utilities read from the
`--content-*` theme namespace. This change is **purely for backwards
compatibility with Tailwind CSS v3**. It is recommended you use
arbitrary values with the `content-*` utility instead.
2025-11-29 11:19:37 -05:00
Jordan Pittman
764275143e
Improve compatibility with special default values in JS configs (#19348)
Fixes #19345

In v3 the `ringColor.DEFAULT` option was used as the default color for
ring utilities (when it was defined). This currently gets translated as
`--ring-color` but that doesn't work this way in v4. Instead it should
translate to `--default-ring-color` and *not* `--ring-color`.

I've also tweaked the upgrade tool to handle this properly as well.
2025-11-28 15:45:10 -05:00
Robin Malfait
af481175e7
remove unnecessary intermediate check 2025-11-26 12:34:42 +01:00
Robin Malfait
9e436f7751
Try to canonicalize any arbitrary utility to a bare value (#19379)
This PR adds an improvement to our canonicalization logic when dealing
with arbitrary values. When trying to canonicalize utilities, we make
use of the intellisense suggestions list where we typically use
multiples of the spacing scale.

This means that a value like `gap-[128px]` gets properly canonicalized
to `gap-32`. However, when you try a value that we typically don't
suggest such as `gap-[116px]` then it doesn't get canonicalized at all.

This PR fixes that by trying to use the spacing scale and convert `116px
/ 4px` and try the `gap-29` utility instead.

This is done by canonicalizing the incoming arbitrary value and the
spacing multipliers such that `--spacing: 0.25rem` and `--spacing: 4px`
both work as expected.

### Test plan

1. Added some tests with a spacing scale of `0.25rem` (which is the
default)
2. Added some tests with the same spacing scale in a different unit
`4px`
3. Added some tests with a different spacing scale `1px`

Also had to update 1 test that now gets canonicalized properly, e.g.:
`w-[124px]` → `w-31`.
2025-11-26 11:07:21 +00:00
Robin Malfait
479b725cd3
Bump Vitest to v4 (#19216)
This PR bumps Vitest from v2 to v4. As far as I know we don't use any
Vitest specific features in our tests, but had to upgrade the
`vitest.workspace.ts` file to a `vitest.config.ts` file instead.

The only features we use are the typical `describe`, `it`, `test`, and
`expect` functions.

The only other part we use is `vi.spyOn` and `vi.fn` but those didn't
change in API either.

The test shards were removed to prevent errors. Not all suites have
enough files / tests to be broken up into 3 parts so Vitest now errors
when that happens.

### Test plan

1. All tests should pass in CI.
2. All integration tests should pass in CI.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-11-20 18:16:20 -05:00
Jordan Pittman
642b9b8576
Don’t unconditionally convert config keys to kebab-case (#19337)
Fixes #18114
Closes #18115

This PR changes JS config handling such that we always preserve casing
for theme keys (when possible).

Now there are *two* exceptions to this rule:

1. Top-level keys in the theme *do* get converted to kebab-case. As CSS
variables are not generated from these this shouldn't be a big issue.

All of our internal plugins look for kebab-case keys. So, for example,
take the path `backgroundColor.red.500`. This must translate to
`--background-color-red-500`. But if you had something like
`backgroundColor.lightBlue` it would be perfectly fine for that to
translate to `--background-color-lightBlue` internally (and thus the
utility be written as `bg-lightBlue`.

2. Tuple object keys are converted to kebab-case as well.

These keys are converted to "nested" key syntax internally and
typtically represent CSS property names.

For example:
```js
export default {
  theme: {
    fontSize: {
      xs: ["1.5rem", { lineHeight: "1.3" }]
    },
    
    fontFamily: {
      sans: ["Potato Mono", { fontVariationSettings: '"XHGT" 0.7' }]
    }
  }
}
```

The `lineHeight` key here must be converted to `line-height` because it
represents a CSS property name. The theme key that represents this value
is `--text-xs--line-height`. The same situation applies for the
`fontVariationSettings` where the theme key is
`--font-sans--font-variation-settings`.
2025-11-20 22:39:55 +00:00
Jordan Pittman
5a8e878319
Handle future and experimental config keys during upgrade (#19344)
Fixes #19342
2025-11-20 22:11:43 +00:00
Ishita SIngh
5bc90dd2e0
Include filename and line numbers in CSS parse errors (#19282)
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-11-10 13:29:23 -05:00
Jordan Pittman
e9c9c4f79d
Release v4.1.17 (#19272) 2025-11-06 10:20:36 -05:00
Jordan Pittman
dc6a3ce349
Substitute @variant inside utilities (#19263)
Fixes #19258
2025-11-04 11:28:52 -05:00
depfu[bot]
e71e70eda9
Update magic-string 0.30.19 → 0.30.21 (minor) (#19238)
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?




#### ✳️ magic-string (0.30.19 → 0.30.21) ·
[Repo](https://github.com/rich-harris/magic-string) ·
[Changelog](https://github.com/Rich-Harris/magic-string/blob/master/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/Rich-Harris/magic-string/releases/tag/v0.30.21">0.30.21</a></h4>

<blockquote><p dir="auto"><em>No significant changes</em></p>
<h5 dir="auto">    <a
href="https://bounce.depfu.com/github.com/Rich-Harris/magic-string/compare/v0.30.20...v0.30.21">View
changes on GitHub</a>
</h5></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please let
us know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="bdef7d5ab5...410fd4d080">See
the full diff on Github</a>. The new version differs by 7 commits:</p>
<ul>
<li><a
href="410fd4d080"><code>chore:
release v0.30.21</code></a></li>
<li><a
href="5c1800935d"><code>chore:
update repository url</code></a></li>
<li><a
href="97ea74d842"><code>chore:
release v0.30.20</code></a></li>
<li><a
href="3f6b5eb3fb"><code>chore:
update deps</code></a></li>
<li><a
href="60177d51ed"><code>ci:
setup OIDC</code></a></li>
<li><a
href="fbcf5d7e59"><code>chore:
create release.yml</code></a></li>
<li><a
href="cde8c33ef5"><code>chore:
update readme (#305)</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-10-31 07:34:21 -04:00
Robin Malfait
cbbbe84475
Release 4.1.16 (#19185) 2025-10-23 12:32:27 +02:00
Robin Malfait
601d6719f8
Fix incorrect colors used in pseudo-element (#19184)
This PR essentially reverts
https://github.com/tailwindlabs/tailwindcss/pull/19069

We added the nested `&` inside the `@supports` query when we create
fallbacks for color-mix so that devtools (Safari) doesn't freak out.
This works in most cases, however, if you have a parent pseudo element
like `::before`, then the browser will not allow the nested `&`
resulting in invalid CSS.

This PR means that we go back to the broken devtools experience in
Safari, but at least the CSS is valid and works as expected.

Fixes: #19183
2025-10-23 10:07:01 +00:00
Jordan Pittman
a41add9fab
Improve canonicalization for & > :pseudo and & :pseudo arbitrary variants (#19178)
This improves canonicalization of arbitrary variants that use pseudo
classes a bit.

Before this we would see `[&_:first-child]:flex` and leave it be when it
can instead be written as `**:first:flex`. Likewise, for pseudo classes
that don't have a variant, we can still simplify things a bit as
`[&_:--custom]` can be written `**:[:--custom]`.
2025-10-22 08:29:08 -04:00
Jordan Pittman
0113b88fbd
Fix canonicalization of arbitrary variants with attribute selectors (#19176)
Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1481

We were detecting when we needed to apply the `*` and `**` variants and
even detecting attribute selectors. However we only cleaned up the
attribute selectors when they were `data-*` or `aria-*` attributes. This
resulted in a "loop" of sorts because we'd:
- See something like `[&_>_[foo]]:flex`
- Notice that it needs a `*` variant
- Add the `*` variant, giving `*:[&_>[foo]]:flex`
- But fail to cleanup the remainder of the variant

This then meant that we'd see the `*:[&_>[foo]]:flex` the next time we
checked, notice that it still needed a `*` variant, and repeat…

This PR fixes this case to clean up the selector.
2025-10-22 08:19:54 -04:00
Jordan Pittman
29687e0183
Discard candidates with an empty data type (#19172)
Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1479

Maybe should close
https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1480 —
perhaps we can find a workaround there for older versions?

We're building up a class name in code to validate if something is a
valid variant: `{variant}:[color:red]`

if `{variant}` got replaced with `bg-[` then we'd produce
`bg-[:[color:red]` and this parsed as a valid candidate:
```
bg-[:[color:red]
^^                root: `bg`
   ^              data type: `` (empty string) — this should be invalid
     ^^^^^^^^^^   value: `[color:red`
```

The value isn't valid _but_ the syntax for arbitrary values is pretty
lax in core. Oxide already won't pick something like this up though so
no problem there. Only a problem for something like IntelliSense or
clients using the compile() API directly.
2025-10-22 06:14:43 -04:00
Robin Malfait
56e7f3b2c2
Improve memory usage during canonicalization (#19171)
This PR drastically improves the memory usage when performing
canonicalization if you swap out the underlying DesignSystem often. This
will be most noticeable in Intellisense.

The big issue we had is that we used module scoped Map objects where we
cache data based on the DesignSystem. If you then create new design
systems (often), then the cache would just keep growing and growing.

This PR solves that by essentially storing all the caches on the Design
System itself. This way, when you throw away a Design System, all the
caches go with it.

Another approach would've been to use a WeakMap, but then we would have
to make sure that no strong references to the DesignSystem exist
anywhere else, otherwise we would still have the same memory issues.

Note: make sure to go commit by commit and use `?w=1` to ignore
whitespace changes.

## Test plan

1. All existing tests pass

Not super sure how to test this as part of the test suite without making
it slow But I also don't think that's super necessary either. Here is an
experiment I did where I introduce 5 design systems:
<img width="1326" height="274" alt="image"
src="https://github.com/user-attachments/assets/817025e3-0f5b-44be-949b-54ed08f5b3fb"
/>

On the current `main` branch, this looks like:
<img width="619" height="69" alt="image"
src="https://github.com/user-attachments/assets/588ae99b-c978-4c01-bfd1-5cc0725723a8"
/>


In this PR, the memory usage looks like:
<img width="512" height="56" alt="image"
src="https://github.com/user-attachments/assets/0052ad21-7b99-4edf-8a14-8ccef52362db"
/>

The memory usage is stable, but to actually prove that we can still
track multiple design systems, let's track them all in a `Set` so
garbage collection cannot get rid of the unused design system objects:
<img width="847" height="230" alt="image"
src="https://github.com/user-attachments/assets/5f044927-3d53-4c15-8145-78eb2b4d6d54"
/>

Now we're sort of back to the current situation on `main`:
<img width="507" height="53" alt="image"
src="https://github.com/user-attachments/assets/868c0238-8646-41ce-8151-e0ef6dd17d64"
/>
2025-10-21 16:55:30 +02:00
Jordan Pittman
3a4ab8201b
Stop suggesting legacy utilities (#19169)
It was already supposed to work this way — and it appeared via the tests
that it did — but the tests weren't loading the design system normally
so when we split the legacy utilities into a separate file it stopped
testing this properly.

The setup here is a bit iffy but the gist is:
- We allow static utilities to be suggested by default
- A static utility can *opt out* of suggestions by explicitly
registering themselves to return none.
2025-10-21 06:54:10 -04:00
Robin Malfait
7537e34fd1
Ignore --tw- variables during internal signature computation (#19156)
This PR improves some of the signature computation logic. Right now,
when you want to convert `[font-weight:400]` to `font-normal` it's not
going to work because the signatures don't like up:

```css
/* [font-weight:400] */
.x {
  font-weight: 400;
}

/* font-normal */
.x {
  --tw-font-weight: 400;
  font-weight: 400;
}
```

So this PR essentially ignores `--tw-{property}` _if_ the `{property}`
exists with the exact same value.

The main reason we have this, is to make composition of utilities
easier.

As with any of these upgrades, they are the expected behavior in most
cases, but there could always be a case where you don't want this, but
that's why the upgrade tool requires you to review each change before
applying it. Intellisense will recommend the simplified class, but it's
up to you to decide if you want to apply it or not.

There is a known edge case for `leading-{num}`, because the property is
`line-height` and the CSS variable is `--tw-leading`. Right now we map
`--tw-leading` to `line-height` but we can also update the leading
classes to use `--tw-line-height` instead but that feels like a bigger
breaking change _if_ people rely on these internal CSS variables...
2025-10-20 15:21:07 +00:00
Robin Malfait
66c18ca8a4
Collapse multiple utilities (#19147)
Some commits look like a lot of changes, but they just move things
around, so best to use `?w=1` when viewing commit by commit.

This PR adds a new feature to the `designSystem.canonicalizeCandidates`
to collapse multiple utilities to fewer utilities. To make this
possible, we also have to convert some logical properties to physical
properties (controllable via an option).

```ts
ds.canonicalizeCandidates(['w-4', 'h-4'], {
  collapse: true // Default `true`
  logicalToPhysical: true // Default `true`
}) // → ['size-4']
```

We can already compute the signature of each utility, where if two
utilities have the same signature they are considered the same.

However it's kind of impossible to generate all combinations of all
utilities ever to figure out if there is a potential collapse possible.
Even if we just focus on the incoming list of candidates, there could
still be a lot of classes. So instead we have to be a bit more clever.

First, we group candidates together by the used variants and if the
important `!` flag was used. We can improve this in the future, but for
now we won't even try to combine `hover:w-4 h-4`.

Next, for each candidate, we figure out which property and value it
uses. We can build up a lookup table for this. We already did this
process for all utilities in the system as well.

The lookup table for `w-4 h-4 p-4` might look something like this.

```json
{
  "width": {
    "16px": ["w-4", "size-4"],
  },
  "height": {
    "16px": ["h-4", "size-4"],
  },
  "padding": {
    "16px": ["p-4"],
  }
}
```

Next, we can build groups of candidates where an intersection exists in
the lookup table. In the example above, we can see that `w-4` and `h-4`
both map to `size-4` for the value `16px`. So we can group these two
candidates. The `p-4`d doesn't intersect with anything else, so it
remains alone. This also means that we only have to generate
combinations for two candidates (2^2 = 4) instead of three (2^3 = 8). In
practice, your class list might have many classes, so keeping this
number low is important.

When we generate combinations, we will generate the most amount of
candidates first so we have the largest collapse possible. We also stop
when we reach <= 1 combinations because we need at least two candidates
to collapse.

Since this uses the internal design system, if you have custom
`@utility`s, this will work as expected.

```css
@utility example {
  @apply border rounded p-4;
}
```

If you then use:
```html
<div class="border m-0 rounded p-4"></div>
```

Then we can collapse this to:
```html
<div class="example m-0"></div>
```

But even if we used:
```html
<div class="border m-0 rounded pt-4 pb-4 px-4"></div>
```

It would still collapse to:
```html
<div class="example m-0"></div>
```

...because the `pt-4` and `pb-4` can collapse to `py-4`, and `py-4` and
`px-4` can collapse to `p-4`.

This is also where that logical to physical conversion comes into play,
because while `pt-4` and `pb-4` set the physical `padding-top` and
`padding-bottom` properties. The `py-4` utility sets the logical
`padding-block` property. So we internally transform `padding-block` to
the `padding-top` and `padding-bottom` physical properties. The funny
thing is that this logical to physical conversion actually means that we
will convert `pt-4` and `pb-4` from _physical_ to _logical_ properties,
because we converted to `py-4`...

In _most_ cases it's fine, and even preferred to use `py-1` over `pt-1
pb-1`, but in case it's not, then you can disable the
`logicalToPhysical` option.

## Test plan

Added some dedicated tests for this new functionality.
2025-10-20 15:15:31 +00:00
Robin Malfait
b2e2435ccb
Release 4.1.15 (#19159) 2025-10-20 14:48:42 +02:00
Robin Malfait
3b636b74d7
Mark break-words as deprecated, and upgrade to wrap-break-word (#19157)
This PR marks `break-words` as deprecated (such that intellisense
doesn't suggest it anymore). Updates the upgrade tooling to prefer
`wrap-break-word` instead.

Note: `break-words` will still work as expected.

## Test plan

1. `break-words` still generates the correct CSS.
2. Intellisense doesn't suggest `break-words` anymore.
3. Upgrade tooling suggests `wrap-break-word` instead of `break-words`.
2025-10-20 14:14:25 +02:00