Repository: tailwindlabs/tailwindcss (TypeScript)

Issue:
Using custom variants in utilities with important feature
**What version of Tailwind CSS are you using?**

3.0.24

**What build tool (or framework if it abstracts the build tool) are you using?**

postcss-cli 9.1.0

**What version of Node.js are you using?**

16


**Reproduction URL**

https://play.tailwindcss.com/WxwCNZmQFX

**Describe your issue**

I want to setup custom variant for the webpage if it is loaded inside the iframe. 

When using this custom variant together with ` important: '#csc-app'` feature it produces different selectors if the classes are defined in the html or in the utilities.

If the variant is used in the html then `.usage-variant_iframe` selector comes after the important selector:
```
#csc-app .usage-variant_iframe .iframe\:text-sm
```

If the variant is used in the utility then `.usage-variant_iframe` selector comes before the important selector:
```
.usage-variant_iframe #csc-app .custom-utility
```

As a work around i'm adding the `.usage-variant_iframe` twice right now. Once before the `#csc-app` div and second time after, but it would be great if this can be consistent.





Hints:
Hey! What do you mean by “if the variant is used in the HTML” and “if the variant is used in the utility”? Can you give examples of each so I understand for sure?
I should've been more clear 😄 

By html i mean using variant like so `<p class="text-2xl iframe:text-sm">`. I define standard text size, and then override it for the iframe variant.

By utility i mean first defining your custom utility so:
```
@layer utilities {
  .custom-utility {
    @apply text-red-900 iframe:text-blue-900;
  }
}
```

And then using it like this `<p class="custom-utility">`


It is also in the playground https://play.tailwindcss.com/WxwCNZmQFX

If you take a look at the generated css for the example above then we get:
```
.usage-variant_iframe #csc-app .custom-utility {
  --tw-text-opacity: 1;
  color: rgb(30 58 138 / var(--tw-text-opacity));
}

#csc-app .usage-variant_iframe .iframe\:text-sm {
  font-size: 0.875rem;
  line-height: 1.25rem;
}
```




Patch:
diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js
--- a/src/lib/expandApplyAtRules.js
+++ b/src/lib/expandApplyAtRules.js
@@ -471,7 +471,27 @@ function processApply(root, context, localCache) {
               return
             }
 
-            rule.selector = replaceSelector(parent.selector, rule.selector, applyCandidate)
+            // Strip the important selector from the parent selector if at the beginning
+            let importantSelector =
+              typeof context.tailwindConfig.important === 'string'
+                ? context.tailwindConfig.important
+                : null
+
+            // We only want to move the "important" selector if this is a Tailwind-generated utility
+            // We do *not* want to do this for user CSS that happens to be structured the same
+            let isGenerated = parent.raws.tailwind !== undefined
+
+            let parentSelector =
+              isGenerated && importantSelector && parent.selector.indexOf(importantSelector) === 0
+                ? parent.selector.slice(importantSelector.length)
+                : parent.selector
+
+            rule.selector = replaceSelector(parentSelector, rule.selector, applyCandidate)
+
+            // And then re-add it if it was removed
+            if (importantSelector && parentSelector !== parent.selector) {
+              rule.selector = `${importantSelector} ${rule.selector}`
+            }
 
             rule.walkDecls((d) => {
               d.important = meta.important || important


Test patch:
diff --git a/tests/apply.test.js b/tests/apply.test.js
--- a/tests/apply.test.js
+++ b/tests/apply.test.js
@@ -1467,3 +1467,84 @@ it('should apply using the updated user CSS when the source has changed', async
     }
   `)
 })
+
+it('apply + layer utilities + selector variants (like group) + important selector', async () => {
+  let config = {
+    important: '#myselector',
+    content: [{ raw: html`<div class="custom-utility"></div>` }],
+    plugins: [],
+  }
+
+  let input = css`
+    @tailwind utilities;
+    @layer utilities {
+      .custom-utility {
+        @apply font-normal group-hover:underline;
+      }
+    }
+  `
+
+  let result = await run(input, config)
+
+  expect(result.css).toMatchFormattedCss(css`
+    #myselector .custom-utility {
+      font-weight: 400;
+    }
+
+    #myselector .group:hover .custom-utility {
+      text-decoration-line: underline;
+    }
+  `)
+})
+
+it('apply + user CSS + selector variants (like group) + important selector (1)', async () => {
+  let config = {
+    important: '#myselector',
+    content: [{ raw: html`<div class="custom-utility"></div>` }],
+    plugins: [],
+  }
+
+  let input = css`
+    .custom-utility {
+      @apply font-normal group-hover:underline;
+    }
+  `
+
+  let result = await run(input, config)
+
+  expect(result.css).toMatchFormattedCss(css`
+    .custom-utility {
+      font-weight: 400;
+    }
+
+    .group:hover .custom-utility {
+      text-decoration-line: underline;
+    }
+  `)
+})
+
+it('apply + user CSS + selector variants (like group) + important selector (2)', async () => {
+  let config = {
+    important: '#myselector',
+    content: [{ raw: html`<div class="custom-utility"></div>` }],
+    plugins: [],
+  }
+
+  let input = css`
+    #myselector .custom-utility {
+      @apply font-normal group-hover:underline;
+    }
+  `
+
+  let result = await run(input, config)
+
+  expect(result.css).toMatchFormattedCss(css`
+    #myselector .custom-utility {
+      font-weight: 400;
+    }
+
+    .group:hover #myselector .custom-utility {
+      text-decoration-line: underline;
+    }
+  `)
+})