tailwindcss/packages/tailwindcss
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
..
src Improve @utility name validation (#19524) 2026-01-06 12:54:43 +01:00
tests Add support for source maps (#17775) 2025-05-08 16:29:49 -04:00
index.css introduce v4 codebase 2024-03-05 14:29:15 +01:00
package.json Update dedent 1.7.0 → 1.7.1 (patch) (#19484) 2025-12-24 10:31:02 -05:00
playwright.config.ts Only apply hover on devices that support hover (#14500) 2024-09-24 11:43:57 -04:00
preflight.css Center the dropdown icon added to an input with a paired datalist in Chrome (#18511) 2025-07-15 14:41:04 -04:00
README.md update READMEs 2025-07-02 22:49:47 +02:00
theme.css Improve compatibility with Safari 15 (#17435) 2025-04-01 13:33:22 +02:00
tsconfig.json introduce v4 codebase 2024-03-05 14:29:15 +01:00
tsup.config.ts Revert: Only expose used CSS variables (#16403) 2025-02-10 13:26:13 +01:00
utilities.css introduce v4 codebase 2024-03-05 14:29:15 +01:00
vitest.config.ts Reduce precision of oklab() arguments in test snapshots (#15210) 2024-11-27 11:06:19 -05:00

Tailwind CSS

A utility-first CSS framework for rapidly building custom user interfaces.

Build Status Total Downloads Latest Release License


Documentation

For full documentation, visit tailwindcss.com.

Community

For help, discussion about best practices, or feature ideas:

Discuss Tailwind CSS on GitHub

Contributing

If you're interested in contributing to Tailwind CSS, please read our contributing docs before submitting a pull request.