facebook / facebook/stylex

[eslint-plugin] valid-styles rejects compound pseudo-classes like :hover:not(:disabled)

Open
#1,618 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
10.3k
Forks
481
Avg merge
3d 8h
Merged PRs (30d)
13

Description

The `@stylexjs/valid-styles` ESLint rule rejects compound pseudo-class selectors like `:hover:not(:disabled)` and `:active:not(:disabled)` when used as conditional style keys in `stylex.create()`.

The **Babel plugin compiles these correctly** — they produce valid CSS like `.x15hc757:active:not(:disabled){background-color:blue}`. But the ESLint rule's `pseudoClassesAndAtRules` allowlist in `cssProperties.js` uses exact literal matching (`makeLiteralRule`) which doesn't account for compound pseudo-classes.

### Steps to reproduce

```tsx
import * as stylex from "@stylexjs/stylex";

const styles = stylex.create({
button: {
backgroundColor: {
default: "red",
// ✅ ESLint passes — flat value
":active:not(:disabled)": "blue",
// ❌ ESLint fails — nested media query value
":hover:not(:disabled)": { default: null, "@media (any-hover: hover) and (any-pointer: fine)": "blue" },
},
},
});
```

**Note:** the flat value on `:active:not(:disabled)` passes, but the nested media query object on `:hover:not(:disabled)` triggers the error. This appears to be because the validation code path differs depending on whether the value is a literal vs an object with further nesting.

### Expected behavior

The rule should accept compound pseudo-classes that combine known pseudo-classes (`:hover`, `:active`, etc.) with `:not()` containing another valid pseudo-class.

### Actual behavior

```
error Invalid Pseudo class or At Rule used for conditional style value @stylexjs/valid-styles
```

### Root cause

In `lib/reference/cssProperties.js`, the `pseudoClassesAndAtRules` union rule only contains `makeLiteralRule` entries for simple pseudo-classes (`:hover`, `:active`, `:disabled`, etc.). There's no regex or pattern matcher for compound selectors.

### Suggested fix

Add a `makeRegExRule` entry to the union that matches compound pseudo-class patterns:

```js
(0, _makeRegExRule.default)(
/^:(hover|focus|focus-visible|focus-within|active|visited|disabled):not\(:[a-z-]+\)$/,
'a compound pseudo class'
)
```

### Environment

- `@stylexjs/eslint-plugin`: 0.18.1
- `@stylexjs/babel-plugin`: 0.18.1

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.