Repository: tailwindlabs/tailwindcss

Problem statement:
Combination of TW JS based config and a class causes malformed CSS
**What version of Tailwind CSS are you using?**

v3.3.3

**What build tool (or framework if it abstracts the build tool) are you using?**

Vite v4.4.9

**What version of Node.js are you using?**

v20.7.0

**What browser are you using?**

Any

**What operating system are you using?**

macOS 14.0

**Reproduction URL**

https://play.tailwindcss.com/jZXL8Axggn

**Describe your issue**

 What I have is this:

**A Tailwind config file containing the following:**
```js
'@media screen(md)': {
  '.outer-grid': {
    rowGap: theme('spacing.16'),
    paddingTop: theme('spacing.16'),
    paddingBottom: theme('spacing.16'),
  },
  '.outer-grid>*:last-child:is(.w-full)': {
    marginBottom: `-${theme('spacing.16')}`,
  },
},
```

**A class in my templates**
 `[&>*]:after:w-full`

The combination of this JS config and this class trips up something and results in compilation warings and the following CSS in my compiled file:

```css
@media (min-width: 768px) {
        {
        content: var(--tw-content);
        row-gap: 4rem;
        padding-top: 4rem;
        padding-bottom: 4rem
    }

    .outer-grid>*:last-child:is(.\[\&\>\*\]\:before\:w-full>*):before {
        content: var(--tw-content);
        margin-bottom: -4rem
    }
}
```

I noticed this because of the error `npm run build` produces:

```
warnings when minifying css:
▲ [WARNING] Unexpected "{" [css-syntax-error]

    <stdin>:2610:3:
      2610 │    {
```

And I can even see in VS code with the TW extension the malformed CSS when I hover over this one class:
<img width="893" alt="Screenshot_2023-09-28_at_17 09 03" src="https://github.com/tailwindlabs/tailwindcss/assets/69107412/a8730642-f74d-43e7-8439-0c0903778464">

When I either:

- Remove the class from my templates, or
- Remove the ` '.outer-grid>*:last-child:is(.w-full)'` statement from the `@media sceen(md)` query,

the warning and malformed CSS is gone. Note that the ` '.outer-grid>*:last-child:is(.w-full)'` statement _not_ in a media query compiles without issues.

A workaround is to use an attribute selector instead `[class~="w-full"]`. https://play.tailwindcss.com/i6OoVDdQbc?file=config


Hints:

Patch:
diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js
--- a/src/lib/generateRules.js
+++ b/src/lib/generateRules.js
@@ -815,10 +815,19 @@ function applyFinalFormat(match, { context, candidate }) {
     }
 
     try {
-      rule.selector = finalizeSelector(rule.selector, finalFormat, {
+      let selector = finalizeSelector(rule.selector, finalFormat, {
         candidate,
         context,
       })
+
+      // Finalize Selector determined that this candidate is irrelevant
+      // TODO: This elimination should happen earlier so this never happens
+      if (selector === null) {
+        rule.remove()
+        return
+      }
+
+      rule.selector = selector
     } catch {
       // If this selector is invalid we also want to skip it
       // But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
diff --git a/src/util/formatVariantSelector.js b/src/util/formatVariantSelector.js
--- a/src/util/formatVariantSelector.js
+++ b/src/util/formatVariantSelector.js
@@ -186,6 +186,13 @@ export function finalizeSelector(current, formats, { context, candidate, base })
   // Remove extraneous selectors that do not include the base candidate
   selector.each((sel) => eliminateIrrelevantSelectors(sel, base))
 
+  // If ffter eliminating irrelevant selectors, we end up with nothing
+  // Then the whole "rule" this is associated with does not need to exist
+  // We use `null` as a marker value for that case
+  if (selector.length === 0) {
+    return null
+  }
+
   // If there are no formats that means there were no variants added to the candidate
   // so we can just return the selector as-is
   let formatAst = Array.isArray(formats)


Test patch:
diff --git a/tests/basic-usage.test.js b/tests/basic-usage.test.js
--- a/tests/basic-usage.test.js
+++ b/tests/basic-usage.test.js
@@ -792,3 +792,46 @@ test('Skips classes inside :not() when nested inside an at-rule', async () => {
 
   expect(result.css).toMatchFormattedCss(css``)
 })
+
+test('Irrelevant rules are removed when applying variants', async () => {
+  let config = {
+    content: [
+      {
+        raw: html` <div class="md:w-full"></div> `,
+      },
+    ],
+    corePlugins: { preflight: false },
+    plugins: [
+      function ({ addUtilities }) {
+        addUtilities({
+          '@supports (foo: bar)': {
+            // This doesn't contain `w-full` so it should not exist in the output
+            '.outer': { color: 'red' },
+            '.outer:is(.w-full)': { color: 'green' },
+          },
+        })
+      },
+    ],
+  }
+
+  let input = css`
+    @tailwind utilities;
+  `
+
+  // We didn't find the hand class therefore
+  // nothing should be generated
+  let result = await run(input, config)
+
+  expect(result.css).toMatchFormattedCss(css`
+    @media (min-width: 768px) {
+      .md\:w-full {
+        width: 100%;
+      }
+      @supports (foo: bar) {
+        .outer.md\:w-full {
+          color: green;
+        }
+      }
+    }
+  `)
+})