Repository: tailwindlabs/tailwindcss

Problem statement:
Classes inside arbitrary variants are incorrectly being prefixed
Classes inside arbitrary variants are prefixed when they shouldn't be as they're not tailwind utilities.

The utility `[.foo_&]:tw-text-red-400` incorrectly generates a selector referencing `.tw-foo` instead of `.foo`

See: https://github.com/tailwindlabs/tailwindcss/pull/8299#issuecomment-1154053957

Reproduction: https://play.tailwindcss.com/GCMzAzdKNY




Hints:
I put \ in front of '.foo_&' and it is some how giving selector .foo 

https://play.tailwindcss.com/LWujxXokrG
@thecrypticace I built upon your example and created an example to test classes, IDs, and attributes.

It appears IDs work as expected, but both classes and attributes experience bugs when using prefixes, and when not using prefixes, only the attributes examples break:

* **With prefix:** https://play.tailwindcss.com/kKJt04VYW2 (classes and attributes break)
* **Without prefix:** https://play.tailwindcss.com/PF2YRSjHnL (only attributes break)

Patch:
diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js
--- a/src/lib/generateRules.js
+++ b/src/lib/generateRules.js
@@ -129,6 +129,7 @@ function applyVariant(variant, matches, context) {
   }
 
   let args
+  let isArbitraryVariant = false
 
   // Find partial arbitrary variants
   if (variant.endsWith(']') && !variant.startsWith('[')) {
@@ -144,6 +145,8 @@ function applyVariant(variant, matches, context) {
       return []
     }
 
+    isArbitraryVariant = true
+
     let fn = parseVariant(selector)
 
     let sort = Array.from(context.variantOrder.values()).pop() << 1n
@@ -300,6 +303,7 @@ function applyVariant(variant, matches, context) {
             ...meta,
             sort: variantSort | meta.sort,
             collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
+            isArbitraryVariant,
           },
           clone.nodes[0],
         ]
@@ -627,6 +631,7 @@ function* resolveMatches(candidate, context, original = candidate) {
             base: candidate
               .split(new RegExp(`\\${context?.tailwindConfig?.separator ?? ':'}(?![^[]*\\])`))
               .pop(),
+            isArbitraryVariant: match[0].isArbitraryVariant,
 
             context,
           })
diff --git a/src/util/formatVariantSelector.js b/src/util/formatVariantSelector.js
--- a/src/util/formatVariantSelector.js
+++ b/src/util/formatVariantSelector.js
@@ -35,6 +35,7 @@ export function finalizeSelector(
     selector,
     candidate,
     context,
+    isArbitraryVariant,
 
     // Split by the separator, but ignore the separator inside square brackets:
     //
@@ -50,7 +51,8 @@ export function finalizeSelector(
 ) {
   let ast = selectorParser().astSync(selector)
 
-  if (context?.tailwindConfig?.prefix) {
+  // We explicitly DO NOT prefix classes in arbitrary variants
+  if (context?.tailwindConfig?.prefix && !isArbitraryVariant) {
     format = prefixSelector(context.tailwindConfig.prefix, format)
   }
 


Test patch:
diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js
--- a/tests/arbitrary-variants.test.js
+++ b/tests/arbitrary-variants.test.js
@@ -527,3 +527,42 @@ test('allows attribute variants with quotes', () => {
     `)
   })
 })
+
+test('classes in arbitrary variants should not be prefixed', () => {
+  let config = {
+    prefix: 'tw-',
+    content: [
+      {
+        raw: `
+          <div class="[.foo_&]:tw-text-red-400">should not be red</div>
+          <div class="foo">
+            <div class="[.foo_&]:tw-text-red-400">should be red</div>
+          </div>
+          <div class="[&_.foo]:tw-text-red-400">
+            <div>should not be red</div>
+            <div class="foo">should be red</div>
+          </div>
+        `,
+      },
+    ],
+    corePlugins: { preflight: false },
+  }
+
+  let input = `
+    @tailwind utilities;
+  `
+
+  return run(input, config).then((result) => {
+    expect(result.css).toMatchFormattedCss(css`
+      .foo .\[\.foo_\&\]\:tw-text-red-400 {
+        --tw-text-opacity: 1;
+        color: rgb(248 113 113 / var(--tw-text-opacity));
+      }
+
+      .\[\&_\.foo\]\:tw-text-red-400 .foo {
+        --tw-text-opacity: 1;
+        color: rgb(248 113 113 / var(--tw-text-opacity));
+      }
+    `)
+  })
+})