Repository: tailwindlabs/tailwindcss
Pull request #5489 (2021-09-13 13:19:15)

Warn when nesting is detected
When we detect that you have nested css, then we warn with a link to the docs to setup nesting.


Patch:
diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml
deleted file mode 100644
index 769d1649..00000000
--- a/.github/ISSUE_TEMPLATE/1.bug_report.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-name: Bug Report
-description: Create a bug report for Tailwind CSS.
-labels: []
-body:
-  - type: input
-    attributes:
-      label: What version of Tailwind CSS are you using?
-      description: 'For example: v2.0.4'
-    validations:
-      required: true
-  - type: input
-    attributes:
-      label: What build tool (or framework if it abstracts the build tool) are you using?
-      description: 'For example: postcss-cli 8.3.1, Next.js 10.0.9, webpack 5.28.0'
-    validations:
-      required: true  
-  - type: input
-    attributes:
-      label: What version of Node.js are you using?
-      description: 'For example: v12.0.0'
-    validations:
-      required: true
-  - type: input
-    attributes:
-      label: What browser are you using?
-      description: 'For example: Chrome, Safari, or N/A'
-    validations:
-      required: true
-  - type: input
-    attributes:
-      label: What operating system are you using?
-      description: 'For example: macOS, Windows'
-    validations:
-      required: true
-  - type: input
-    attributes:
-      label: Reproduction repository
-      description: A public GitHub repo that includes a minimal reproduction of the bug. **Please do not link to your actual project**, what we need instead is a _minimal_ reproduction in a fresh project without any unnecessary code. This means it doesn't matter if your real project is private/confidential, since we want a link to a separate, isolated reproduction anyways. Unfortunately we can't provide support without a reproduction, and your issue will be closed and locked with no comment if this is not provided.
-    validations:
-      required: true
-  - type: textarea
-    attributes:
-      label: Describe your issue
-      description: Describe the problem you're seeing, any important steps to reproduce and what behavior you expect instead
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 93e5f82d..3e0df6d6 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,14 +1,14 @@
 blank_issues_enabled: false
 contact_links:
+  - name: Get Help
+    url: https://github.com/tailwindlabs/tailwindcss/discussions/new?category=help
+    about: If you can't get something to work the way you expect, open a question in our discussion forums.
   - name: Feature Request
     url: https://github.com/tailwindlabs/tailwindcss/discussions/new?category=ideas
     about: 'Suggest any ideas you have using our discussion forums.'
-  - name: Help
-    url: https://github.com/tailwindlabs/tailwindcss/discussions/new?category=help
-    about: 'If you have a question or need help, ask a question on the discussion forums.'
+  - name: Bug Report
+    url: https://github.com/tailwindlabs/tailwindcss/issues/new?body=%3C!--%20Please%20provide%20all%20of%20the%20information%20requested%20below.%20We%27re%20a%20small%20team%20and%20without%20all%20of%20this%20information%20it%27s%20not%20possible%20for%20us%20to%20help%20and%20your%20bug%20report%20will%20be%20closed.%20--%3E%0A%0A**What%20version%20of%20Tailwind%20CSS%20are%20you%20using%3F**%0A%0AFor%20example%3A%20v2.0.4%0A%0A**What%20build%20tool%20(or%20framework%20if%20it%20abstracts%20the%20build%20tool)%20are%20you%20using%3F**%0A%0AFor%20example%3A%20postcss-cli%208.3.1%2C%20Next.js%2010.0.9%2C%20webpack%205.28.0%0A%0A**What%20version%20of%20Node.js%20are%20you%20using%3F**%0A%0AFor%20example%3A%20v12.0.0%0A%0A**What%20browser%20are%20you%20using%3F**%0A%0AFor%20example%3A%20Chrome%2C%20Safari%2C%20or%20N%2FA%0A%0A**What%20operating%20system%20are%20you%20using%3F**%0A%0AFor%20example%3A%20macOS%2C%20Windows%0A%0A**Reproduction%20URL**%0A%0AA%20Tailwind%20Play%20link%20or%20public%20GitHub%20repo%20that%20includes%20a%20minimal%20reproduction%20of%20the%20bug.%20**Please%20do%20not%20link%20to%20your%20actual%20project**%2C%20what%20we%20need%20instead%20is%20a%20_minimal_%20reproduction%20in%20a%20fresh%20project%20without%20any%20unnecessary%20code.%20This%20means%20it%20doesn%27t%20matter%20if%20your%20real%20project%20is%20private%2Fconfidential%2C%20since%20we%20want%20a%20link%20to%20a%20separate%2C%20isolated%20reproduction%20anyways.%20Unfortunately%20we%20can%27t%20provide%20support%20without%20a%20reproduction%2C%20and%20your%20issue%20will%20be%20closed%20with%20no%20comment%20if%20this%20is%20not%20provided.%0A%0A**Describe%20your%20issue**%0A%0ADescribe%20the%20problem%20you%27re%20seeing%2C%20any%20important%20steps%20to%20reproduce%20and%20what%20behavior%20you%20expect%20instead.
+    about: If you've already asked for help with a problem and confirmed something is broken with Tailwind CSS itself, create a bug report.
   - name: Documentation Issue
     url: https://github.com/tailwindlabs/tailwindcss.com
     about: 'For documentation issues, suggest changes on our documentation repository.'
-  - name: Kind Words
-    url: https://github.com/tailwindlabs/tailwindcss/discussions/new?category=kind-words
-    about: "Have something nice to say about Tailwind? We'd love to hear it!"
diff --git a/README.md b/README.md
index 3c7584ed..4d4ed895 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 <p>
     <a href="https://tailwindcss.com/" target="_blank">
-      <img alt="Tailwind CSS" width="350" src="https://refactoringui.nyc3.cdn.digitaloceanspaces.com/tailwind-logo.svg">
+      <img alt="Tailwind CSS" width="350" src="https://refactoringui.nyc3.cdn.digitaloceanspaces.com/tailwind-logo-sticker.svg">
     </a><br>
     A utility-first CSS framework for rapidly building custom user interfaces.
 </p>
diff --git a/integrations/execute.js b/integrations/execute.js
index fccabcdd..bab80b4b 100644
--- a/integrations/execute.js
+++ b/integrations/execute.js
@@ -8,6 +8,15 @@ afterEach(() => {
   runningProcessess.splice(0).forEach((runningProcess) => runningProcess.stop())
 })
 
+function debounce(fn, ms) {
+  let state = { timer: undefined }
+
+  return (...args) => {
+    if (state.timer) clearTimeout(state.timer)
+    state.timer = setTimeout(() => fn(...args), ms)
+  }
+}
+
 module.exports = function $(command, options = {}) {
   let abortController = new AbortController()
   let cwd = resolveToolRoot()
@@ -37,13 +46,13 @@ module.exports = function $(command, options = {}) {
     }
   }
 
-  function notifyNextStdoutActor() {
+  let notifyNextStdoutActor = debounce(() => {
     return notifyNext(stdoutActors, stdoutMessages)
-  }
+  }, 200)
 
-  function notifyNextStderrActor() {
+  let notifyNextStderrActor = debounce(() => {
     return notifyNext(stderrActors, stderrMessages)
-  }
+  }, 200)
 
   let runningProcess = new Promise((resolve, reject) => {
     let child = spawn(command, args, {
@@ -74,6 +83,12 @@ module.exports = function $(command, options = {}) {
       combined += data
     })
 
+    child.on('error', (err) => {
+      if (err.name !== 'AbortError') {
+        throw err
+      }
+    })
+
     child.on('close', (code, signal) => {
       ;(signal === 'SIGTERM' ? resolve : code === 0 ? resolve : reject)({
         code,
diff --git a/integrations/io.js b/integrations/io.js
index f1a406ec..38377683 100644
--- a/integrations/io.js
+++ b/integrations/io.js
@@ -6,6 +6,17 @@ let chokidar = require('chokidar')
 
 let resolveToolRoot = require('./resolve-tool-root')
 
+function getWatcherOptions() {
+  return {
+    usePolling: true,
+    interval: 200,
+    awaitWriteFinish: {
+      stabilityThreshold: 1500,
+      pollInterval: 50,
+    },
+  }
+}
+
 module.exports = function ({
   /** Output directory, relative to the tool. */
   output = 'dist',
@@ -95,6 +106,10 @@ module.exports = function ({
       file = await resolveFile(file, absoluteOutputFolder)
       return fs.readFile(path.resolve(absoluteOutputFolder, file), 'utf8')
     },
+    async readInputFile(file) {
+      file = await resolveFile(file, absoluteInputFolder)
+      return fs.readFile(path.resolve(absoluteInputFolder, file), 'utf8')
+    },
     async appendToInputFile(file, contents) {
       let filePath = path.resolve(absoluteInputFolder, file)
       if (!fileCache[filePath]) {
@@ -122,48 +137,41 @@ module.exports = function ({
     async waitForOutputFileCreation(file) {
       if (file instanceof RegExp) {
         let r = file
-        let watcher = chokidar.watch(absoluteOutputFolder)
+        let watcher = chokidar.watch(absoluteOutputFolder, getWatcherOptions())
 
         return new Promise((resolve) => {
           watcher.on('add', (file) => {
             if (r.test(file)) {
-              watcher.close()
-              resolve()
+              watcher.close().then(() => resolve())
             }
           })
         })
       } else {
         let filePath = path.resolve(absoluteOutputFolder, file)
-        let watcher = chokidar.watch(filePath)
 
-        let watcherPromise = new Promise((resolve) => {
-          watcher.once('add', () => {
-            watcher.close()
-            resolve()
+        return new Promise((resolve) => {
+          let watcher = chokidar.watch(absoluteOutputFolder, getWatcherOptions())
+
+          watcher.on('add', (addedFile) => {
+            if (addedFile !== filePath) return
+            return watcher.close().finally(resolve)
           })
         })
-
-        if (existsSync(filePath)) {
-          watcher.close()
-          return Promise.resolve()
-        }
-
-        return watcherPromise
       }
     },
     async waitForOutputFileChange(file, cb = () => {}) {
       file = await resolveFile(file, absoluteOutputFolder)
-
       let filePath = path.resolve(absoluteOutputFolder, file)
-      let watcher = chokidar.watch(filePath)
 
       return new Promise((resolve) => {
-        let chain = Promise.resolve()
-        watcher.once('change', () => {
-          watcher.close()
-          chain.then(() => resolve())
-        })
-        chain.then(() => cb())
+        let watcher = chokidar.watch(absoluteOutputFolder, getWatcherOptions())
+
+        watcher
+          .on('change', (changedFile) => {
+            if (changedFile !== filePath) return
+            return watcher.close().finally(resolve)
+          })
+          .on('ready', cb)
       })
     },
   }
diff --git a/integrations/package-lock.json b/integrations/package-lock.json
index 2534f299..6824c194 100644
--- a/integrations/package-lock.json
+++ b/integrations/package-lock.json
@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "integrations",
       "version": "0.0.0",
       "devDependencies": {
         "isomorphic-fetch": "^3.0.0",
diff --git a/integrations/package.json b/integrations/package.json
index ba070a6d..20eed5b0 100644
--- a/integrations/package.json
+++ b/integrations/package.json
@@ -3,7 +3,7 @@
   "private": true,
   "version": "0.0.0",
   "scripts": {
-    "test": "jest --runInBand"
+    "test": "jest --runInBand --forceExit"
   },
   "jest": {
     "testTimeout": 30000,
diff --git a/integrations/parcel/package-lock.json b/integrations/parcel/package-lock.json
index ef7f647e..e49a1892 100644
--- a/integrations/parcel/package-lock.json
+++ b/integrations/parcel/package-lock.json
@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "parcel",
       "version": "0.0.0",
       "devDependencies": {
         "parcel": "nightly"
diff --git a/integrations/parcel/package.json b/integrations/parcel/package.json
index 1bd16340..e4e88bd5 100644
--- a/integrations/parcel/package.json
+++ b/integrations/parcel/package.json
@@ -5,9 +5,10 @@
   "scripts": {
     "build": "parcel build ./src/index.html --no-cache",
     "dev": "parcel watch ./src/index.html --no-cache",
-    "test": "jest --runInBand"
+    "test": "jest --runInBand --forceExit"
   },
   "jest": {
+    "testTimeout": 10000,
     "displayName": "parcel",
     "setupFilesAfterEnv": [
       "<rootDir>/../../jest/customMatchers.js"
diff --git a/integrations/postcss-cli/package-lock.json b/integrations/postcss-cli/package-lock.json
index 038384ae..27fafd5c 100644
--- a/integrations/postcss-cli/package-lock.json
+++ b/integrations/postcss-cli/package-lock.json
@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "postcss-cli",
       "version": "0.0.0",
       "devDependencies": {
         "postcss": "^8.2.15",
diff --git a/integrations/postcss-cli/package.json b/integrations/postcss-cli/package.json
index bed103c0..6ad9adf0 100644
--- a/integrations/postcss-cli/package.json
+++ b/integrations/postcss-cli/package.json
@@ -3,10 +3,11 @@
   "private": true,
   "version": "0.0.0",
   "scripts": {
-    "build": "NODE_ENV=production postcss ./src/index.css -o ./dist/main.css",
-    "test": "jest --runInBand"
+    "build": "NODE_ENV=production postcss ./src/index.css -o ./dist/main.css --verbose",
+    "test": "jest --runInBand --forceExit"
   },
   "jest": {
+    "testTimeout": 10000,
     "displayName": "PostCSS CLI",
     "setupFilesAfterEnv": [
       "<rootDir>/../../jest/customMatchers.js"
diff --git a/integrations/rollup/package.json b/integrations/rollup/package.json
index 9cfbcd1f..7234f08b 100644
--- a/integrations/rollup/package.json
+++ b/integrations/rollup/package.json
@@ -4,13 +4,12 @@
   "version": "0.0.0",
   "scripts": {
     "build": "rollup -c",
-    "test": "jest --runInBand"
+    "test": "jest --runInBand --forceExit"
   },
   "jest": {
+    "testTimeout": 10000,
     "displayName": "rollup.js",
-    "setupFilesAfterEnv": [
-      "<rootDir>/../../jest/customMatchers.js"
-    ]
+    "setupFilesAfterEnv": ["<rootDir>/../../jest/customMatchers.js"]
   },
   "devDependencies": {
     "rollup": "^2.48.0",
diff --git a/integrations/tailwindcss-cli/package-lock.json b/integrations/tailwindcss-cli/package-lock.json
index 7b7a3814..c121da5a 100644
--- a/integrations/tailwindcss-cli/package-lock.json
+++ b/integrations/tailwindcss-cli/package-lock.json
@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "tailwindcss-cli",
       "version": "0.0.0"
     }
   }
diff --git a/integrations/tailwindcss-cli/package.json b/integrations/tailwindcss-cli/package.json
index 882e30b4..0ed3dc1f 100644
--- a/integrations/tailwindcss-cli/package.json
+++ b/integrations/tailwindcss-cli/package.json
@@ -4,9 +4,10 @@
   "version": "0.0.0",
   "scripts": {
     "build": "NODE_ENV=production node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css",
-    "test": "jest --runInBand"
+    "test": "jest --runInBand --forceExit"
   },
   "jest": {
+    "testTimeout": 10000,
     "displayName": "Tailwind CSS CLI",
     "setupFilesAfterEnv": [
       "<rootDir>/../../jest/customMatchers.js"
diff --git a/integrations/vite/package-lock.json b/integrations/vite/package-lock.json
index dc99469e..3d1b0c24 100644
--- a/integrations/vite/package-lock.json
+++ b/integrations/vite/package-lock.json
@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "vite",
       "version": "0.0.0",
       "devDependencies": {
         "vite": "^2.3.3"
diff --git a/integrations/vite/package.json b/integrations/vite/package.json
index 9734a834..91fb4870 100644
--- a/integrations/vite/package.json
+++ b/integrations/vite/package.json
@@ -6,9 +6,10 @@
   "browser": "./src/index.js",
   "scripts": {
     "build": "vite build",
-    "test": "jest --runInBand"
+    "test": "jest --runInBand --forceExit"
   },
   "jest": {
+    "testTimeout": 10000,
     "displayName": "vite",
     "setupFilesAfterEnv": [
       "<rootDir>/../../jest/customMatchers.js"
diff --git a/integrations/webpack-4/package-lock.json b/integrations/webpack-4/package-lock.json
index 002e4190..ddb12373 100644
--- a/integrations/webpack-4/package-lock.json
+++ b/integrations/webpack-4/package-lock.json
@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "webpack-4",
       "version": "0.0.0",
       "devDependencies": {
         "css-loader": "^5.2.4",
diff --git a/integrations/webpack-4/package.json b/integrations/webpack-4/package.json
index 32d665c4..352229f4 100644
--- a/integrations/webpack-4/package.json
+++ b/integrations/webpack-4/package.json
@@ -7,9 +7,10 @@
   "scripts": {
     "build": "webpack --mode=production",
     "dev": "webpack --mode=development --watch",
-    "test": "jest --runInBand"
+    "test": "jest --runInBand --forceExit"
   },
   "jest": {
+    "testTimeout": 10000,
     "displayName": "webpack 4",
     "setupFilesAfterEnv": [
       "<rootDir>/../../jest/customMatchers.js"
diff --git a/integrations/webpack-5/package-lock.json b/integrations/webpack-5/package-lock.json
index 6960b0ee..1ef026ce 100644
--- a/integrations/webpack-5/package-lock.json
+++ b/integrations/webpack-5/package-lock.json
@@ -5,6 +5,7 @@
   "requires": true,
   "packages": {
     "": {
+      "name": "webpack-5",
       "version": "0.0.0",
       "devDependencies": {
         "css-loader": "^5.2.4",
diff --git a/integrations/webpack-5/package.json b/integrations/webpack-5/package.json
index 061aab2b..8d88bbf1 100644
--- a/integrations/webpack-5/package.json
+++ b/integrations/webpack-5/package.json
@@ -7,9 +7,10 @@
   "scripts": {
     "build": "webpack --mode=production",
     "dev": "webpack --mode=development",
-    "test": "jest"
+    "test": "jest --runInBand --forceExit"
   },
   "jest": {
+    "testTimeout": 10000,
     "displayName": "webpack 5",
     "setupFilesAfterEnv": [
       "<rootDir>/../../jest/customMatchers.js"
diff --git a/src/lib/detectNesting.js b/src/lib/detectNesting.js
new file mode 100644
index 00000000..5a3181a2
--- /dev/null
+++ b/src/lib/detectNesting.js
@@ -0,0 +1,20 @@
+export default function (_context) {
+  return (root, result) => {
+    let found = false
+
+    root.walkRules((rule) => {
+      if (found) return false
+
+      rule.walkRules((nestedRule) => {
+        found = true
+        nestedRule.warn(
+          result,
+          // TODO: Improve this warning message
+          'Nested CSS detected, checkout the docs on how to support nesting: https://tailwindcss.com/docs/using-with-preprocessors#nesting'
+        )
+
+        return false
+      })
+    })
+  }
+}
diff --git a/src/processTailwindFeatures.js b/src/processTailwindFeatures.js
index 2ac6374c..de28a894 100644
--- a/src/processTailwindFeatures.js
+++ b/src/processTailwindFeatures.js
@@ -5,6 +5,7 @@ import evaluateTailwindFunctions from './lib/evaluateTailwindFunctions'
 import substituteScreenAtRules from './lib/substituteScreenAtRules'
 import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
 import collapseAdjacentRules from './lib/collapseAdjacentRules'
+import detectNesting from './lib/detectNesting'
 import { createContext } from './lib/setupContextUtils'
 
 export default function processTailwindFeatures(setupContext) {
@@ -31,6 +32,7 @@ export default function processTailwindFeatures(setupContext) {
       )
     }
 
+    detectNesting(context)(root, result)
     expandTailwindAtRules(context)(root, result)
     expandApplyAtRules(context)(root, result)
     evaluateTailwindFunctions(context)(root, result)


Test patch:
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
new file mode 100644
index 00000000..7e314924
--- /dev/null
+++ b/.github/workflows/integration-tests.yml
@@ -0,0 +1,59 @@
+name: Integration Tests
+
+on:
+  push:
+    branches: [master]
+  pull_request:
+    branches: [master]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        integration: [parcel, postcss-cli, rollup, tailwindcss-cli, vite, webpack-4, webpack-5]
+        node-version: [16]
+
+    steps:
+      - uses: actions/checkout@v2
+      - name: Use Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v2
+        with:
+          node-version: ${{ matrix.node-version }}
+
+      - name: Use cached node_modules (tailwindcss)
+        id: cache-tailwindcss
+        uses: actions/cache@v2
+        with:
+          path: node_modules
+          key: nodeModules-${{ hashFiles('**/package-lock.json') }}-${{ matrix.node-version }}-tailwindcss
+          restore-keys: |
+            nodeModules-
+
+      - name: Install dependencies
+        if: steps.cache-tailwindcss.outputs.cache-hit != 'true'
+        run: npm install
+        env:
+          CI: true
+
+      - name: Build Tailwind CSS
+        run: npm run prepublishOnly
+
+      - name: Use cached node_modules (integrations)
+        id: cache-integrations
+        uses: actions/cache@v2
+        with:
+          path: ./integrations/**/node_modules
+          key: nodeModules-${{ hashFiles('**/package-lock.json') }}-${{ matrix.node-version }}-${{ matrix.integration }}-integrations
+          restore-keys: |
+            nodeModules-
+
+      - name: Install shared dependencies
+        if: steps.cache-integrations.outputs.cache-hit != 'true'
+        run: npm run install:integrations
+        env:
+          CI: true
+
+      - name: Test ${{ matrix.integration }}
+        run: npm test --prefix ./integrations/${{ matrix.integration }}
diff --git a/integrations/parcel/tests/integration.test.js b/integrations/parcel/tests/integration.test.js
index f61536df..cece481b 100644
--- a/integrations/parcel/tests/integration.test.js
+++ b/integrations/parcel/tests/integration.test.js
@@ -80,7 +80,7 @@ describe.skip('watcher', () => {
       css`
         .bg-red-500 {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
         }
         .font-bold {
           font-weight: 700;
@@ -240,7 +240,7 @@ describe.skip('watcher', () => {
         .btn {
           border-radius: 0.25rem;
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
           padding-left: 0.5rem;
           padding-right: 0.5rem;
           padding-top: 0.25rem;
diff --git a/integrations/postcss-cli/tests/integration.test.js b/integrations/postcss-cli/tests/integration.test.js
index 15681e3e..5f5cac96 100644
--- a/integrations/postcss-cli/tests/integration.test.js
+++ b/integrations/postcss-cli/tests/integration.test.js
@@ -1,13 +1,14 @@
 let $ = require('../../execute')
 let { css, html, javascript } = require('../../syntax')
 
-let {
-  readOutputFile,
-  appendToInputFile,
-  writeInputFile,
-  waitForOutputFileCreation,
-  waitForOutputFileChange,
-} = require('../../io')({ output: 'dist', input: 'src' })
+let { readOutputFile, appendToInputFile, writeInputFile } = require('../../io')({
+  output: 'dist',
+  input: 'src',
+})
+
+function ready(message) {
+  return message.includes('Finished')
+}
 
 describe('static build', () => {
   it('should be possible to generate tailwind output', async () => {
@@ -31,11 +32,10 @@ describe('watcher', () => {
   test('classes are generated when the html file changes', async () => {
     await writeInputFile('index.html', html`<div class="font-bold"></div>`)
 
-    let runningProcess = $('postcss ./src/index.css -o ./dist/main.css -w', {
+    let runningProcess = $('postcss ./src/index.css -o ./dist/main.css -w --verbose', {
       env: { TAILWIND_MODE: 'watch' },
     })
-
-    await waitForOutputFileCreation('main.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -45,9 +45,8 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await appendToInputFile('index.html', html`<div class="font-normal"></div>`)
-    })
+    await appendToInputFile('index.html', html`<div class="font-normal"></div>`)
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -60,15 +59,14 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
-    })
+    await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
         .bg-red-500 {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
         }
         .font-bold {
           font-weight: 700;
@@ -85,11 +83,11 @@ describe('watcher', () => {
   test('classes are generated when the tailwind.config.js file changes', async () => {
     await writeInputFile('index.html', html`<div class="font-bold md:font-medium"></div>`)
 
-    let runningProcess = $('postcss ./src/index.css -o ./dist/main.css -w', {
+    let runningProcess = $('postcss ./src/index.css -o ./dist/main.css -w --verbose', {
       env: { TAILWIND_MODE: 'watch' },
     })
 
-    await waitForOutputFileCreation('main.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -104,10 +102,9 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await writeInputFile(
-        '../tailwind.config.js',
-        javascript`
+    await writeInputFile(
+      '../tailwind.config.js',
+      javascript`
           module.exports = {
             content: ['./src/index.html'],
             theme: {
@@ -126,8 +123,8 @@ describe('watcher', () => {
             plugins: [],
           }
       `
-      )
-    })
+    )
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -148,11 +145,11 @@ describe('watcher', () => {
   test('classes are generated when the index.css file changes', async () => {
     await writeInputFile('index.html', html`<div class="font-bold btn"></div>`)
 
-    let runningProcess = $('postcss ./src/index.css -o ./dist/main.css -w', {
+    let runningProcess = $('postcss ./src/index.css -o ./dist/main.css -w --verbose', {
       env: { TAILWIND_MODE: 'watch' },
     })
 
-    await waitForOutputFileCreation('main.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -162,22 +159,21 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await writeInputFile(
-        'index.css',
-        css`
-          @tailwind base;
-          @tailwind components;
-          @tailwind utilities;
-
-          @layer components {
-            .btn {
-              @apply px-2 py-1 rounded;
-            }
+    await writeInputFile(
+      'index.css',
+      css`
+        @tailwind base;
+        @tailwind components;
+        @tailwind utilities;
+
+        @layer components {
+          .btn {
+            @apply px-2 py-1 rounded;
           }
-        `
-      )
-    })
+        }
+      `
+    )
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -194,29 +190,28 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await writeInputFile(
-        'index.css',
-        css`
-          @tailwind base;
-          @tailwind components;
-          @tailwind utilities;
-
-          @layer components {
-            .btn {
-              @apply px-2 py-1 rounded bg-red-500;
-            }
+    await writeInputFile(
+      'index.css',
+      css`
+        @tailwind base;
+        @tailwind components;
+        @tailwind utilities;
+
+        @layer components {
+          .btn {
+            @apply px-2 py-1 rounded bg-red-500;
           }
-        `
-      )
-    })
+        }
+      `
+    )
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
         .btn {
           border-radius: 0.25rem;
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
           padding-left: 0.5rem;
           padding-right: 0.5rem;
           padding-top: 0.25rem;
diff --git a/integrations/rollup/tests/integration.test.js b/integrations/rollup/tests/integration.test.js
index ab17a5d5..0e52a2ec 100644
--- a/integrations/rollup/tests/integration.test.js
+++ b/integrations/rollup/tests/integration.test.js
@@ -1,13 +1,14 @@
 let $ = require('../../execute')
 let { css, html, javascript } = require('../../syntax')
 
-let {
-  readOutputFile,
-  appendToInputFile,
-  writeInputFile,
-  waitForOutputFileCreation,
-  waitForOutputFileChange,
-} = require('../../io')({ output: 'dist', input: 'src' })
+let { readOutputFile, appendToInputFile, writeInputFile } = require('../../io')({
+  output: 'dist',
+  input: 'src',
+})
+
+function ready(message) {
+  return message.includes('created')
+}
 
 describe('static build', () => {
   it('should be possible to generate tailwind output', async () => {
@@ -32,8 +33,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
     await writeInputFile('index.html', html`<div class="font-bold"></div>`)
 
     let runningProcess = $('rollup -c --watch', { env })
-
-    await waitForOutputFileCreation('index.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('index.css')).toIncludeCss(
       css`
@@ -43,9 +43,8 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
       `
     )
 
-    await waitForOutputFileChange('index.css', async () => {
-      await appendToInputFile('index.html', html`<div class="font-normal"></div>`)
-    })
+    await appendToInputFile('index.html', html`<div class="font-normal"></div>`)
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('index.css')).toIncludeCss(
       css`
@@ -58,15 +57,14 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
       `
     )
 
-    await waitForOutputFileChange('index.css', async () => {
-      await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
-    })
+    await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('index.css')).toIncludeCss(
       css`
         .bg-red-500 {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
         }
         .font-bold {
           font-weight: 700;
@@ -84,8 +82,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
     await writeInputFile('index.html', html`<div class="font-bold md:font-medium"></div>`)
 
     let runningProcess = $('rollup -c --watch', { env })
-
-    await waitForOutputFileCreation('index.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('index.css')).toIncludeCss(
       css`
@@ -100,10 +97,9 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
       `
     )
 
-    await waitForOutputFileChange('index.css', async () => {
-      await writeInputFile(
-        '../tailwind.config.js',
-        javascript`
+    await writeInputFile(
+      '../tailwind.config.js',
+      javascript`
           module.exports = {
             content: ['./src/index.html'],
             theme: {
@@ -122,8 +118,8 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
             plugins: [],
           }
       `
-      )
-    })
+    )
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('index.css')).toIncludeCss(
       css`
@@ -145,8 +141,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
     await writeInputFile('index.html', html`<div class="font-bold btn"></div>`)
 
     let runningProcess = $('rollup -c --watch', { env })
-
-    await waitForOutputFileCreation('index.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('index.css')).toIncludeCss(
       css`
@@ -156,22 +151,21 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
       `
     )
 
-    await waitForOutputFileChange('index.css', async () => {
-      await writeInputFile(
-        'index.css',
-        css`
-          @tailwind base;
-          @tailwind components;
-          @tailwind utilities;
-
-          @layer components {
-            .btn {
-              @apply px-2 py-1 rounded;
-            }
+    await writeInputFile(
+      'index.css',
+      css`
+        @tailwind base;
+        @tailwind components;
+        @tailwind utilities;
+
+        @layer components {
+          .btn {
+            @apply px-2 py-1 rounded;
           }
-        `
-      )
-    })
+        }
+      `
+    )
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('index.css')).toIncludeCss(
       css`
@@ -188,29 +182,28 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
       `
     )
 
-    await waitForOutputFileChange('index.css', async () => {
-      await writeInputFile(
-        'index.css',
-        css`
-          @tailwind base;
-          @tailwind components;
-          @tailwind utilities;
-
-          @layer components {
-            .btn {
-              @apply px-2 py-1 rounded bg-red-500;
-            }
+    await writeInputFile(
+      'index.css',
+      css`
+        @tailwind base;
+        @tailwind components;
+        @tailwind utilities;
+
+        @layer components {
+          .btn {
+            @apply px-2 py-1 rounded bg-red-500;
           }
-        `
-      )
-    })
+        }
+      `
+    )
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('index.css')).toIncludeCss(
       css`
         .btn {
           border-radius: 0.25rem;
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
           padding-left: 0.5rem;
           padding-right: 0.5rem;
           padding-top: 0.25rem;
diff --git a/integrations/tailwindcss-cli/tests/cli.test.js b/integrations/tailwindcss-cli/tests/cli.test.js
index b73fc61e..fb39c944 100644
--- a/integrations/tailwindcss-cli/tests/cli.test.js
+++ b/integrations/tailwindcss-cli/tests/cli.test.js
@@ -187,7 +187,7 @@ describe('Build command', () => {
 
         .btn-after {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
           padding-left: 0.5rem;
           padding-right: 0.5rem;
           padding-top: 0.25rem;
@@ -238,7 +238,7 @@ describe('Build command', () => {
 
         .btn-after {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
           padding-left: 0.5rem;
           padding-right: 0.5rem;
           padding-top: 0.25rem;
diff --git a/integrations/tailwindcss-cli/tests/integration.test.js b/integrations/tailwindcss-cli/tests/integration.test.js
index c0808057..72c80378 100644
--- a/integrations/tailwindcss-cli/tests/integration.test.js
+++ b/integrations/tailwindcss-cli/tests/integration.test.js
@@ -1,13 +1,14 @@
 let $ = require('../../execute')
 let { css, html, javascript } = require('../../syntax')
 
-let {
-  readOutputFile,
-  appendToInputFile,
-  writeInputFile,
-  waitForOutputFileCreation,
-  waitForOutputFileChange,
-} = require('../../io')({ output: 'dist', input: 'src' })
+let { readOutputFile, appendToInputFile, writeInputFile } = require('../../io')({
+  output: 'dist',
+  input: 'src',
+})
+
+function ready(message) {
+  return message.includes('Done in')
+}
 
 describe('static build', () => {
   it('should be possible to generate tailwind output', async () => {
@@ -48,22 +49,20 @@ describe('static build', () => {
       `
     )
 
-    $('node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css', {
+    await $('node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css', {
       env: { NODE_ENV: 'production' },
     })
 
-    await waitForOutputFileCreation('main.css')
-
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
         .bg-red-500 {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
         }
 
         .bg-red-600 {
           --tw-bg-opacity: 1;
-          background-color: rgba(220, 38, 38, var(--tw-bg-opacity));
+          background-color: rgb(220 38 38 / var(--tw-bg-opacity));
         }
 
         .font-bold {
@@ -79,8 +78,7 @@ describe('watcher', () => {
     await writeInputFile('index.html', html`<div class="font-bold"></div>`)
 
     let runningProcess = $('node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w')
-
-    await waitForOutputFileCreation('main.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -90,9 +88,8 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await appendToInputFile('index.html', html`<div class="font-normal"></div>`)
-    })
+    await appendToInputFile('index.html', html`<div class="font-normal"></div>`)
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -105,15 +102,14 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
-    })
+    await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
         .bg-red-500 {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
         }
         .font-bold {
           font-weight: 700;
@@ -145,8 +141,7 @@ describe('watcher', () => {
     )
 
     let runningProcess = $('node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w')
-
-    await waitForOutputFileCreation('main.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -156,9 +151,8 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await appendToInputFile('index.html', html`<div class="font-normal"></div>`)
-    })
+    await appendToInputFile('index.html', html`<div class="font-normal"></div>`)
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).not.toIncludeCss(css`
       @layer base {
@@ -186,8 +180,7 @@ describe('watcher', () => {
     await writeInputFile('index.html', html`<div class="font-bold md:font-medium"></div>`)
 
     let runningProcess = $('node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w')
-
-    await waitForOutputFileCreation('main.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -202,10 +195,9 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await writeInputFile(
-        '../tailwind.config.js',
-        javascript`
+    await writeInputFile(
+      '../tailwind.config.js',
+      javascript`
           module.exports = {
             content: ['./src/index.html'],
             theme: {
@@ -224,8 +216,8 @@ describe('watcher', () => {
             plugins: [],
           }
       `
-      )
-    })
+    )
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -247,8 +239,7 @@ describe('watcher', () => {
     await writeInputFile('index.html', html`<div class="font-bold btn"></div>`)
 
     let runningProcess = $('node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css -w')
-
-    await waitForOutputFileCreation('main.css')
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -258,22 +249,21 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await writeInputFile(
-        'index.css',
-        css`
-          @tailwind base;
-          @tailwind components;
-          @tailwind utilities;
-
-          @layer components {
-            .btn {
-              @apply px-2 py-1 rounded;
-            }
+    await writeInputFile(
+      'index.css',
+      css`
+        @tailwind base;
+        @tailwind components;
+        @tailwind utilities;
+
+        @layer components {
+          .btn {
+            @apply px-2 py-1 rounded;
           }
-        `
-      )
-    })
+        }
+      `
+    )
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
@@ -290,29 +280,28 @@ describe('watcher', () => {
       `
     )
 
-    await waitForOutputFileChange('main.css', async () => {
-      await writeInputFile(
-        'index.css',
-        css`
-          @tailwind base;
-          @tailwind components;
-          @tailwind utilities;
-
-          @layer components {
-            .btn {
-              @apply px-2 py-1 rounded bg-red-500;
-            }
+    await writeInputFile(
+      'index.css',
+      css`
+        @tailwind base;
+        @tailwind components;
+        @tailwind utilities;
+
+        @layer components {
+          .btn {
+            @apply px-2 py-1 rounded bg-red-500;
           }
-        `
-      )
-    })
+        }
+      `
+    )
+    await runningProcess.onStderr(ready)
 
     expect(await readOutputFile('main.css')).toIncludeCss(
       css`
         .btn {
           border-radius: 0.25rem;
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
           padding-left: 0.5rem;
           padding-right: 0.5rem;
           padding-top: 0.25rem;
diff --git a/integrations/vite/tests/integration.test.js b/integrations/vite/tests/integration.test.js
index 18ff59c7..17a5ebb6 100644
--- a/integrations/vite/tests/integration.test.js
+++ b/integrations/vite/tests/integration.test.js
@@ -87,7 +87,7 @@ describe('watcher', () => {
       css`
         .bg-red-500 {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
         }
         .font-bold {
           font-weight: 700;
@@ -242,7 +242,7 @@ describe('watcher', () => {
         .btn {
           border-radius: 0.25rem;
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
           padding-left: 0.5rem;
           padding-right: 0.5rem;
           padding-top: 0.25rem;
diff --git a/integrations/webpack-4/tests/integration.test.js b/integrations/webpack-4/tests/integration.test.js
index ce1d5dba..929a8bd7 100644
--- a/integrations/webpack-4/tests/integration.test.js
+++ b/integrations/webpack-4/tests/integration.test.js
@@ -64,7 +64,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
       css`
         .bg-red-500 {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
         }
         .font-bold {
           font-weight: 700;
@@ -208,7 +208,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
         .btn {
           border-radius: 0.25rem;
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
           padding-left: 0.5rem;
           padding-right: 0.5rem;
           padding-top: 0.25rem;
diff --git a/integrations/webpack-5/tests/integration.test.js b/integrations/webpack-5/tests/integration.test.js
index 6112b7bf..b6f65452 100644
--- a/integrations/webpack-5/tests/integration.test.js
+++ b/integrations/webpack-5/tests/integration.test.js
@@ -64,7 +64,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
       css`
         .bg-red-500 {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
         }
         .font-bold {
           font-weight: 700;
@@ -208,7 +208,7 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
         .btn {
           border-radius: 0.25rem;
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
           padding-left: 0.5rem;
           padding-right: 0.5rem;
           padding-top: 0.25rem;
@@ -253,12 +253,12 @@ describe.each([{ TAILWIND_MODE: 'watch' }, { TAILWIND_MODE: undefined }])('watch
       css`
         .bg-red-500 {
           --tw-bg-opacity: 1;
-          background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
+          background-color: rgb(239 68 68 / var(--tw-bg-opacity));
         }
 
         .bg-red-600 {
           --tw-bg-opacity: 1;
-          background-color: rgba(220, 38, 38, var(--tw-bg-opacity));
+          background-color: rgb(220 38 38 / var(--tw-bg-opacity));
         }
 
         .font-bold {
diff --git a/tests/detect-nesting.test.js b/tests/detect-nesting.test.js
new file mode 100644
index 00000000..35e30393
--- /dev/null
+++ b/tests/detect-nesting.test.js
@@ -0,0 +1,73 @@
+import { run, html, css } from './util/run'
+
+it('should warn when we detect nested css', () => {
+  let config = {
+    content: [{ raw: html`<div class="nested"></div>` }],
+  }
+
+  let input = css`
+    @tailwind utilities;
+
+    .nested {
+      .example {
+      }
+    }
+  `
+
+  return run(input, config).then((result) => {
+    expect(result.messages).toHaveLength(1)
+    expect(result.messages).toMatchObject([
+      {
+        type: 'warning',
+        text: 'Nested CSS detected, checkout the docs on how to support nesting: https://tailwindcss.com/docs/using-with-preprocessors#nesting',
+      },
+    ])
+  })
+})
+
+it('should not warn when nesting a single rule inside a media query', () => {
+  let config = {
+    content: [{ raw: html`<div class="nested"></div>` }],
+  }
+
+  let input = css`
+    @tailwind utilities;
+
+    @media (min-width: 768px) {
+      .nested {
+      }
+    }
+  `
+
+  return run(input, config).then((result) => {
+    expect(result.messages).toHaveLength(0)
+    expect(result.messages).toEqual([])
+  })
+})
+
+it('should only warn for the first detected nesting ', () => {
+  let config = {
+    content: [{ raw: html`<div class="nested other"></div>` }],
+  }
+
+  let input = css`
+    @tailwind utilities;
+
+    .nested {
+      .example {
+      }
+
+      .other {
+      }
+    }
+
+    .other {
+      .example {
+      }
+    }
+  `
+
+  return run(input, config).then((result) => {
+    expect(result.messages).toHaveLength(1)
+  })
+})