Bug: use-layers reports keyframe blocks as rules that need to be in a layer
- Dominant language
- JavaScript
- Stars
- 308
- Forks
- 44
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 18
Description
### Environment
ESLint version: v10.7.0
@eslint/css version: v1.4.0
Node version: v22.15.0
npm version: v11.12.1
Operating System: Windows 11
### Which language are you using?
stylesheet
### What did you do?
I enabled `use-layers` while adopting cascade layers in a project that contains CSS animations, and every `@keyframes` block immediately produced errors on its keyframe steps.
Configuration
```js
import css from "@eslint/css";
export default [
{
files: ["**/*.css"],
plugins: { css },
language: "css/css",
rules: {
"css/use-layers": "error",
},
},
];
```
```css
@keyframes spin {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
```
The `from` / `to` form and vendor-prefixed keyframes (`@-webkit-keyframes`) produce the same errors.
### What did you expect to happen?
No errors. Keyframe steps (`0%`, `100%`, `from`, `to`) are not style rules that participate in the cascade — they are keyframe selectors inside an at-rule — so requiring them to be "within a layer" doesn't apply to them.
Wrapping the `@keyframes` in `@layer` does silence the errors, but that's not a real resolution: `@keyframes` has no relationship to cascade layers, so the rule effectively forces authors to either wrap every animation in a layer or disable the rule around keyframes.
### What actually happened?
Each keyframe step is reported as a rule that should be within a layer:
```
2:3 error Expected rule to be within a layer css/use-layers
5:3 error Expected rule to be within a layer css/use-layers
```
Root cause: css-tree parses keyframe steps as `Rule` nodes, and the `Rule` handler in `src/rules/use-layers.js` reports any `Rule` whenever `layerDepth` is `0`, without checking whether the node is inside a `@keyframes` block:
```js
Rule(node) {
if (layerDepth > 0) {
return;
}
context.report({
loc: node.loc,
messageId: "missingLayer",
});
},
```
A possible fix is to track keyframes the same way `no-duplicate-keyframe-selectors` does - an `Atrule[name=/^(-(o|moz|webkit)-)?keyframes$/i]` enter/exit pair maintaining a `keyframesDepth`, and an early return in the `Rule` handler when inside one. Rules nested in other at-rules (`@media`, `@supports`) should still be reported, since those do participate in the cascade.
### Link to Minimal Reproducible Example
https://stackblitz.com/edit/stackblitz-starters-ru1f8tzx?description=Starter%20project%20for%20Node.js,%20a%20JavaScript%20runtime%20built%20on%20Chrome%27s%20V8%20JavaScript%20engine&file=eslint.config.mjs,test.css&title=node.new%20Starter
### Participation
- [x] I am willing to submit a pull request for this issue.
### AI acknowledgment
- [ ] I did not use AI to generate this issue report.
- [x] (If the above is not checked) I have reviewed the AI-generated content before submitting.
### Additional comments
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.