Repository: tailwindlabs/tailwindcss

Problem statement:
Unexpected !important added when rules have multiple selectors
<!-- Please provide all of the information requested below. We're a small team and without all of this information it's not possible for us to help and your bug report will be closed. -->

**What version of Tailwind CSS are you using?**

v3.2.1

**What build tool (or framework if it abstracts the build tool) are you using?**

webpack 5.74.0

**What version of Node.js are you using?**

v16.18.0

**What browser are you using?**

Firefox

**What operating system are you using?**

macOS

**Reproduction URL**

https://play.tailwindcss.com/VMLRCIqg77?file=config

**Describe your issue**

When adding rules with multiple selectors in a plugin, the generated CSS for the `!important` modifier can cause issues. The `!important` rule includes all selectors from the original rule, even those which don't include the modified class.

For example, when adding a utility or component with these rules

```
'.btn': {
     '&.disabled, &:disabled': {
         color: 'gray',
     }
}
``` 

the following CSS is generated

```
.btn.disabled, .btn:disabled {
  color: gray;
}

.btn.\!disabled, .btn:disabled {
  color: gray !important;
}
```

but I think the following makes more sense

```
.btn.disabled, .btn:disabled {
  color: gray;
}

.btn.\!disabled {
  color: gray !important;
}
```

The workaround in this case is straightforward, just splitting the rule in two. 

```
'.btn': {
     '&.disabled': {
         color: 'gray',
     },
    '&:disabled': {
         color: 'gray',
     }
}
``` 



Hints:

Patch:
diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js
--- a/src/lib/generateRules.js
+++ b/src/lib/generateRules.js
@@ -3,7 +3,7 @@ import selectorParser from 'postcss-selector-parser'
 import parseObjectStyles from '../util/parseObjectStyles'
 import isPlainObject from '../util/isPlainObject'
 import prefixSelector from '../util/prefixSelector'
-import { updateAllClasses, getMatchingTypes } from '../util/pluginUtils'
+import { updateAllClasses, filterSelectorsForClass, getMatchingTypes } from '../util/pluginUtils'
 import log from '../util/log'
 import * as sharedState from './sharedState'
 import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector'
@@ -116,12 +116,15 @@ function applyImportant(matches, classCandidate) {
   for (let [meta, rule] of matches) {
     let container = postcss.root({ nodes: [rule.clone()] })
     container.walkRules((r) => {
-      r.selector = updateAllClasses(r.selector, (className) => {
-        if (className === classCandidate) {
-          return `!${className}`
+      r.selector = updateAllClasses(
+        filterSelectorsForClass(r.selector, classCandidate),
+        (className) => {
+          if (className === classCandidate) {
+            return `!${className}`
+          }
+          return className
         }
-        return className
-      })
+      )
       r.walkDecls((d) => (d.important = true))
     })
     result.push([{ ...meta, important: true }, container.nodes[0]])
diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js
--- a/src/util/pluginUtils.js
+++ b/src/util/pluginUtils.js
@@ -37,6 +37,23 @@ export function updateAllClasses(selectors, updateClass) {
   return result
 }
 
+export function filterSelectorsForClass(selectors, classCandidate) {
+  let parser = selectorParser((selectors) => {
+    selectors.each((sel) => {
+      const containsClass = sel.nodes.some(
+        (node) => node.type === 'class' && node.value === classCandidate
+      )
+      if (!containsClass) {
+        sel.remove()
+      }
+    })
+  })
+
+  let result = parser.processSync(selectors)
+
+  return result
+}
+
 function resolveArbitraryValue(modifier, validate) {
   if (!isArbitraryValue(modifier)) {
     return undefined


Test patch:
diff --git a/tests/important-modifier.test.js b/tests/important-modifier.test.js
--- a/tests/important-modifier.test.js
+++ b/tests/important-modifier.test.js
@@ -13,12 +13,13 @@ test('important modifier', () => {
           <div class="lg:!opacity-50"></div>
           <div class="xl:focus:disabled:!float-right"></div>
           <div class="!custom-parent-5"></div>
+          <div class="btn !disabled"></div>
         `,
       },
     ],
     corePlugins: { preflight: false },
     plugins: [
-      function ({ theme, matchUtilities }) {
+      function ({ theme, matchUtilities, addComponents }) {
         matchUtilities(
           {
             'custom-parent': (value) => {
@@ -31,6 +32,13 @@ test('important modifier', () => {
           },
           { values: theme('spacing') }
         )
+        addComponents({
+          '.btn': {
+            '&.disabled, &:disabled': {
+              color: 'gray',
+            },
+          },
+        })
       },
     ],
   }
@@ -70,6 +78,13 @@ test('important modifier', () => {
           max-width: 1536px !important;
         }
       }
+      .btn.disabled,
+      .btn:disabled {
+        color: gray;
+      }
+      .btn.\!disabled {
+        color: gray !important;
+      }
       .\!font-bold {
         font-weight: 700 !important;
       }