eslint / eslint/css

New Rule: `no-declarations-after-nested-rules`

Open
#497 3 comments 0 reactions 1 assignee Claimed by @przemyslaw-zan View on GitHub
feature
Dominant language
JavaScript
Stars
308
Forks
44
Avg merge
2d 6h
Merged PRs (30d)
18

Description

### Rule details

Disallow bare declarations placed after nested rules within the same block. Declarations should come before nested rules, or be wrapped in `& { }`.

### What type of rule is this?

Warns about a potential problem

### Example code

```css
.actions {
padding: 8px;
display: flex;

& > * + * {
margin-inline-start: 8px;
}

justify-content: flex-end; /* stranded after the nested rule */
}

.message {
@media (width >= 600px) {
padding: 16px;
}

padding: 8px; /* interop hazard: renders differently in older engines */
}
```

### Prior Art

- Sass made this pattern a breaking change in 1.77 and rejects it outright unless the declarations are wrapped in `& { }`: https://sass-lang.com/documentation/breaking-changes/mixed-decls/
- [stylelint-no-mixed-decls](https://github.com/apostrophecms/apostrophe/tree/main/packages/stylelint-no-mixed-decls) - a stylelint plugin enforcing the same constraint.

### Participation

- [x] I am willing to submit a pull request to implement this rule.

### 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

- Before [CSSNestedDeclarations](https://web.dev/blog/css-nesting-cssnesteddeclarations) (Chrome 130, Firefox 132, Safari 18.2), engines hoisted trailing declarations above the nested rules, while engines implementing the current spec keep them in place - so in the `.message` example above, older engines render `padding: 16px` at `>= 600px` while spec-compliant ones always render `padding: 8px`. Same stylesheet, different rendering. The corrected behavior is Baseline newly available, so both behaviors coexist in still-supported browsers for a long while.
- Even on spec-compliant engines the pattern is a latent conflict: it is harmless until someone later adds an overlapping `@media`/state/child rule, at which point source order silently decides the winner.
- Declarations wrapped in `& { }` would not be reported - that is the spec-blessed way to place declarations after nested rules, and the fix Sass mandates.
- A suggestion (rather than an autofix) to wrap the declaration in `& { }` seems feasible. Moving the declaration up automatically would not be safe, since on spec-compliant engines that changes the cascade outcome.

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.