Don't prefix arbitrary classes in peer/group variants (#11454)
* Refactor * Don’t prefix classes in arbitrary values for group and peer * use `foo` instead of `lol` * handle the prefix inside the group/peer variants Then add the `NoPrefix` feature to the variant itself, which will skip prefixing any other class in the generated selector (because we already took care of prefixing `.group` and `.peer`). We are using an internal symbol such that: - We can keep it as a private API - We don't introduce a breaking change * refactor to simple object instead We will still use a symbol as an internal/private marker, but the data itself will be a simple object for now. If we want to refactor this (and more) in the future using bitflags then we can refactor that in a separate PR. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This commit is contained in:
parent
5b9cbb3a81
commit
005c1be2ed
9 changed files with 130 additions and 57 deletions
|
|
@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Ensure `repeating-conic-gradient` is detected as an image ([#11180](https://github.com/tailwindlabs/tailwindcss/pull/11180))
|
||||
- Move unknown pseudo-elements outside of `:is` by default ([#11345](https://github.com/tailwindlabs/tailwindcss/pull/11345))
|
||||
- Escape animation names when prefixes contain special characters ([#11470](https://github.com/tailwindlabs/tailwindcss/pull/11470))
|
||||
- Don't prefix arbitrary classes in `group` and `peer` variants ([#11454](https://github.com/tailwindlabs/tailwindcss/pull/11454))
|
||||
- Sort classes using position of first matching rule ([#11504](https://github.com/tailwindlabs/tailwindcss/pull/11504))
|
||||
- Allow variant to be an at-rule without a prelude ([#11589](https://github.com/tailwindlabs/tailwindcss/pull/11589))
|
||||
- Make PostCSS plugin async to improve performance ([#11548](https://github.com/tailwindlabs/tailwindcss/pull/11548))
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadow
|
|||
import { removeAlphaVariables } from './util/removeAlphaVariables'
|
||||
import { flagEnabled } from './featureFlags'
|
||||
import { normalize } from './util/dataTypes'
|
||||
import { INTERNAL_FEATURES } from './lib/setupContextUtils'
|
||||
|
||||
export let variantPlugins = {
|
||||
pseudoElementVariants: ({ addVariant }) => {
|
||||
|
|
@ -80,7 +81,7 @@ export let variantPlugins = {
|
|||
})
|
||||
},
|
||||
|
||||
pseudoClassVariants: ({ addVariant, matchVariant, config }) => {
|
||||
pseudoClassVariants: ({ addVariant, matchVariant, config, prefix }) => {
|
||||
let pseudoVariants = [
|
||||
// Positional
|
||||
['first', '&:first-child'],
|
||||
|
|
@ -151,12 +152,12 @@ export let variantPlugins = {
|
|||
let variants = {
|
||||
group: (_, { modifier }) =>
|
||||
modifier
|
||||
? [`:merge(.group\\/${escapeClassName(modifier)})`, ' &']
|
||||
: [`:merge(.group)`, ' &'],
|
||||
? [`:merge(${prefix('.group')}\\/${escapeClassName(modifier)})`, ' &']
|
||||
: [`:merge(${prefix('.group')})`, ' &'],
|
||||
peer: (_, { modifier }) =>
|
||||
modifier
|
||||
? [`:merge(.peer\\/${escapeClassName(modifier)})`, ' ~ &']
|
||||
: [`:merge(.peer)`, ' ~ &'],
|
||||
? [`:merge(${prefix('.peer')}\\/${escapeClassName(modifier)})`, ' ~ &']
|
||||
: [`:merge(${prefix('.peer')})`, ' ~ &'],
|
||||
}
|
||||
|
||||
for (let [name, fn] of Object.entries(variants)) {
|
||||
|
|
@ -192,7 +193,12 @@ export let variantPlugins = {
|
|||
|
||||
return result.slice(0, start) + a + result.slice(start + 1, end) + b + result.slice(end)
|
||||
},
|
||||
{ values: Object.fromEntries(pseudoVariants) }
|
||||
{
|
||||
values: Object.fromEntries(pseudoVariants),
|
||||
[INTERNAL_FEATURES]: {
|
||||
respectPrefix: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from '../util/formatVariantSelector'
|
||||
import { asClass } from '../util/nameClass'
|
||||
import { normalize } from '../util/dataTypes'
|
||||
import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
|
||||
import { isValidVariantFormatString, parseVariant, INTERNAL_FEATURES } from './setupContextUtils'
|
||||
import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
|
||||
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
|
||||
import { flagEnabled } from '../featureFlags'
|
||||
|
|
@ -230,9 +230,16 @@ function applyVariant(variant, matches, context) {
|
|||
|
||||
if (context.variantMap.has(variant)) {
|
||||
let isArbitraryVariant = isArbitraryValue(variant)
|
||||
let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {}
|
||||
let variantFunctionTuples = context.variantMap.get(variant).slice()
|
||||
let result = []
|
||||
|
||||
let respectPrefix = (() => {
|
||||
if (isArbitraryVariant) return false
|
||||
if (internalFeatures.respectPrefix === false) return false
|
||||
return true
|
||||
})()
|
||||
|
||||
for (let [meta, rule] of matches) {
|
||||
// Don't generate variants for user css
|
||||
if (meta.layer === 'user') {
|
||||
|
|
@ -293,7 +300,7 @@ function applyVariant(variant, matches, context) {
|
|||
format(selectorFormat) {
|
||||
collectedFormats.push({
|
||||
format: selectorFormat,
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
})
|
||||
},
|
||||
args,
|
||||
|
|
@ -322,7 +329,7 @@ function applyVariant(variant, matches, context) {
|
|||
if (typeof ruleWithVariant === 'string') {
|
||||
collectedFormats.push({
|
||||
format: ruleWithVariant,
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +373,7 @@ function applyVariant(variant, matches, context) {
|
|||
// format: .foo &
|
||||
collectedFormats.push({
|
||||
format: modified.replace(rebuiltBase, '&'),
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
})
|
||||
rule.selector = before
|
||||
})
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import { Offsets } from './offsets.js'
|
|||
import { flagEnabled } from '../featureFlags.js'
|
||||
import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'
|
||||
|
||||
export const INTERNAL_FEATURES = Symbol()
|
||||
|
||||
const VARIANT_TYPES = {
|
||||
AddVariant: Symbol.for('ADD_VARIANT'),
|
||||
MatchVariant: Symbol.for('MATCH_VARIANT'),
|
||||
|
|
@ -1123,17 +1125,24 @@ function registerPlugins(plugins, context) {
|
|||
}
|
||||
|
||||
let isArbitraryVariant = !(value in (options.values ?? {}))
|
||||
let internalFeatures = options[INTERNAL_FEATURES] ?? {}
|
||||
|
||||
let respectPrefix = (() => {
|
||||
if (isArbitraryVariant) return false
|
||||
if (internalFeatures.respectPrefix === false) return false
|
||||
return true
|
||||
})()
|
||||
|
||||
formatStrings = formatStrings.map((format) =>
|
||||
format.map((str) => ({
|
||||
format: str,
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
}))
|
||||
)
|
||||
|
||||
manualFormatStrings = manualFormatStrings.map((format) => ({
|
||||
format,
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
}))
|
||||
|
||||
let opts = {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { movePseudos } from './pseudoElements'
|
|||
/** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
|
||||
/** @typedef {import('postcss-selector-parser').Node} Node */
|
||||
|
||||
/** @typedef {{format: string, isArbitraryVariant: boolean}[]} RawFormats */
|
||||
/** @typedef {{format: string, respectPrefix: boolean}[]} RawFormats */
|
||||
/** @typedef {import('postcss-selector-parser').Root} ParsedFormats */
|
||||
/** @typedef {RawFormats | ParsedFormats} AcceptedFormats */
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ export function formatVariantSelector(formats, { context, candidate }) {
|
|||
|
||||
return {
|
||||
...format,
|
||||
ast: format.isArbitraryVariant ? ast : prefixSelector(prefix, ast),
|
||||
ast: format.respectPrefix ? prefixSelector(prefix, ast) : ast,
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export default function (prefix, selector, prependNegative = false) {
|
|||
return selector
|
||||
}
|
||||
|
||||
/** @type {import('postcss-selector-parser').Root} */
|
||||
let ast = typeof selector === 'string' ? parser().astSync(selector) : selector
|
||||
|
||||
ast.walkClasses((classSelector) => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ crosscheck(() => {
|
|||
let selector = '.text-center'
|
||||
let candidate = 'hover:text-center'
|
||||
|
||||
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
||||
let formats = [{ format: '&:hover', respectPrefix: true }]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual('.hover\\:text-center:hover')
|
||||
})
|
||||
|
|
@ -16,8 +16,8 @@ crosscheck(() => {
|
|||
let candidate = 'focus:hover:text-center'
|
||||
|
||||
let formats = [
|
||||
{ format: '&:hover', isArbitraryVariant: false },
|
||||
{ format: '&:focus', isArbitraryVariant: false },
|
||||
{ format: '&:hover', respectPrefix: true },
|
||||
{ format: '&:focus', respectPrefix: true },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -29,7 +29,7 @@ crosscheck(() => {
|
|||
let selector = '.bg-\\[rgba\\(0\\,0\\,0\\)\\]'
|
||||
let candidate = 'hover:bg-[rgba(0,0,0)]'
|
||||
|
||||
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
||||
let formats = [{ format: '&:hover', respectPrefix: true }]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
'.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover'
|
||||
|
|
@ -40,7 +40,7 @@ crosscheck(() => {
|
|||
let selector = '.bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]'
|
||||
let candidate = 'hover:bg-[rgba(0,0,0)]'
|
||||
|
||||
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
||||
let formats = [{ format: '&:hover', respectPrefix: true }]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
'.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover'
|
||||
|
|
@ -51,7 +51,7 @@ crosscheck(() => {
|
|||
let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
|
||||
let candidate = 'hover:space-x-4'
|
||||
|
||||
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
||||
let formats = [{ format: '&:hover', respectPrefix: true }]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
'.hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])'
|
||||
|
|
@ -63,9 +63,9 @@ crosscheck(() => {
|
|||
let candidate = 'disabled:focus:hover:space-x-4'
|
||||
|
||||
let formats = [
|
||||
{ format: '&:hover', isArbitraryVariant: false },
|
||||
{ format: '&:focus', isArbitraryVariant: false },
|
||||
{ format: '&:disabled', isArbitraryVariant: false },
|
||||
{ format: '&:hover', respectPrefix: true },
|
||||
{ format: '&:focus', respectPrefix: true },
|
||||
{ format: '&:disabled', respectPrefix: true },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -77,7 +77,7 @@ crosscheck(() => {
|
|||
let selector = '.text-center'
|
||||
let candidate = 'group-hover:text-center'
|
||||
|
||||
let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }]
|
||||
let formats = [{ format: ':merge(.group):hover &', respectPrefix: true }]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
'.group:hover .group-hover\\:text-center'
|
||||
|
|
@ -89,8 +89,8 @@ crosscheck(() => {
|
|||
let candidate = 'group-focus:group-hover:text-center'
|
||||
|
||||
let formats = [
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):focus &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true },
|
||||
{ format: ':merge(.group):focus &', respectPrefix: true },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -102,7 +102,7 @@ crosscheck(() => {
|
|||
let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])'
|
||||
let candidate = 'group-hover:space-x-4'
|
||||
|
||||
let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }]
|
||||
let formats = [{ format: ':merge(.group):hover &', respectPrefix: true }]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
'.group:hover .group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])'
|
||||
|
|
@ -114,8 +114,8 @@ crosscheck(() => {
|
|||
let candidate = 'group-focus:group-hover:space-x-4'
|
||||
|
||||
let formats = [
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):focus &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true },
|
||||
{ format: ':merge(.group):focus &', respectPrefix: true },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -128,7 +128,7 @@ crosscheck(() => {
|
|||
let candidate = 'peer-focus:group-hover:text-center'
|
||||
|
||||
let formats = [
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true },
|
||||
{ format: ':merge(.peer):focus ~ &' },
|
||||
]
|
||||
|
||||
|
|
@ -142,8 +142,8 @@ crosscheck(() => {
|
|||
let candidate = 'group-hover:peer-focus:text-center'
|
||||
|
||||
let formats = [
|
||||
{ format: ':merge(.peer):focus ~ &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.peer):focus ~ &', respectPrefix: true },
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -156,10 +156,10 @@ crosscheck(() => {
|
|||
let candidate = 'peer-focus:group-focus:peer-hover:group-hover:foo'
|
||||
|
||||
let formats = [
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.peer):hover ~ &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):focus &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.peer):focus ~ &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true },
|
||||
{ format: ':merge(.peer):hover ~ &', respectPrefix: true },
|
||||
{ format: ':merge(.group):focus &', respectPrefix: true },
|
||||
{ format: ':merge(.peer):focus ~ &', respectPrefix: true },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -171,8 +171,8 @@ crosscheck(() => {
|
|||
let selector = '.text-center'
|
||||
let candidate = 'group-hover:prose-headings:text-center'
|
||||
let formats = [
|
||||
{ format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover
|
||||
{ format: ':where(&) :is(h1, h2, h3, h4)', respectPrefix: true }, // Prose Headings
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true }, // Group Hover
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -184,8 +184,8 @@ crosscheck(() => {
|
|||
let selector = '.text-center'
|
||||
let candidate = 'prose-headings:group-hover:text-center'
|
||||
let formats = [
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover
|
||||
{ format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true }, // Group Hover
|
||||
{ format: ':where(&) :is(h1, h2, h3, h4)', respectPrefix: true }, // Prose Headings
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -197,12 +197,12 @@ crosscheck(() => {
|
|||
let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
|
||||
let candidate = 'peer-disabled:peer-first-child:group-hover:group-focus:focus:hover:space-x-4'
|
||||
let formats = [
|
||||
{ format: '&:hover', isArbitraryVariant: false }, // Hover
|
||||
{ format: '&:focus', isArbitraryVariant: false }, // Focus
|
||||
{ format: ':merge(.group):focus &', isArbitraryVariant: false }, // Group focus
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group hover
|
||||
{ format: ':merge(.peer):first-child ~ &', isArbitraryVariant: false }, // Peer first-child
|
||||
{ format: ':merge(.peer):disabled ~ &', isArbitraryVariant: false }, // Peer disabled
|
||||
{ format: '&:hover', respectPrefix: true }, // Hover
|
||||
{ format: '&:focus', respectPrefix: true }, // Focus
|
||||
{ format: ':merge(.group):focus &', respectPrefix: true }, // Group focus
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true }, // Group hover
|
||||
{ format: ':merge(.peer):first-child ~ &', respectPrefix: true }, // Peer first-child
|
||||
{ format: ':merge(.peer):disabled ~ &', respectPrefix: true }, // Peer disabled
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -223,7 +223,7 @@ crosscheck(() => {
|
|||
let context = { tailwindConfig: { prefix: 'tw-' } }
|
||||
let selector = '.tw-text-center'
|
||||
let candidate = 'foo:tw-text-center'
|
||||
let formats = [{ format: '.foo &', isArbitraryVariant: false }]
|
||||
let formats = [{ format: '.foo &', respectPrefix: true }]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate, context })).toEqual(
|
||||
'.tw-foo .foo\\:tw-text-center'
|
||||
|
|
@ -234,7 +234,7 @@ crosscheck(() => {
|
|||
let context = { tailwindConfig: { prefix: 'tw-' } }
|
||||
let selector = '.tw-text-center'
|
||||
let candidate = '[.foo_&]:tw-text-center'
|
||||
let formats = [{ format: '.foo &', isArbitraryVariant: true }]
|
||||
let formats = [{ format: '.foo &', respectPrefix: false }]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate, context })).toEqual(
|
||||
'.foo .\\[\\.foo_\\&\\]\\:tw-text-center'
|
||||
|
|
@ -247,8 +247,8 @@ crosscheck(() => {
|
|||
let selector = '.text-center'
|
||||
let candidate = 'text-center'
|
||||
let formats = [
|
||||
{ format: ':merge(.group):focus > &', isArbitraryVariant: true },
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: true },
|
||||
{ format: ':merge(.group):focus > &', respectPrefix: false },
|
||||
{ format: ':merge(.group):hover &', respectPrefix: false },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -261,7 +261,7 @@ crosscheck(() => {
|
|||
let selector = '.placeholder-red-500::placeholder'
|
||||
let candidate = 'hover:placeholder-red-500'
|
||||
|
||||
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
||||
let formats = [{ format: '&:hover', respectPrefix: true }]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
'.hover\\:placeholder-red-500:hover::placeholder'
|
||||
|
|
@ -273,8 +273,8 @@ crosscheck(() => {
|
|||
let candidate = 'group-hover:hover:space-x-4'
|
||||
|
||||
let formats = [
|
||||
{ format: '&:hover', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
||||
{ format: '&:hover', respectPrefix: true },
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -287,8 +287,8 @@ crosscheck(() => {
|
|||
let candidate = 'dark:group-hover:text-center'
|
||||
|
||||
let formats = [
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
||||
{ format: '.dark &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true },
|
||||
{ format: '.dark &', respectPrefix: true },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -302,7 +302,7 @@ crosscheck(() => {
|
|||
|
||||
let formats = [
|
||||
{ format: '.dark &' },
|
||||
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
||||
{ format: ':merge(.group):hover &', respectPrefix: true },
|
||||
]
|
||||
|
||||
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
||||
|
|
@ -357,7 +357,7 @@ crosscheck(() => {
|
|||
${'.parent::placeholder input'} | ${'.parent input::placeholder'}
|
||||
${'.parent::backdrop dialog'} | ${'.parent dialog::backdrop'}
|
||||
`('should translate "$before" into "$after"', ({ before, after }) => {
|
||||
let result = finalizeSelector('.a', [{ format: before, isArbitraryVariant: false }], {
|
||||
let result = finalizeSelector('.a', [{ format: before, respectPrefix: true }], {
|
||||
candidate: 'a',
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,25 @@ crosscheck(() => {
|
|||
expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual(['.foo .group\\/foo &'])
|
||||
})
|
||||
|
||||
it('should provide selectors for complex matchVariant variants like `group` (when using a prefix)', () => {
|
||||
let config = { prefix: 'tw-' }
|
||||
let context = createContext(resolveConfig(config))
|
||||
|
||||
let variants = context.getVariants()
|
||||
|
||||
let variant = variants.find((v) => v.name === 'group')
|
||||
expect(variant.selectors()).toEqual(['.tw-group &'])
|
||||
expect(variant.selectors({})).toEqual(['.tw-group &'])
|
||||
expect(variant.selectors({ value: 'hover' })).toEqual(['.tw-group:hover &'])
|
||||
expect(variant.selectors({ value: '.foo_&' })).toEqual(['.foo .tw-group &'])
|
||||
expect(variant.selectors({ modifier: 'foo', value: 'hover' })).toEqual([
|
||||
'.tw-group\\/foo:hover &',
|
||||
])
|
||||
expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual([
|
||||
'.foo .tw-group\\/foo &',
|
||||
])
|
||||
})
|
||||
|
||||
it('should provide selectors for variants with atrules', () => {
|
||||
let config = {}
|
||||
let context = createContext(resolveConfig(config))
|
||||
|
|
|
|||
|
|
@ -623,3 +623,33 @@ crosscheck(({ stable, oxide }) => {
|
|||
`)
|
||||
})
|
||||
})
|
||||
|
||||
test('does not prefix arbitrary group/peer classes', async () => {
|
||||
let config = {
|
||||
prefix: 'tw-',
|
||||
content: [
|
||||
{
|
||||
raw: html`
|
||||
<div class="tw-group tw-peer foo">
|
||||
<div class="group-[&.foo]:tw-flex"></div>
|
||||
</div>
|
||||
<div class="peer-[&.foo]:tw-flex"></div>
|
||||
`,
|
||||
},
|
||||
],
|
||||
corePlugins: { preflight: false },
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
const result = await run(input, config)
|
||||
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.tw-group.foo .group-\[\&\.foo\]\:tw-flex,
|
||||
.tw-peer.foo ~ .peer-\[\&\.foo\]\:tw-flex {
|
||||
display: flex;
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue