Repository: tailwindlabs/tailwindcss (TypeScript)

Issue:
Abnormal css ouput when some string in the code is wrapped in square brackets
**What version of Tailwind CSS are you using?**

v3.0.1

**What build tool (or framework if it abstracts the build tool) are you using?**

Next.js 12.0.4, postcss 8.4.4

**What version of Node.js are you using?**

v14.18.1

**What browser are you using?**

Chrome

**What operating system are you using?**

macOS

**Reproduction URL**

https://github.com/DrShpongle/tailwindcss-3-issue-demo

**Describe your issue**

In case we use some text wrapped with square brackets we get abnormal css output:

**Example 1:**
Just an object with string field:
https://github.com/DrShpongle/tailwindcss-3-issue-demo/blob/main/components/problem-01.js#L4-L5
![problem-01 js — with-tailwindcss-app 2021-12-11 at 1 36 31 PM](https://user-images.githubusercontent.com/1519448/145675038-5633e78e-79ad-493a-b3ab-8d0245fa0831.jpg)

and related output:
https://github.com/DrShpongle/tailwindcss-3-issue-demo/blob/main/dist/output.css#L579-L581
![output css — with-tailwindcss-app 2021-12-11 at 1 38 58 PM](https://user-images.githubusercontent.com/1519448/145675095-66ae8a3c-3e50-49f7-9948-2f3fa8802b86.jpg)

**Example 2:**
Just regular dumb component:
https://github.com/DrShpongle/tailwindcss-3-issue-demo/blob/main/components/problem-02.tsx#L10
![problem-02 tsx — with-tailwindcss-app 2021-12-11 at 1 42 50 PM](https://user-images.githubusercontent.com/1519448/145675212-7f46ff3e-6877-474f-9f70-3848f2140dc5.jpg)

and related output:
https://github.com/DrShpongle/tailwindcss-3-issue-demo/blob/main/dist/output.css#L583-L585
![output css — with-tailwindcss-app 2021-12-11 at 1 44 09 PM](https://user-images.githubusercontent.com/1519448/145675245-51b3dea5-f9ce-43b4-8a2c-ba6e230d1ec6.jpg)






Hints:
This is probably because of [arbitrary properties](https://tailwindcss.com/docs/adding-custom-styles#arbitrary-properties). 

you can just add an unnecessary escape:
```ts
const ProblemComponent = () => {
  return (
    <button
      nCanPlay={(event: any) => {
        console.debug(`player ready: [autoplay\:${autoplay}]`);
      }}
    ></button>
  );
};
```

you can also use a function like this:
```ts
const addBrackets = (str: string) => `[${str}]`;

const ProblemComponent = () => {
  return (
    <button
      nCanPlay={(event: any) => {
        console.debug(`player ready: ${addBrackets(`autoplay:${autoplay}`)}]`);
      }}
    ></button>
  );
};
```

Patch:
diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js
--- a/src/lib/generateRules.js
+++ b/src/lib/generateRules.js
@@ -248,6 +248,21 @@ function parseRules(rule, cache, options = {}) {
   return [cache.get(rule), options]
 }
 
+const IS_VALID_PROPERTY_NAME = /^[a-z_-]/
+
+function isValidPropName(name) {
+  return IS_VALID_PROPERTY_NAME.test(name)
+}
+
+function isParsableCssValue(property, value) {
+  try {
+    postcss.parse(`a{${property}:${value}}`).toResult()
+    return true
+  } catch (err) {
+    return false
+  }
+}
+
 function extractArbitraryProperty(classCandidate, context) {
   let [, property, value] = classCandidate.match(/^\[([a-zA-Z0-9-_]+):(\S+)\]$/) ?? []
 
@@ -255,9 +270,17 @@ function extractArbitraryProperty(classCandidate, context) {
     return null
   }
 
+  if (!isValidPropName(property)) {
+    return null
+  }
+
+  if (!isValidArbitraryValue(value)) {
+    return null
+  }
+
   let normalized = normalize(value)
 
-  if (!isValidArbitraryValue(normalized)) {
+  if (!isParsableCssValue(property, normalized)) {
     return null
   }
 


Test patch:
diff --git a/tests/arbitrary-properties.test.js b/tests/arbitrary-properties.test.js
--- a/tests/arbitrary-properties.test.js
+++ b/tests/arbitrary-properties.test.js
@@ -231,3 +231,45 @@ test('invalid class', () => {
     expect(result.css).toMatchFormattedCss(css``)
   })
 })
+
+test('invalid arbitrary property', () => {
+  let config = {
+    content: [
+      {
+        raw: html`<div class="[autoplay:\${autoplay}]"></div>`,
+      },
+    ],
+    corePlugins: { preflight: false },
+  }
+
+  let input = css`
+    @tailwind base;
+    @tailwind components;
+    @tailwind utilities;
+  `
+
+  return run(input, config).then((result) => {
+    expect(result.css).toMatchFormattedCss(css``)
+  })
+})
+
+test('invalid arbitrary property 2', () => {
+  let config = {
+    content: [
+      {
+        raw: html`[0:02]`,
+      },
+    ],
+    corePlugins: { preflight: false },
+  }
+
+  let input = css`
+    @tailwind base;
+    @tailwind components;
+    @tailwind utilities;
+  `
+
+  return run(input, config).then((result) => {
+    expect(result.css).toMatchFormattedCss(css``)
+  })
+})