Repository: eslint/css (js)

Issue:
Change Request: Allow custom descriptors in no-invalid-at-rules
### Environment

HEAD

### What problem do you want to solve?

Right now, the `no-invalid-at-rules` rule will flag any descriptor beginning with a `--` as invalid. For example, the [`@theme` rule](https://tailwindcss.com/docs/functions-and-directives#theme-directive) from Tailwind 4:

```css
@theme {
  --font-display: "Satoshi", "sans-serif";
  --breakpoint-3xl: 120rem;
  --color-avocado-100: oklch(0.99 0 0);
  --color-avocado-200: oklch(0.98 0.04 113.22);
  --color-avocado-300: oklch(0.94 0.11 115.03);
  --color-avocado-400: oklch(0.92 0.19 114.08);
  --color-avocado-500: oklch(0.84 0.18 117.33);
  --color-avocado-600: oklch(0.53 0.12 118.34);
  --ease-fluid: cubic-bezier(0.3, 0, 0, 1);
  --ease-snappy: cubic-bezier(0.2, 0, 0, 1);
  /* ... */
}
```

This can never be considered valid because each custom descriptor is flagged as invalid.


### What do you think is the correct solution?

I think `no-invalid-at-rules` should skip validating custom descriptors. This would match the behavior in `no-invalid-properties` where we don't validate custom properties:
https://github.com/eslint/css/blob/7de79159fed82543eceadc537ca6f4020076636c/src/rules/no-invalid-properties.js#L50-L52

### Participation

- [ ] I am willing to submit a pull request for this change.

### Additional comments

_No response_

Pull request:
feat: allow custom descriptors in no-invalid-at-rules
<!--
    Thank you for contributing!

    ESLint adheres to the [OpenJS Foundation Code of Conduct](https://eslint.org/conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/HEAD/CONTRIBUTING.md).

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/latest/contribute/pull-requests)
    - Update or create tests
    - If performance-related, include a benchmark
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What is the purpose of this pull request?

This PR modifies the `no-invalid-at-rules` rule to allow custom descriptors in at-rules.

#### What changes did you make? (Give an overview)

Modified the `no-invalid-at-rules` rule to skip validation for any descriptor that starts with `--`

#### Related Issues

Closes #127

<!-- include tags like "fixes #123" or "refs #123" -->

#### Is there anything you'd like reviewers to focus on?

<!-- markdownlint-disable-file MD004 -->


Patch:
diff --git a/README.md b/README.md
index dbbf7b5..56ddad0 100644
--- a/README.md
+++ b/README.md
@@ -237,6 +237,28 @@ export default defineConfig([
 
 **Note:** The Tailwind syntax doesn't currently provide for the `theme()` function. This is a [limitation of CSSTree](https://github.com/csstree/csstree/issues/292) that we hope will be resolved soon.
 
+## Editor and IDE Setup
+
+### Visual Studio Code
+
+First, ensure you have the [ESLint plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) installed.
+
+Then, edit `eslint.validate` in your `settings.json` file to include `css`:
+
+```json
+{
+	"eslint.validate": ["css"]
+}
+```
+
+### JetBrains WebStorm
+
+For any [JetBrains WebStorm](https://www.jetbrains.com/webstorm/), configure the [ESLint scope](https://www.jetbrains.com/help/webstorm/eslint.html#ws_eslint_configure_scope) to include `css`, such as:
+
+```text
+**/*.{js,ts,jsx,tsx,cjs,cts,mjs,mts,html,vue,css}
+```
+
 ## License
 
 Apache 2.0
@@ -250,7 +272,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
 to get your logo on our READMEs and [website](https://eslint.org/sponsors).
 
 <h3>Diamond Sponsors</h3>
-<p><a href="https://www.ag-grid.com/"><img src="https://images.opencollective.com/ag-grid/2c8d545/logo.png" alt="AG Grid" height="128"></a></p><h3>Platinum Sponsors</h3>
+<p><a href="https://www.ag-grid.com/"><img src="https://images.opencollective.com/ag-grid/bec0580/logo.png" alt="AG Grid" height="128"></a></p><h3>Platinum Sponsors</h3>
 <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
 <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301" alt="American Express" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
diff --git a/src/rules/no-invalid-at-rules.js b/src/rules/no-invalid-at-rules.js
index c2c0204..adcf514 100644
--- a/src/rules/no-invalid-at-rules.js
+++ b/src/rules/no-invalid-at-rules.js
@@ -121,6 +121,11 @@ export default {
 			},
 
 			"AtRule > Block > Declaration"(node) {
+				// skip custom descriptors
+				if (node.property.startsWith("--")) {
+					return;
+				}
+
 				// get at rule node
 				const atRule = /** @type {AtrulePlain} */ (
 					sourceCode.getParent(sourceCode.getParent(node))


Test patch:
diff --git a/tests/rules/no-invalid-at-rules.test.js b/tests/rules/no-invalid-at-rules.test.js
index 151261c..4da4b6b 100644
--- a/tests/rules/no-invalid-at-rules.test.js
+++ b/tests/rules/no-invalid-at-rules.test.js
@@ -67,6 +67,19 @@ ruleTester.run("no-invalid-at-rules", rule, {
 				customSyntax: tailwindSyntax,
 			},
 		},
+		{
+			code: `@custom-rule {
+				--foo: red;
+				--bar: blue;
+			}`,
+			languageOptions: {
+				customSyntax: {
+					atrules: {
+						"custom-rule": {},
+					},
+				},
+			},
+		},
 	],
 	invalid: [
 		{