parseVariant() — tailwindcss Function Reference
Architecture documentation for the parseVariant() function in candidate.ts from the tailwindcss codebase.
Entity Profile
Dependency Diagram
graph TD ceb1ff41_a641_27dd_6289_7f779710b411["parseVariant()"] 707ef5d3_1654_7094_06d2_99e8004cab43["migrateArbitraryVariants()"] 707ef5d3_1654_7094_06d2_99e8004cab43 -->|calls| ceb1ff41_a641_27dd_6289_7f779710b411 042344d6_c400_b6f0_00d2_ff8fded428bc["migrateModernizeArbitraryValues()"] 042344d6_c400_b6f0_00d2_ff8fded428bc -->|calls| ceb1ff41_a641_27dd_6289_7f779710b411 0bec5ca9_74c8_dcc7_ec12_6404fb6493bd["parseCandidate()"] 0bec5ca9_74c8_dcc7_ec12_6404fb6493bd -->|calls| ceb1ff41_a641_27dd_6289_7f779710b411 6a036819_1cd4_f532_6d62_77770a6b1666["arbitraryVariants()"] 6a036819_1cd4_f532_6d62_77770a6b1666 -->|calls| ceb1ff41_a641_27dd_6289_7f779710b411 c17298f2_5925_766a_629e_a0acb211713d["modernizeArbitraryValuesVariant()"] c17298f2_5925_766a_629e_a0acb211713d -->|calls| ceb1ff41_a641_27dd_6289_7f779710b411 c58d3214_88d6_f4fc_257f_8e84def5b24f["buildDesignSystem()"] c58d3214_88d6_f4fc_257f_8e84def5b24f -->|calls| ceb1ff41_a641_27dd_6289_7f779710b411 101ae1c2_eee3_3589_2aa3_922a6a429d4f["substituteAtVariant()"] 101ae1c2_eee3_3589_2aa3_922a6a429d4f -->|calls| ceb1ff41_a641_27dd_6289_7f779710b411 fcaea65f_8651_c21e_ec5d_f042e1ab495e["decodeArbitraryValue()"] ceb1ff41_a641_27dd_6289_7f779710b411 -->|calls| fcaea65f_8651_c21e_ec5d_f042e1ab495e d94e70b3_9bf8_d390_b32f_1a5bd7d409d9["isValidArbitrary()"] ceb1ff41_a641_27dd_6289_7f779710b411 -->|calls| d94e70b3_9bf8_d390_b32f_1a5bd7d409d9 03b8d706_a876_a776_0056_186ced5d6067["segment()"] ceb1ff41_a641_27dd_6289_7f779710b411 -->|calls| 03b8d706_a876_a776_0056_186ced5d6067 096ef313_f05a_4a68_9927_ab12300906fd["findRoots()"] ceb1ff41_a641_27dd_6289_7f779710b411 -->|calls| 096ef313_f05a_4a68_9927_ab12300906fd 62a1e81d_e57d_83b1_bc05_4b2210993980["parseModifier()"] ceb1ff41_a641_27dd_6289_7f779710b411 -->|calls| 62a1e81d_e57d_83b1_bc05_4b2210993980 style ceb1ff41_a641_27dd_6289_7f779710b411 fill:#6366f1,stroke:#818cf8,color:#fff
Relationship Graph
Source Code
packages/tailwindcss/src/candidate.ts lines 661–849
export function parseVariant(variant: string, designSystem: DesignSystem): Variant | null {
// Arbitrary variants
if (variant[0] === '[' && variant[variant.length - 1] === ']') {
/**
* TODO: Breaking change
*
* @deprecated Arbitrary variants containing at-rules with other selectors
* are deprecated. Use stacked variants instead.
*
* Before:
* - `[@media(width>=123px){&:hover}]:`
*
* After:
* - `[@media(width>=123px)]:[&:hover]:`
* - `[@media(width>=123px)]:hover:`
*/
if (variant[1] === '@' && variant.includes('&')) return null
let selector = decodeArbitraryValue(variant.slice(1, -1))
// Values can't contain `;` or `}` characters at the top-level.
if (!isValidArbitrary(selector)) return null
// Empty arbitrary values are invalid. E.g.: `[]:`
// ^^
if (selector.length === 0 || selector.trim().length === 0) return null
let relative = selector[0] === '>' || selector[0] === '+' || selector[0] === '~'
// Ensure `&` is always present by wrapping the selector in `&:is(…)`,
// unless it's a relative selector like `> img`.
//
// E.g.:
//
// - `[p]:flex`
if (!relative && selector[0] !== '@' && !selector.includes('&')) {
selector = `&:is(${selector})`
}
return {
kind: 'arbitrary',
selector,
relative,
}
}
// Static, functional and compound variants
{
// group-hover/group-name
// ^^^^^^^^^^^ -> Variant without modifier
// ^^^^^^^^^^ -> Modifier
let [variantWithoutModifier, modifier = null, additionalModifier] = segment(variant, '/')
// If there's more than one modifier, the variant is invalid.
//
// E.g.:
//
// - `group-hover/foo/bar`
if (additionalModifier) return null
let roots = findRoots(variantWithoutModifier, (root) => {
return designSystem.variants.has(root)
})
for (let [root, value] of roots) {
switch (designSystem.variants.kind(root)) {
case 'static': {
// Static variants do not have a value
if (value !== null) return null
// Static variants do not have a modifier
if (modifier !== null) return null
return {
kind: 'static',
root,
}
}
case 'functional': {
let parsedModifier = modifier === null ? null : parseModifier(modifier)
// Empty arbitrary values are invalid. E.g.: `@max-md/[]:` or `@max-md/():`
// ^^ ^^
if (modifier !== null && parsedModifier === null) return null
if (value === null) {
return {
kind: 'functional',
root,
modifier: parsedModifier,
value: null,
}
}
if (value[value.length - 1] === ']') {
// Discard values like `foo-[#bar]`
if (value[0] !== '[') continue
let arbitraryValue = decodeArbitraryValue(value.slice(1, -1))
// Values can't contain `;` or `}` characters at the top-level.
if (!isValidArbitrary(arbitraryValue)) return null
// Empty arbitrary values are invalid. E.g.: `data-[]:`
// ^^
if (arbitraryValue.length === 0 || arbitraryValue.trim().length === 0) return null
return {
kind: 'functional',
root,
modifier: parsedModifier,
value: {
kind: 'arbitrary',
value: arbitraryValue,
},
}
}
if (value[value.length - 1] === ')') {
// Discard values like `foo-(--bar)`
if (value[0] !== '(') continue
let arbitraryValue = decodeArbitraryValue(value.slice(1, -1))
// Values can't contain `;` or `}` characters at the top-level.
if (!isValidArbitrary(arbitraryValue)) return null
// Empty arbitrary values are invalid. E.g.: `data-():`
// ^^
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,
modifier: parsedModifier,
value: {
kind: 'arbitrary',
value: `var(${arbitraryValue})`,
},
}
}
if (!IS_VALID_NAMED_VALUE.test(value)) continue
return {
kind: 'functional',
root,
modifier: parsedModifier,
value: { kind: 'named', value },
}
}
case 'compound': {
if (value === null) return null
// Forward the modifier of the compound variants to its subVariant.
// This allows for `not-group-hover/name:flex` to work.
if (modifier && (root === 'not' || root === 'has' || root === 'in')) {
value = `${value}/${modifier}`
modifier = null
}
let subVariant = designSystem.parseVariant(value)
if (subVariant === null) return null
// These two variants must be compatible when compounded
if (!designSystem.variants.compoundsWith(root, subVariant)) return null
let parsedModifier = modifier === null ? null : parseModifier(modifier)
// Empty arbitrary values are invalid. E.g.: `group-focus/[]:` or `group-focus/():`
// ^^ ^^
if (modifier !== null && parsedModifier === null) return null
return {
kind: 'compound',
root,
modifier: parsedModifier,
variant: subVariant,
}
}
}
}
}
return null
}
Domain
Subdomains
Called By
Source
Frequently Asked Questions
What does parseVariant() do?
parseVariant() is a function in the tailwindcss codebase.
What does parseVariant() call?
parseVariant() calls 5 function(s): decodeArbitraryValue, findRoots, isValidArbitrary, parseModifier, segment.
What calls parseVariant()?
parseVariant() is called by 7 function(s): arbitraryVariants, buildDesignSystem, migrateArbitraryVariants, migrateModernizeArbitraryValues, modernizeArbitraryValuesVariant, parseCandidate, substituteAtVariant.
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free