createVariants() — tailwindcss Function Reference
Architecture documentation for the createVariants() function in variants.ts from the tailwindcss codebase.
Entity Profile
Dependency Diagram
graph TD 7c96535c_85cb_d6bf_efe4_875fba595c4f["createVariants()"] cebe77e1_f0f2_aeee_417e_2192f5790344["buildDesignSystem()"] cebe77e1_f0f2_aeee_417e_2192f5790344 -->|calls| 7c96535c_85cb_d6bf_efe4_875fba595c4f 828655d7_fafb_f7a4_ba36_e6b356a78fbc["compoundsForSelectors()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| 828655d7_fafb_f7a4_ba36_e6b356a78fbc 08f33202_11d1_569a_e8df_de23eb987e2f["rule()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| 08f33202_11d1_569a_e8df_de23eb987e2f 2a20ea29_c850_cd61_5600_aeebbe3dda66["segment()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| 2a20ea29_c850_cd61_5600_aeebbe3dda66 a9af385a_fd12_f1d8_7cf0_ccb9b281ca18["atRule()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| a9af385a_fd12_f1d8_7cf0_ccb9b281ca18 8711d3c6_2537_4267_acab_f8fa7a5ab188["compound()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| 8711d3c6_2537_4267_acab_f8fa7a5ab188 e9d556bc_f22d_356c_1bd2_27442c34b5c7["walk()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| e9d556bc_f22d_356c_1bd2_27442c34b5c7 c3b56f1d_0d90_0f17_2f55_85f3419d74bd["styleRule()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| c3b56f1d_0d90_0f17_2f55_85f3419d74bd e69fc175_885d_a758_85b9_9613d68e20bf["suggest()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| e69fc175_885d_a758_85b9_9613d68e20bf 0cd2c95f_7ec0_fa14_e5f7_51185e900451["keys()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| 0cd2c95f_7ec0_fa14_e5f7_51185e900451 322c45ea_3835_3229_6b47_7ac91365ee73["compoundsWith()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| 322c45ea_3835_3229_6b47_7ac91365ee73 b9f8f3ed_4296_2840_57dd_a1c25e0e5f52["atRoot()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| b9f8f3ed_4296_2840_57dd_a1c25e0e5f52 85b46de2_edfa_9371_e2c6_e60f3f5346a2["decl()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| 85b46de2_edfa_9371_e2c6_e60f3f5346a2 cd658659_fd0a_fcda_c564_b848d3481771["has()"] 7c96535c_85cb_d6bf_efe4_875fba595c4f -->|calls| cd658659_fd0a_fcda_c564_b848d3481771 style 7c96535c_85cb_d6bf_efe4_875fba595c4f fill:#6366f1,stroke:#818cf8,color:#fff
Relationship Graph
Source Code
packages/tailwindcss/src/variants.ts lines 348–1163
export function createVariants(theme: Theme): Variants {
// In the future we may want to support returning a rule here if some complex
// variant requires it. For now pure mutation is sufficient and will be the
// most performant.
let variants = new Variants()
/**
* Register a static variant like `hover`.
*/
function staticVariant(
name: string,
selectors: string[],
{ compounds }: { compounds?: Compounds } = {},
) {
compounds = compounds ?? compoundsForSelectors(selectors)
variants.static(
name,
(r) => {
r.nodes = selectors.map((selector) => rule(selector, r.nodes))
},
{ compounds },
)
}
staticVariant('*', [':is(& > *)'], { compounds: Compounds.Never })
staticVariant('**', [':is(& *)'], { compounds: Compounds.Never })
function negateConditions(ruleName: string, conditions: string[]) {
return conditions.map((condition) => {
condition = condition.trim()
let parts = segment(condition, ' ')
// @media not {query}
// @supports not {query}
// @container not {query}
if (parts[0] === 'not') {
return parts.slice(1).join(' ')
}
if (ruleName === '@container') {
// @container {query}
if (parts[0][0] === '(') {
return `not ${condition}`
}
// @container {name} not {query}
else if (parts[1] === 'not') {
return `${parts[0]} ${parts.slice(2).join(' ')}`
}
// @container {name} {query}
else {
return `${parts[0]} not ${parts.slice(1).join(' ')}`
}
}
return `not ${condition}`
})
}
let conditionalRules = ['@media', '@supports', '@container']
function negateAtRule(rule: AtRule) {
for (let ruleName of conditionalRules) {
if (ruleName !== rule.name) continue
let conditions = segment(rule.params, ',')
// We don't support things like `@media screen, print` because
// the negation would be `@media not screen and print` and we don't
// want to deal with that complexity.
if (conditions.length > 1) return null
conditions = negateConditions(rule.name, conditions)
return atRule(rule.name, conditions.join(', '))
}
return null
}
function negateSelector(selector: string) {
if (selector.includes('::')) return null
let selectors = segment(selector, ',').map((sel) => {
// Replace `&` in target variant with `*`, so variants like `&:hover`
// become `&:not(*:hover)`. The `*` will often be optimized away.
sel = sel.replaceAll('&', '*')
return sel
})
return `&:not(${selectors.join(', ')})`
}
variants.compound('not', Compounds.StyleRules | Compounds.AtRules, (ruleNode, variant) => {
if (variant.variant.kind === 'arbitrary' && variant.variant.relative) return null
if (variant.modifier) return null
let didApply = false
walk([ruleNode], (node, ctx) => {
if (node.kind !== 'rule' && node.kind !== 'at-rule') return WalkAction.Continue
if (node.nodes.length > 0) return WalkAction.Continue
// Throw out any candidates with variants using nested style rules
let atRules: AtRule[] = []
let styleRules: StyleRule[] = []
let path = ctx.path()
path.push(node)
for (let node of path) {
if (node.kind === 'at-rule') {
atRules.push(node)
} else if (node.kind === 'rule') {
styleRules.push(node)
}
}
if (atRules.length > 1) return WalkAction.Stop
if (styleRules.length > 1) return WalkAction.Stop
let rules: Rule[] = []
for (let node of styleRules) {
let selector = negateSelector(node.selector)
if (!selector) {
didApply = false
return WalkAction.Stop
}
rules.push(styleRule(selector, []))
}
for (let node of atRules) {
let negatedAtRule = negateAtRule(node)
if (!negatedAtRule) {
didApply = false
return WalkAction.Stop
}
rules.push(negatedAtRule)
}
Object.assign(ruleNode, styleRule('&', rules))
// Track that the variant was actually applied
didApply = true
return WalkAction.Skip
})
// TODO: Tweak group, peer, has to ignore intermediate `&` selectors (maybe?)
if (ruleNode.kind === 'rule' && ruleNode.selector === '&' && ruleNode.nodes.length === 1) {
Object.assign(ruleNode, ruleNode.nodes[0])
}
// If the node wasn't modified, this variant is not compatible with
// `not-*` so discard the candidate.
if (!didApply) return null
})
variants.suggest('not', () => {
return Array.from(variants.keys()).filter((name) => {
return variants.compoundsWith('not', name)
})
})
variants.compound('group', Compounds.StyleRules, (ruleNode, variant) => {
if (variant.variant.kind === 'arbitrary' && variant.variant.relative) return null
// Name the group by appending the modifier to `group` class itself if
// present.
let variantSelector = variant.modifier
? `:where(.${theme.prefix ? `${theme.prefix}\\:` : ''}group\\/${variant.modifier.value})`
: `:where(.${theme.prefix ? `${theme.prefix}\\:` : ''}group)`
let didApply = false
walk([ruleNode], (node, ctx) => {
if (node.kind !== 'rule') return WalkAction.Continue
// Throw out any candidates with variants using nested style rules
for (let parent of ctx.path()) {
if (parent.kind !== 'rule') continue
didApply = false
return WalkAction.Stop
}
// For most variants we rely entirely on CSS nesting to build-up the final
// selector, but there is no way to use CSS nesting to make `&` refer to
// just the `.group` class the way we'd need to for these variants, so we
// need to replace it in the selector ourselves.
let selector = node.selector.replaceAll('&', variantSelector)
// When the selector is a selector _list_ we need to wrap it in `:is`
// to make sure the matching behavior is consistent with the original
// variant / selector.
if (segment(selector, ',').length > 1) {
selector = `:is(${selector})`
}
node.selector = `&:is(${selector} *)`
// Track that the variant was actually applied
didApply = true
})
// If the node wasn't modified, this variant is not compatible with
// `group-*` so discard the candidate.
if (!didApply) return null
})
variants.suggest('group', () => {
return Array.from(variants.keys()).filter((name) => {
return variants.compoundsWith('group', name)
})
})
variants.compound('peer', Compounds.StyleRules, (ruleNode, variant) => {
if (variant.variant.kind === 'arbitrary' && variant.variant.relative) return null
// Name the peer by appending the modifier to `peer` class itself if
// present.
let variantSelector = variant.modifier
? `:where(.${theme.prefix ? `${theme.prefix}\\:` : ''}peer\\/${variant.modifier.value})`
: `:where(.${theme.prefix ? `${theme.prefix}\\:` : ''}peer)`
let didApply = false
walk([ruleNode], (node, ctx) => {
if (node.kind !== 'rule') return WalkAction.Continue
// Throw out any candidates with variants using nested style rules
for (let parent of ctx.path()) {
if (parent.kind !== 'rule') continue
didApply = false
return WalkAction.Stop
}
// For most variants we rely entirely on CSS nesting to build-up the final
// selector, but there is no way to use CSS nesting to make `&` refer to
// just the `.group` class the way we'd need to for these variants, so we
// need to replace it in the selector ourselves.
let selector = node.selector.replaceAll('&', variantSelector)
// When the selector is a selector _list_ we need to wrap it in `:is`
// to make sure the matching behavior is consistent with the original
// variant / selector.
if (segment(selector, ',').length > 1) {
selector = `:is(${selector})`
}
node.selector = `&:is(${selector} ~ *)`
// Track that the variant was actually applied
didApply = true
})
// If the node wasn't modified, this variant is not compatible with
// `peer-*` so discard the candidate.
if (!didApply) return null
})
variants.suggest('peer', () => {
return Array.from(variants.keys()).filter((name) => {
return variants.compoundsWith('peer', name)
})
})
staticVariant('first-letter', ['&::first-letter'])
staticVariant('first-line', ['&::first-line'])
staticVariant('marker', [
'& *::marker',
'&::marker',
'& *::-webkit-details-marker',
'&::-webkit-details-marker',
])
staticVariant('selection', ['& *::selection', '&::selection'])
staticVariant('file', ['&::file-selector-button'])
staticVariant('placeholder', ['&::placeholder'])
staticVariant('backdrop', ['&::backdrop'])
staticVariant('details-content', ['&::details-content'])
{
function contentProperties() {
return atRoot([
atRule('@property', '--tw-content', [
decl('syntax', '"*"'),
decl('initial-value', '""'),
decl('inherits', 'false'),
]),
])
}
variants.static(
'before',
(v) => {
v.nodes = [
styleRule('&::before', [
contentProperties(),
decl('content', 'var(--tw-content)'),
...v.nodes,
]),
]
},
{ compounds: Compounds.Never },
)
variants.static(
'after',
(v) => {
v.nodes = [
styleRule('&::after', [
contentProperties(),
decl('content', 'var(--tw-content)'),
...v.nodes,
]),
]
},
{ compounds: Compounds.Never },
)
}
// Positional
staticVariant('first', ['&:first-child'])
staticVariant('last', ['&:last-child'])
staticVariant('only', ['&:only-child'])
staticVariant('odd', ['&:nth-child(odd)'])
staticVariant('even', ['&:nth-child(even)'])
staticVariant('first-of-type', ['&:first-of-type'])
staticVariant('last-of-type', ['&:last-of-type'])
staticVariant('only-of-type', ['&:only-of-type'])
// State
staticVariant('visited', ['&:visited'])
staticVariant('target', ['&:target'])
staticVariant('open', ['&:is([open], :popover-open, :open)'])
// Forms
staticVariant('default', ['&:default'])
staticVariant('checked', ['&:checked'])
staticVariant('indeterminate', ['&:indeterminate'])
staticVariant('placeholder-shown', ['&:placeholder-shown'])
staticVariant('autofill', ['&:autofill'])
staticVariant('optional', ['&:optional'])
staticVariant('required', ['&:required'])
staticVariant('valid', ['&:valid'])
staticVariant('invalid', ['&:invalid'])
staticVariant('user-valid', ['&:user-valid'])
staticVariant('user-invalid', ['&:user-invalid'])
staticVariant('in-range', ['&:in-range'])
staticVariant('out-of-range', ['&:out-of-range'])
staticVariant('read-only', ['&:read-only'])
// Content
staticVariant('empty', ['&:empty'])
// Interactive
staticVariant('focus-within', ['&:focus-within'])
variants.static('hover', (r) => {
r.nodes = [styleRule('&:hover', [atRule('@media', '(hover: hover)', r.nodes)])]
})
staticVariant('focus', ['&:focus'])
staticVariant('focus-visible', ['&:focus-visible'])
staticVariant('active', ['&:active'])
staticVariant('enabled', ['&:enabled'])
staticVariant('disabled', ['&:disabled'])
staticVariant('inert', ['&:is([inert], [inert] *)'])
variants.compound('in', Compounds.StyleRules, (ruleNode, variant) => {
if (variant.modifier) return null
let didApply = false
walk([ruleNode], (node, ctx) => {
if (node.kind !== 'rule') return WalkAction.Continue
// Throw out any candidates with variants using nested style rules
for (let parent of ctx.path()) {
if (parent.kind !== 'rule') continue
didApply = false
return WalkAction.Stop
}
// Replace `&` in target variant with `*`, so variants like `&:hover`
// become `:where(*:hover) &`. The `*` will often be optimized away.
node.selector = `:where(${node.selector.replaceAll('&', '*')}) &`
// Track that the variant was actually applied
didApply = true
})
// If the node wasn't modified, this variant is not compatible with
// `in-*` so discard the candidate.
if (!didApply) return null
})
variants.suggest('in', () => {
return Array.from(variants.keys()).filter((name) => {
return variants.compoundsWith('in', name)
})
})
variants.compound('has', Compounds.StyleRules, (ruleNode, variant) => {
if (variant.modifier) return null
let didApply = false
walk([ruleNode], (node, ctx) => {
if (node.kind !== 'rule') return WalkAction.Continue
// Throw out any candidates with variants using nested style rules
for (let parent of ctx.path()) {
if (parent.kind !== 'rule') continue
didApply = false
return WalkAction.Stop
}
// Replace `&` in target variant with `*`, so variants like `&:hover`
// become `&:has(*:hover)`. The `*` will often be optimized away.
node.selector = `&:has(${node.selector.replaceAll('&', '*')})`
// Track that the variant was actually applied
didApply = true
})
// If the node wasn't modified, this variant is not compatible with
// `has-*` so discard the candidate.
if (!didApply) return null
})
variants.suggest('has', () => {
return Array.from(variants.keys()).filter((name) => {
return variants.compoundsWith('has', name)
})
})
variants.functional('aria', (ruleNode, variant) => {
if (!variant.value || variant.modifier) return null
if (variant.value.kind === 'arbitrary') {
ruleNode.nodes = [
styleRule(`&[aria-${quoteAttributeValue(variant.value.value)}]`, ruleNode.nodes),
]
} else {
ruleNode.nodes = [styleRule(`&[aria-${variant.value.value}="true"]`, ruleNode.nodes)]
}
})
variants.suggest('aria', () => [
'busy',
'checked',
'disabled',
'expanded',
'hidden',
'pressed',
'readonly',
'required',
'selected',
])
variants.functional('data', (ruleNode, variant) => {
if (!variant.value || variant.modifier) return null
ruleNode.nodes = [
styleRule(`&[data-${quoteAttributeValue(variant.value.value)}]`, ruleNode.nodes),
]
})
variants.functional('nth', (ruleNode, variant) => {
if (!variant.value || variant.modifier) return null
// Only numeric bare values are allowed
if (variant.value.kind === 'named' && !isPositiveInteger(variant.value.value)) return null
ruleNode.nodes = [styleRule(`&:nth-child(${variant.value.value})`, ruleNode.nodes)]
})
variants.functional('nth-last', (ruleNode, variant) => {
if (!variant.value || variant.modifier) return null
// Only numeric bare values are allowed
if (variant.value.kind === 'named' && !isPositiveInteger(variant.value.value)) return null
ruleNode.nodes = [styleRule(`&:nth-last-child(${variant.value.value})`, ruleNode.nodes)]
})
variants.functional('nth-of-type', (ruleNode, variant) => {
if (!variant.value || variant.modifier) return null
// Only numeric bare values are allowed
if (variant.value.kind === 'named' && !isPositiveInteger(variant.value.value)) return null
ruleNode.nodes = [styleRule(`&:nth-of-type(${variant.value.value})`, ruleNode.nodes)]
})
variants.functional('nth-last-of-type', (ruleNode, variant) => {
if (!variant.value || variant.modifier) return null
// Only numeric bare values are allowed
if (variant.value.kind === 'named' && !isPositiveInteger(variant.value.value)) return null
ruleNode.nodes = [styleRule(`&:nth-last-of-type(${variant.value.value})`, ruleNode.nodes)]
})
variants.functional(
'supports',
(ruleNode, variant) => {
if (!variant.value || variant.modifier) return null
let value = variant.value.value
if (value === null) return null
// When the value starts with `not()`, `selector()`, `font-tech()`, or
// other functions, we can use the value as-is.
if (/^[\w-]*\s*\(/.test(value)) {
// Chrome has a bug where `(condition1)or(condition2)` is not valid, but
// `(condition1) or (condition2)` is supported.
let query = value.replace(/\b(and|or|not)\b/g, ' $1 ')
ruleNode.nodes = [atRule('@supports', query, ruleNode.nodes)]
return
}
// When `supports-[display]` is used as a shorthand, we need to make sure
// that this becomes a valid CSS supports condition.
//
// E.g.: `supports-[display]` -> `@supports (display: var(--tw))`
if (!value.includes(':')) {
value = `${value}: var(--tw)`
}
// When `supports-[display:flex]` is used, we need to make sure that this
// becomes a valid CSS supports condition by wrapping it in parens.
//
// E.g.: `supports-[display:flex]` -> `@supports (display: flex)`
//
// We also have to make sure that we wrap the value in parens if the last
// character is a paren already for situations where we are testing
// against a CSS custom property.
//
// E.g.: `supports-[display]:flex` -> `@supports (display: var(--tw))`
if (value[0] !== '(' || value[value.length - 1] !== ')') {
value = `(${value})`
}
ruleNode.nodes = [atRule('@supports', value, ruleNode.nodes)]
},
{ compounds: Compounds.AtRules },
)
staticVariant('motion-safe', ['@media (prefers-reduced-motion: no-preference)'])
staticVariant('motion-reduce', ['@media (prefers-reduced-motion: reduce)'])
staticVariant('contrast-more', ['@media (prefers-contrast: more)'])
staticVariant('contrast-less', ['@media (prefers-contrast: less)'])
{
// Helper to compare variants by their resolved values, this is used by the
// responsive variants (`sm`, `md`, ...), `min-*`, `max-*` and container
// queries (`@`).
function compareBreakpointVariants(
a: Variant,
z: Variant,
direction: 'asc' | 'desc',
lookup: { get(v: Variant): string | null },
) {
if (a === z) return 0
let aValue = lookup.get(a)
if (aValue === null) return direction === 'asc' ? -1 : 1
let zValue = lookup.get(z)
if (zValue === null) return direction === 'asc' ? 1 : -1
return compareBreakpoints(aValue, zValue, direction)
}
// Breakpoints
{
let breakpoints = theme.namespace('--breakpoint')
let resolvedBreakpoints = new DefaultMap((variant: Variant) => {
switch (variant.kind) {
case 'static': {
return theme.resolveValue(variant.root, ['--breakpoint']) ?? null
}
case 'functional': {
if (!variant.value || variant.modifier) return null
let value: string | null = null
if (variant.value.kind === 'arbitrary') {
value = variant.value.value
} else if (variant.value.kind === 'named') {
value = theme.resolveValue(variant.value.value, ['--breakpoint'])
}
if (!value) return null
if (value.includes('var(')) return null
return value
}
case 'arbitrary':
case 'compound':
return null
}
})
// Max
variants.group(
() => {
variants.functional(
'max',
(ruleNode, variant) => {
if (variant.modifier) return null
let value = resolvedBreakpoints.get(variant)
if (value === null) return null
ruleNode.nodes = [atRule('@media', `(width < ${value})`, ruleNode.nodes)]
},
{ compounds: Compounds.AtRules },
)
},
(a, z) => compareBreakpointVariants(a, z, 'desc', resolvedBreakpoints),
)
variants.suggest(
'max',
() => Array.from(breakpoints.keys()).filter((key) => key !== null) as string[],
)
// Min
variants.group(
() => {
// Registers breakpoint variants like `sm`, `md`, `lg`, etc.
for (let [key, value] of theme.namespace('--breakpoint')) {
if (key === null) continue
variants.static(
key,
(ruleNode) => {
ruleNode.nodes = [atRule('@media', `(width >= ${value})`, ruleNode.nodes)]
},
{ compounds: Compounds.AtRules },
)
}
variants.functional(
'min',
(ruleNode, variant) => {
if (variant.modifier) return null
let value = resolvedBreakpoints.get(variant)
if (value === null) return null
ruleNode.nodes = [atRule('@media', `(width >= ${value})`, ruleNode.nodes)]
},
{ compounds: Compounds.AtRules },
)
},
(a, z) => compareBreakpointVariants(a, z, 'asc', resolvedBreakpoints),
)
variants.suggest(
'min',
() => Array.from(breakpoints.keys()).filter((key) => key !== null) as string[],
)
}
{
let widths = theme.namespace('--container')
// Container queries
let resolvedWidths = new DefaultMap((variant: Variant) => {
switch (variant.kind) {
case 'functional': {
if (variant.value === null) return null
let value: string | null = null
if (variant.value.kind === 'arbitrary') {
value = variant.value.value
} else if (variant.value.kind === 'named') {
value = theme.resolveValue(variant.value.value, ['--container'])
}
if (!value) return null
if (value.includes('var(')) return null
return value
}
case 'static':
case 'arbitrary':
case 'compound':
return null
}
})
variants.group(
() => {
variants.functional(
'@max',
(ruleNode, variant) => {
let value = resolvedWidths.get(variant)
if (value === null) return null
ruleNode.nodes = [
atRule(
'@container',
variant.modifier
? `${variant.modifier.value} (width < ${value})`
: `(width < ${value})`,
ruleNode.nodes,
),
]
},
{ compounds: Compounds.AtRules },
)
},
(a, z) => compareBreakpointVariants(a, z, 'desc', resolvedWidths),
)
variants.suggest(
'@max',
() => Array.from(widths.keys()).filter((key) => key !== null) as string[],
)
variants.group(
() => {
variants.functional(
'@',
(ruleNode, variant) => {
let value = resolvedWidths.get(variant)
if (value === null) return null
ruleNode.nodes = [
atRule(
'@container',
variant.modifier
? `${variant.modifier.value} (width >= ${value})`
: `(width >= ${value})`,
ruleNode.nodes,
),
]
},
{ compounds: Compounds.AtRules },
)
variants.functional(
'@min',
(ruleNode, variant) => {
let value = resolvedWidths.get(variant)
if (value === null) return null
ruleNode.nodes = [
atRule(
'@container',
variant.modifier
? `${variant.modifier.value} (width >= ${value})`
: `(width >= ${value})`,
ruleNode.nodes,
),
]
},
{ compounds: Compounds.AtRules },
)
},
(a, z) => compareBreakpointVariants(a, z, 'asc', resolvedWidths),
)
variants.suggest(
'@min',
() => Array.from(widths.keys()).filter((key) => key !== null) as string[],
)
variants.suggest(
'@',
() => Array.from(widths.keys()).filter((key) => key !== null) as string[],
)
}
}
staticVariant('portrait', ['@media (orientation: portrait)'])
staticVariant('landscape', ['@media (orientation: landscape)'])
staticVariant('ltr', ['&:where(:dir(ltr), [dir="ltr"], [dir="ltr"] *)'])
staticVariant('rtl', ['&:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *)'])
staticVariant('dark', ['@media (prefers-color-scheme: dark)'])
staticVariant('starting', ['@starting-style'])
staticVariant('print', ['@media print'])
staticVariant('forced-colors', ['@media (forced-colors: active)'])
staticVariant('inverted-colors', ['@media (inverted-colors: inverted)'])
staticVariant('pointer-none', ['@media (pointer: none)'])
staticVariant('pointer-coarse', ['@media (pointer: coarse)'])
staticVariant('pointer-fine', ['@media (pointer: fine)'])
staticVariant('any-pointer-none', ['@media (any-pointer: none)'])
staticVariant('any-pointer-coarse', ['@media (any-pointer: coarse)'])
staticVariant('any-pointer-fine', ['@media (any-pointer: fine)'])
staticVariant('noscript', ['@media (scripting: none)'])
return variants
}
Domain
Subdomains
Calls
Called By
Source
Frequently Asked Questions
What does createVariants() do?
createVariants() is a function in the tailwindcss codebase.
What does createVariants() call?
createVariants() calls 21 function(s): atRoot, atRule, compareBreakpoints, compound, compoundsForSelectors, compoundsWith, decl, functional, and 13 more.
What calls createVariants()?
createVariants() is called by 1 function(s): buildDesignSystem.
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free