Ensure CSS variable shorthand uses valid CSS variables (#15738)

This PR fixes an issue where we didn't always correctly validated
invalid CSS variables when using the CSS variable shorthand syntax.

This PR fixes that by ensuring we start with `--`. We can't validate
that the variable exists at runtime, but we can validate if it looks
like a valid CSS variable.

We can go a step further by validating if the CSS variable is valid
syntax (e.g.: all characters are valid), but we can introduce this if
this is causing actual issues in projects.

```
p-(a-b)
```

Used to compile to:
```css
.p-\(a-b\) {
  padding: var(a-b);
}
```

But not anymore.
This commit is contained in:
Robin Malfait 2025-01-27 12:55:10 +01:00 committed by GitHub
parent 79541c94d9
commit 193fc60989
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 30 additions and 0 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Remove invalid `min-w/h-none` utilities ([#15845](https://github.com/tailwindlabs/tailwindcss/pull/15845))
- Ensure CSS variable shorthand uses valid CSS variables ([#15738](https://github.com/tailwindlabs/tailwindcss/pull/15738))
## [4.0.0] - 2025-01-21

View File

@ -570,6 +570,13 @@ it('should parse a utility with an arbitrary value with parens', () => {
`)
})
it('should not parse a utility with an arbitrary value with parens that does not start with --', () => {
let utilities = new Utilities()
utilities.functional('bg', () => [])
expect(run('bg-(my-color)', { utilities })).toMatchInlineSnapshot(`[]`)
})
it('should parse a utility with an arbitrary value including a typehint', () => {
let utilities = new Utilities()
utilities.functional('bg', () => [])
@ -616,6 +623,13 @@ it('should parse a utility with an arbitrary value with parens including a typeh
`)
})
it('should not parse a utility with an arbitrary value with parens including a typehint that does not start with --', () => {
let utilities = new Utilities()
utilities.functional('bg', () => [])
expect(run('bg-(color:my-color)', { utilities })).toMatchInlineSnapshot(`[]`)
})
it('should parse a utility with an arbitrary value with parens and a fallback', () => {
let utilities = new Utilities()
utilities.functional('bg', () => [])
@ -888,6 +902,8 @@ it('should not parse invalid arbitrary values in variants', () => {
'data-foo-(--value)/(number:--mod):flex',
'data-foo(--value)/(number:--mod):flex',
'data-(value):flex',
]) {
expect(run(candidate, { utilities, variants })).toEqual([])
}
@ -945,6 +961,13 @@ it('should parse a utility with an implicit variable as the modifier using the s
`)
})
it('should not parse a utility with an implicit invalid variable as the modifier using the shorthand', () => {
let utilities = new Utilities()
utilities.functional('bg', () => [])
expect(run('bg-red-500/(value)', { utilities })).toMatchInlineSnapshot(`[]`)
})
it('should parse a utility with an implicit variable as the modifier that is important', () => {
let utilities = new Utilities()
utilities.functional('bg', () => [])

View File

@ -517,6 +517,9 @@ function parseModifier(modifier: string): CandidateModifier | null {
// ^^
if (arbitraryValue.length === 0 || arbitraryValue.trim().length === 0) return null
// Arbitrary values must start with `--` since it represents a CSS variable.
if (arbitraryValue[0] !== '-' && arbitraryValue[1] !== '-') return null
return {
kind: 'arbitrary',
value: `var(${arbitraryValue})`,
@ -651,6 +654,9 @@ export function parseVariant(variant: string, designSystem: DesignSystem): Varia
// ^^
if (arbitraryValue.length === 0 || arbitraryValue.trim().length === 0) return null
// Arbitrary values must start with `--` since it represents a CSS variable.
if (arbitraryValue[0] !== '-' && arbitraryValue[1] !== '-') return null
return {
kind: 'functional',
root,