diff --git a/__fixtures__/arbitraryVariants/arbitraryVariants.js b/__fixtures__/arbitraryVariants/arbitraryVariants.js new file mode 100644 index 00000000..6f636efd --- /dev/null +++ b/__fixtures__/arbitraryVariants/arbitraryVariants.js @@ -0,0 +1,18 @@ +import tw from './macro' + +tw`[section]:hover:block` + +tw`[p]:hover:block` +tw`hover:[p]:block` + +tw`[* + *]:block` // Spaces +tw`[.class1 .class2]:block` // Classes + +tw`[.class1]:[.class2]:block` // Multiple dynamic variants +tw`[.class1 .class2]:[.class3]:block` // Multiple dynamic variants + +tw`[p]:placeholder-red-500/[var(--myvar)]` +tw`[p]:mt-[var(--myvar)]` +tw`[p]:marginTop[var(--myvar)]` + +tw`[p]:(mt-4 mb-4)` diff --git a/__snapshots__/plugin.test.js.snap b/__snapshots__/plugin.test.js.snap index fb085d6a..be9cb0d1 100644 --- a/__snapshots__/plugin.test.js.snap +++ b/__snapshots__/plugin.test.js.snap @@ -3111,6 +3111,105 @@ tw\`md:text-[red]\` }) +`; + +exports[`twin.macro arbitraryVariants.js: arbitraryVariants.js 1`] = ` + +import tw from './macro' + +tw\`[section]:hover:block\` + +tw\`[p]:hover:block\` +tw\`hover:[p]:block\` + +tw\`[* + *]:block\` // Spaces +tw\`[.class1 .class2]:block\` // Classes + +tw\`[.class1]:[.class2]:block\` // Multiple dynamic variants +tw\`[.class1 .class2]:[.class3]:block\` // Multiple dynamic variants + +tw\`[p]:placeholder-red-500/[var(--myvar)]\` +tw\`[p]:mt-[var(--myvar)]\` +tw\`[p]:marginTop[var(--myvar)]\` + +tw\`[p]:(mt-4 mb-4)\` + + ↓ ↓ ↓ ↓ ↓ ↓ + +;({ + section: { + ':hover': { + display: 'block', + }, + }, +}) +;({ + p: { + ':hover': { + display: 'block', + }, + }, +}) +;({ + ':hover': { + p: { + display: 'block', + }, + }, +}) +;({ + '* + *': { + display: 'block', + }, +}) // Spaces + +;({ + '.class1 .class2': { + display: 'block', + }, +}) // Classes + +;({ + '.class1': { + '.class2': { + display: 'block', + }, + }, +}) // Multiple dynamic variants + +;({ + '.class1 .class2': { + '.class3': { + display: 'block', + }, + }, +}) // Multiple dynamic variants + +;({ + p: { + '::placeholder': { + color: 'rgba(239, 68, 68, var(--myvar))', + }, + }, +}) +;({ + p: { + marginTop: 'var(--myvar)', + }, +}) +;({ + p: { + marginTop: 'var(--myvar)', + }, +}) +;({ + p: { + marginTop: '1rem', + marginBottom: '1rem', + }, +}) + + `; exports[`twin.macro autoCssProp.js: autoCssProp.js 1`] = ` diff --git a/src/variants.js b/src/variants.js index 6431ad92..a844b30d 100644 --- a/src/variants.js +++ b/src/variants.js @@ -39,6 +39,12 @@ const validateVariants = ({ variants, state, ...rest }) => { if (isResponsive) return stringifyScreen(state.config, variant) let foundVariant = fullVariantConfig[variant] + + if (!foundVariant) { + const arbitraryVariant = variant.match(/^\[(.+)]/) + if (arbitraryVariant) foundVariant = arbitraryVariant[1] + } + if (!foundVariant) { if (variant === 'only-child') { throw new MacroError( @@ -87,10 +93,14 @@ const splitVariants = ({ classNameRaw, state }) => { let variant let className = classNameRaw while (variant !== null) { - variant = className.match(/^([\d_a-z-]+):/) + // Match arbitrary variants + variant = className.match(/^([\d_a-z-]+):|^\[.*?]:/) + if (variant) { className = className.slice(variant[0].length) - variantsList.push(variant[1]) + variantsList.push( + variant[0].slice(0, -1).replace(new RegExp(SPACE_ID, 'g'), ' ') + ) } } @@ -214,7 +224,8 @@ function spreadVariantGroups( const results = [] classes = classes.slice(start, end).trim() - const reg = /([\w-]+:)|([\w-./[\]]+!?)|\(|(\S+)/g + // variant / class / group + const reg = /(\[.*?]:|[\w-]+:)|([\w-./[\]]+!?)|\(|(\S+)/g let match const baseContext = context @@ -223,7 +234,9 @@ function spreadVariantGroups( const [, variant, className, weird] = match if (variant) { - context += variant + // Replace arbitrary variant spaces with a placeholder to avoid incorrect splitting + const spaceReplacedVariant = variant.replace(/\s+/g, SPACE_ID) + context += spaceReplacedVariant // Skip empty classes if (/\s/.test(classes[reg.lastIndex])) {