diff --git a/packages/tailwindcss/src/variants.test.ts b/packages/tailwindcss/src/variants.test.ts index e9398044007c..880ae17b78ac 100644 --- a/packages/tailwindcss/src/variants.test.ts +++ b/packages/tailwindcss/src/variants.test.ts @@ -1594,6 +1594,67 @@ test('forced-colors', () => { `) }) +test('nth', () => { + expect( + run([ + 'nth-3:flex', + 'nth-[2n+1]:flex', + 'nth-[2n+1_of_.foo]:flex', + 'nth-last-3:flex', + 'nth-last-[2n+1]:flex', + 'nth-last-[2n+1_of_.foo]:flex', + 'nth-of-type-3:flex', + 'nth-of-type-[2n+1]:flex', + 'nth-last-of-type-3:flex', + 'nth-last-of-type-[2n+1]:flex', + ]), + ).toMatchInlineSnapshot(` + ".nth-3\\:flex:nth-child(3) { + display: flex; + } + + .nth-\\[2n\\+1\\]\\:flex:nth-child(odd) { + display: flex; + } + + .nth-\\[2n\\+1_of_\\.foo\\]\\:flex:nth-child(odd of .foo) { + display: flex; + } + + .nth-last-3\\:flex:nth-last-child(3) { + display: flex; + } + + .nth-last-\\[2n\\+1\\]\\:flex:nth-last-child(odd) { + display: flex; + } + + .nth-last-\\[2n\\+1_of_\\.foo\\]\\:flex:nth-last-child(odd of .foo) { + display: flex; + } + + .nth-of-type-3\\:flex:nth-of-type(3) { + display: flex; + } + + .nth-of-type-\\[2n\\+1\\]\\:flex:nth-of-type(odd) { + display: flex; + } + + .nth-last-of-type-3\\:flex:nth-last-of-type(3) { + display: flex; + } + + .nth-last-of-type-\\[2n\\+1\\]\\:flex:nth-last-of-type(odd) { + display: flex; + }" + `) + + expect( + run(['nth-foo:flex', 'nth-of-type-foo:flex', 'nth-last-foo:flex', 'nth-last-of-type-foo:flex']), + ).toMatchInlineSnapshot(`""`) +}) + test('container queries', () => { expect( compileCss( diff --git a/packages/tailwindcss/src/variants.ts b/packages/tailwindcss/src/variants.ts index 4738d6d64b4b..8c76c237c366 100644 --- a/packages/tailwindcss/src/variants.ts +++ b/packages/tailwindcss/src/variants.ts @@ -373,6 +373,42 @@ export function createVariants(theme: Theme): Variants { ruleNode.nodes = [rule(`&[data-${variant.value.value}]`, ruleNode.nodes)] }) + variants.functional('nth', (ruleNode, variant) => { + if (variant.value === null) return null + + // Only numeric bare values are allowed + if (variant.value.kind === 'named' && Number.isNaN(Number(variant.value.value))) return null + + ruleNode.nodes = [rule(`&:nth-child(${variant.value.value})`, ruleNode.nodes)] + }) + + variants.functional('nth-last', (ruleNode, variant) => { + if (variant.value === null) return null + + // Only numeric bare values are allowed + if (variant.value.kind === 'named' && Number.isNaN(Number(variant.value.value))) return null + + ruleNode.nodes = [rule(`&:nth-last-child(${variant.value.value})`, ruleNode.nodes)] + }) + + variants.functional('nth-of-type', (ruleNode, variant) => { + if (variant.value === null) return null + + // Only numeric bare values are allowed + if (variant.value.kind === 'named' && Number.isNaN(Number(variant.value.value))) return null + + ruleNode.nodes = [rule(`&:nth-of-type(${variant.value.value})`, ruleNode.nodes)] + }) + + variants.functional('nth-last-of-type', (ruleNode, variant) => { + if (variant.value === null) return null + + // Only numeric bare values are allowed + if (variant.value.kind === 'named' && Number.isNaN(Number(variant.value.value))) return null + + ruleNode.nodes = [rule(`&:nth-last-of-type(${variant.value.value})`, ruleNode.nodes)] + }) + variants.functional( 'supports', (ruleNode, variant) => {