Fix container names with hyphens (#17628)
Fixes #17614. Candidate parsing for variants only account for the root `@` if there no hyphens. It seems like the current logic assumes if it *does* have a hyphen, then it would be one of `@min` or `@max`. However, with: ```css @theme { --container-foo-bar: 1440px; } ``` Then `@foo-bar` should be valid. However, we only check for `@foo-bar` and `@foo` as roots, but never `@`. This PR adds a check for `@` at the very end after iterating through root permutations. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com>
This commit is contained in:
parent
cdecb55702
commit
3ab7f12563
4 changed files with 83 additions and 11 deletions
|
|
@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Ensure `color-mix(…)` polyfills do not cause used CSS variables to be removed ([#17555](https://github.com/tailwindlabs/tailwindcss/pull/17555))
|
||||
- Ensure the `color-mix(…)` polyfill creates fallbacks for theme variables that reference other theme variables ([#17562](https://github.com/tailwindlabs/tailwindcss/pull/17562))
|
||||
- Fix brace expansion in `@source inline('z-{10..0}')` with range going down ([#17591](https://github.com/tailwindlabs/tailwindcss/pull/17591))
|
||||
- Ensure container query variant names can contain hyphens ([#17628](https://github.com/tailwindlabs/tailwindcss/pull/17628))
|
||||
|
||||
## [4.1.3] - 2025-04-04
|
||||
|
||||
|
|
|
|||
|
|
@ -1294,6 +1294,36 @@ it('should parse a functional variant starting with @', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
it('should parse a functional variant starting with @ that has a hyphen', () => {
|
||||
let utilities = new Utilities()
|
||||
utilities.static('flex', () => [])
|
||||
|
||||
let variants = new Variants()
|
||||
variants.functional('@', () => {})
|
||||
|
||||
expect(run('@foo-bar:flex', { utilities, variants })).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"important": false,
|
||||
"kind": "static",
|
||||
"raw": "@foo-bar:flex",
|
||||
"root": "flex",
|
||||
"variants": [
|
||||
{
|
||||
"kind": "functional",
|
||||
"modifier": null,
|
||||
"root": "@",
|
||||
"value": {
|
||||
"kind": "named",
|
||||
"value": "foo-bar",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
`)
|
||||
})
|
||||
|
||||
it('should parse a functional variant starting with @ and a modifier', () => {
|
||||
let utilities = new Utilities()
|
||||
utilities.static('flex', () => [])
|
||||
|
|
|
|||
|
|
@ -745,14 +745,6 @@ function* findRoots(input: string, exists: (input: string) => boolean): Iterable
|
|||
// Otherwise test every permutation of the input by iteratively removing
|
||||
// everything after the last dash.
|
||||
let idx = input.lastIndexOf('-')
|
||||
if (idx === -1) {
|
||||
// Variants starting with `@` are special because they don't need a `-`
|
||||
// after the `@` (E.g.: `@-lg` should be written as `@lg`).
|
||||
if (input[0] === '@' && exists('@')) {
|
||||
yield ['@', input.slice(1)]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Determine the root and value by testing permutations of the incoming input.
|
||||
//
|
||||
|
|
@ -761,7 +753,7 @@ function* findRoots(input: string, exists: (input: string) => boolean): Iterable
|
|||
// `bg-red-500` -> No match
|
||||
// `bg-red` -> No match
|
||||
// `bg` -> Match
|
||||
do {
|
||||
while (idx > 0) {
|
||||
let maybeRoot = input.slice(0, idx)
|
||||
|
||||
if (exists(maybeRoot)) {
|
||||
|
|
@ -776,5 +768,11 @@ function* findRoots(input: string, exists: (input: string) => boolean): Iterable
|
|||
}
|
||||
|
||||
idx = input.lastIndexOf('-', idx - 1)
|
||||
} while (idx > 0)
|
||||
}
|
||||
|
||||
// Try '@' variant after permutations. This allows things like `@max` of `@max-foo-bar`
|
||||
// to match before looking for `@`.
|
||||
if (input[0] === '@' && exists('@')) {
|
||||
yield ['@', input.slice(1)]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2074,6 +2074,7 @@ test('container queries', async () => {
|
|||
css`
|
||||
@theme {
|
||||
--container-lg: 1024px;
|
||||
--container-foo-bar: 1440px;
|
||||
}
|
||||
@tailwind utilities;
|
||||
`,
|
||||
|
|
@ -2082,20 +2083,38 @@ test('container queries', async () => {
|
|||
'@lg/name:flex',
|
||||
'@[123px]:flex',
|
||||
'@[456px]/name:flex',
|
||||
'@foo-bar:flex',
|
||||
'@foo-bar/name:flex',
|
||||
|
||||
'@min-lg:flex',
|
||||
'@min-lg/name:flex',
|
||||
'@min-[123px]:flex',
|
||||
'@min-[456px]/name:flex',
|
||||
'@min-foo-bar:flex',
|
||||
'@min-foo-bar/name:flex',
|
||||
|
||||
'@max-lg:flex',
|
||||
'@max-lg/name:flex',
|
||||
'@max-[123px]:flex',
|
||||
'@max-[456px]/name:flex',
|
||||
'@max-foo-bar:flex',
|
||||
'@max-foo-bar/name:flex',
|
||||
],
|
||||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
"@container name not (min-width: 1024px) {
|
||||
"@container name not (min-width: 1440px) {
|
||||
.\\@max-foo-bar\\/name\\:flex {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
@container not (min-width: 1440px) {
|
||||
.\\@max-foo-bar\\:flex {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
@container name not (min-width: 1024px) {
|
||||
.\\@max-lg\\/name\\:flex {
|
||||
display: flex;
|
||||
}
|
||||
|
|
@ -2153,6 +2172,30 @@ test('container queries', async () => {
|
|||
.\\@min-lg\\:flex {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
@container name (min-width: 1440px) {
|
||||
.\\@foo-bar\\/name\\:flex {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
@container (min-width: 1440px) {
|
||||
.\\@foo-bar\\:flex {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
@container name (min-width: 1440px) {
|
||||
.\\@min-foo-bar\\/name\\:flex {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
@container (min-width: 1440px) {
|
||||
.\\@min-foo-bar\\:flex {
|
||||
display: flex;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue