Repository: tailwindlabs/tailwindcss

Problem statement:
PurgeCSS remove first components class when using whitelist comment
Hi tailwindlabs team,

I noticed the following point with the building PurgeCSS.

If I import a CSS file before `@tailwind components;` and use PurgeCSS whitelist comment: `.container` classes are remove but not responsive container classes. 

My Sass file
```
@tailwind base;

/* purgecss start ignore */
@import "button";
/* purgecss end ignore */

@tailwind components;

@tailwind utilities;
```

I make different test using exclamation mark in comment, ... same result

I use last tailwindcss 1.7.5, sass, csso.

My `tailwind.config.js`
```
module.exports = {
  purge: [
    'path/to/templates',
  ],
  future: {
    removeDeprecatedGapUtilities: true,
  },
  experimental: {
    applyComplexClasses: true,
    uniformColorPalette: true,
    extendedSpacingScale: true,
    defaultLineHeights: true,
    extendedFontSizeScale: true,
  },
}
```


Hints:
I can use `mode: all` + PurgeCSS comment as a work around.
Can you create a simple project that reproduces the issue? We got a lot of issues that require me to manually recreate someone's project from scratch and I unfortunately just don't have time for that so things end up going unfixed. If you can provide something I can clone and start troubleshooting with right away it's much easier for me to find time to diagnose and fix the issue.
Was able to reproduce this but realizing it sort of doesn't make sense to configure things this way. Using your config, your custom button styles are already protected from purging, because only the `@tailwind utilities` block gets purged, so the comments are redundant.

I agree this shouldn't break the way it is and we should handle it more gracefully or fail with a clear error, but in the mean time just delete those comments and you're good 👍 

Patch:
diff --git a/src/lib/purgeUnusedStyles.js b/src/lib/purgeUnusedStyles.js
--- a/src/lib/purgeUnusedStyles.js
+++ b/src/lib/purgeUnusedStyles.js
@@ -77,10 +77,18 @@ export default function purgeUnusedUtilities(config, configChanged) {
           ? ['utilities']
           : _.get(config, 'purge.layers', ['base', 'components', 'utilities'])
 
-      css.prepend(postcss.comment({ text: 'purgecss start ignore' }))
-      css.append(postcss.comment({ text: 'purgecss end ignore' }))
-
       css.walkComments(comment => {
+        switch (comment.text.trim()) {
+          case `purgecss start ignore`:
+            comment.before(postcss.comment({ text: 'purgecss end ignore' }))
+            break
+          case `purgecss end ignore`:
+            comment.before(postcss.comment({ text: 'purgecss end ignore' }))
+            comment.text = 'purgecss start ignore'
+            break
+          default:
+            break
+        }
         layers.forEach(layer => {
           switch (comment.text.trim()) {
             case `tailwind start ${layer}`:
@@ -94,6 +102,9 @@ export default function purgeUnusedUtilities(config, configChanged) {
           }
         })
       })
+
+      css.prepend(postcss.comment({ text: 'purgecss start ignore' }))
+      css.append(postcss.comment({ text: 'purgecss end ignore' }))
     },
     removeTailwindMarkers,
     purgecss({


Test patch:
diff --git a/__tests__/purgeUnusedStyles.test.js b/__tests__/purgeUnusedStyles.test.js
--- a/__tests__/purgeUnusedStyles.test.js
+++ b/__tests__/purgeUnusedStyles.test.js
@@ -195,19 +195,19 @@ test('custom css in a layer is purged by default when using layers mode', () =>
       ])
         .process(
           `
-        @tailwind base;
+          @tailwind base;
 
-        @tailwind components;
+          @tailwind components;
 
-        @layer components {
-          .example {
-            @apply font-bold;
-            color: theme('colors.red.500');
+          @layer components {
+            .example {
+              @apply font-bold;
+              color: theme('colors.red.500');
+            }
           }
-        }
 
-        @tailwind utilities;
-      `,
+          @tailwind utilities;
+        `,
           { from: null }
         )
         .then(result => {
@@ -411,6 +411,43 @@ test('does not purge components when mode is conservative', () => {
   )
 })
 
+test('extra purgecss control comments can be added manually', () => {
+  return inProduction(
+    suppressConsoleLogs(() => {
+      const input = `
+        @tailwind base;
+
+        /* purgecss start ignore */
+        .btn {
+          background: red;
+        }
+        /* purgecss end ignore */
+
+        @tailwind components;
+        @tailwind utilities;
+      `
+
+      return postcss([
+        tailwind({
+          ...config,
+          purge: {
+            layers: ['utilities'],
+            content: [path.resolve(`${__dirname}/fixtures/**/*.html`)],
+          },
+        }),
+      ])
+        .process(input, { from: null })
+        .then(result => {
+          const rules = extractRules(result.root)
+
+          expect(rules).toContain('.btn')
+          expect(rules).toContain('.container')
+          assertPurged(result)
+        })
+    })
+  )
+})
+
 test(
   'does not purge except in production',
   suppressConsoleLogs(() => {