Ensure --value(…) is required in functional @utility definitions (#20005)
While working on #19989, I noticed that `--value(…)` inside functional `@utility` definitions is not required right now. That means that the following CSS is valid: ```css @utility foo-* { color: red; } ``` But this doesn't really makes sense, because this now accepts a value and `foo-a`, `foo-b` and `foo-c` would generate the following CSS: ```css .foo-a { color: red; } .foo-b { color: red; } .foo-c { color: red; } ``` The `a`, `b`, and `c` are not doing anything here apart from making your CSS bigger. So this is very likely an actual bug that you forgot to use `--value(…)`. Additionally, if a `--value(…)` was used, but it didn't resolve anything, then we already properly discared the candidate. ## Test plan 1. Add test to ensure `--value(…)` is required in functional `@utility` definitions 2. Existing tests pass
This commit is contained in:
parent
6e2b60e84a
commit
0f6f7d480f
3 changed files with 44 additions and 8 deletions
|
|
@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Allow multiple `@utility` definitions with the same name but different value types ([#19777](https://github.com/tailwindlabs/tailwindcss/pull/19777))
|
||||
- Export missing `PluginWithConfig` type from `tailwindcss/plugin` to fix errors when inferring plugin config types ([#19707](https://github.com/tailwindlabs/tailwindcss/pull/19707))
|
||||
- Ensure `start` and `end` legacy utilities without values do not generate CSS ([#20003](https://github.com/tailwindlabs/tailwindcss/pull/20003))
|
||||
- Ensure `--value(…)` is required in functional `@utility` definitions ([#20005](https://github.com/tailwindlabs/tailwindcss/pull/20005))
|
||||
|
||||
## [4.2.4] - 2026-04-21
|
||||
|
||||
|
|
|
|||
|
|
@ -28870,6 +28870,40 @@ describe('custom utilities', () => {
|
|||
})
|
||||
|
||||
describe('functional utilities', () => {
|
||||
test('functional utilities require a `--value(…)`', async () => {
|
||||
let input = css`
|
||||
@utility tab-* {
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
expect(await compileCss(input, ['tab', 'tab-foo'])).toEqual('')
|
||||
})
|
||||
|
||||
test('functional utilities must resolve at least one `--value(…)`', async () => {
|
||||
let input = css`
|
||||
@utility tab-* {
|
||||
tab-size: --value(integer);
|
||||
}
|
||||
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
expect(await compileCss(input, ['tab-1', 'tab-2'])).toMatchInlineSnapshot(`
|
||||
".tab-1 {
|
||||
tab-size: 1;
|
||||
}
|
||||
|
||||
.tab-2 {
|
||||
tab-size: 2;
|
||||
}"
|
||||
`)
|
||||
|
||||
expect(await compileCss(input, ['tab', 'tab-foo', 'tab-2.5'])).toEqual('')
|
||||
})
|
||||
|
||||
test('resolving values from `@theme`', async () => {
|
||||
let input = css`
|
||||
@theme reference {
|
||||
|
|
|
|||
|
|
@ -6178,14 +6178,14 @@ export function createCssUtility(node: AtRule) {
|
|||
let shouldRemoveDeclaration = false
|
||||
|
||||
let valueAst = ValueParser.parse(node.value)
|
||||
walk(valueAst, (valueNode) => {
|
||||
if (valueNode.kind !== 'function') return
|
||||
walk(valueAst, (fnNode) => {
|
||||
if (fnNode.kind !== 'function') return
|
||||
|
||||
// Value function, e.g.: `--value(integer)`
|
||||
if (valueNode.value === '--value') {
|
||||
if (fnNode.value === '--value') {
|
||||
usedValueFn = true
|
||||
|
||||
let resolved = resolveValueFunction(value, valueNode, designSystem)
|
||||
let resolved = resolveValueFunction(value, fnNode, designSystem)
|
||||
if (resolved) {
|
||||
resolvedValueFn = true
|
||||
if (resolved.ratio) {
|
||||
|
|
@ -6203,7 +6203,7 @@ export function createCssUtility(node: AtRule) {
|
|||
}
|
||||
|
||||
// Modifier function, e.g.: `--modifier(integer)`
|
||||
else if (valueNode.value === '--modifier') {
|
||||
else if (fnNode.value === '--modifier') {
|
||||
// If there is no modifier present in the candidate, then the
|
||||
// declaration can be removed.
|
||||
if (modifier === null) {
|
||||
|
|
@ -6213,7 +6213,7 @@ export function createCssUtility(node: AtRule) {
|
|||
|
||||
usedModifierFn = true
|
||||
|
||||
let replacement = resolveValueFunction(modifier, valueNode, designSystem)
|
||||
let replacement = resolveValueFunction(modifier, fnNode, designSystem)
|
||||
if (replacement) {
|
||||
resolvedModifierFn = true
|
||||
return WalkAction.ReplaceSkip(replacement.nodes)
|
||||
|
|
@ -6233,8 +6233,9 @@ export function createCssUtility(node: AtRule) {
|
|||
node.value = ValueParser.toCss(valueAst)
|
||||
})
|
||||
|
||||
// Used `--value(…)` but nothing resolved
|
||||
if (usedValueFn && !resolvedValueFn) return null
|
||||
// Functional CSS utilities require `--value(…)`, and one of those
|
||||
// branches must resolve for the candidate to be valid.
|
||||
if (!usedValueFn || !resolvedValueFn) return null
|
||||
|
||||
// Used `--modifier(…)` but nothing resolved
|
||||
if (usedModifierFn && !resolvedModifierFn) return null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue