Skip to content

Commit 8ef8419

Browse files
committed
register @custom-variant in the correct topological order
1 parent 5f52530 commit 8ef8419

File tree

1 file changed

+36
-6
lines changed

1 file changed

+36
-6
lines changed

packages/tailwindcss/src/index.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { createCssUtility } from './utilities'
3232
import { expand } from './utils/brace-expansion'
3333
import { escape, unescape } from './utils/escape'
3434
import { segment } from './utils/segment'
35+
import { topologicalSort } from './utils/topological-sort'
3536
import { compoundsForSelectors, IS_VALID_VARIANT_NAME, substituteAtVariant } from './variants'
3637
export type Config = UserConfig
3738

@@ -150,7 +151,8 @@ async function parseCss(
150151

151152
let important = null as boolean | null
152153
let theme = new Theme()
153-
let customVariants: ((designSystem: DesignSystem) => void)[] = []
154+
let customVariants = new Map<string, (designSystem: DesignSystem) => void>()
155+
let customVariantDependencies = new Map<string, Set<string>>()
154156
let customUtilities: ((designSystem: DesignSystem) => void)[] = []
155157
let firstThemeRule = null as StyleRule | null
156158
let utilitiesNode = null as AtRule | null
@@ -390,7 +392,7 @@ async function parseCss(
390392
}
391393
}
392394

393-
customVariants.push((designSystem) => {
395+
customVariants.set(name, (designSystem) => {
394396
designSystem.variants.static(
395397
name,
396398
(r) => {
@@ -411,6 +413,7 @@ async function parseCss(
411413
},
412414
)
413415
})
416+
customVariantDependencies.set(name, new Set<string>())
414417

415418
return
416419
}
@@ -431,9 +434,17 @@ async function parseCss(
431434
// }
432435
// ```
433436
else {
434-
customVariants.push((designSystem) => {
435-
designSystem.variants.fromAst(name, node.nodes)
437+
let dependencies = new Set<string>()
438+
walk(node.nodes, (child) => {
439+
if (child.kind === 'at-rule' && child.name === '@variant') {
440+
dependencies.add(child.params)
441+
}
442+
})
443+
444+
customVariants.set(name, (designSystem) => {
445+
designSystem.variants.fromAst(name, node.nodes, designSystem)
436446
})
447+
customVariantDependencies.set(name, dependencies)
437448

438449
return
439450
}
@@ -605,8 +616,27 @@ async function parseCss(
605616
sources,
606617
})
607618

608-
for (let customVariant of customVariants) {
609-
customVariant(designSystem)
619+
for (let name of customVariants.keys()) {
620+
// Pre-register the variant to ensure its position in the variant list is
621+
// based on the order we see them in the CSS.
622+
designSystem.variants.static(name, () => {})
623+
}
624+
625+
// Register custom variants in order
626+
for (let variant of topologicalSort(customVariantDependencies, {
627+
onCircularDependency(path, start) {
628+
let output = toCss(
629+
path.map((name, idx) => {
630+
return atRule('@custom-variant', name, [atRule('@variant', path[idx + 1] ?? start, [])])
631+
}),
632+
)
633+
.replaceAll(';', ' { … }')
634+
.replace(`@custom-variant ${start} {`, `@custom-variant ${start} { /* ← */`)
635+
636+
throw new Error(`Circular dependency detected in custom variants:\n\n${output}`)
637+
},
638+
})) {
639+
customVariants.get(variant)?.(designSystem)
610640
}
611641

612642
for (let customUtility of customUtilities) {

0 commit comments

Comments
 (0)