Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c5586c7
Lint inline <style scoped> blocks with a postcss custom syntax
tylerturdenpants Sep 2, 2026
d5c232c
Make the syntax's parsers optional peer dependencies
tylerturdenpants Sep 2, 2026
14c2f06
Fix six ways the syntax reported success while doing the wrong thing
tylerturdenpants Sep 2, 2026
0545710
Ship the syntax's parsers as dependencies rather than peers
tylerturdenpants Sep 4, 2026
a7de581
Read scoped and lang attributes with ember-scoped-css's own helpers
tylerturdenpants Sep 4, 2026
bca9710
Link the dropped-CSS limitation to its tracking issue
tylerturdenpants Sep 4, 2026
ab8f4ed
Drop the limitation the build no longer has
tylerturdenpants Sep 4, 2026
dec4db7
Add @glimmer/env so the syntax tests can run
tylerturdenpants Sep 4, 2026
0d8416d
Point each block's input at the .gts its offsets index
tylerturdenpants Sep 4, 2026
e186c62
Lint inline scss, less and stylus instead of skipping them
tylerturdenpants Sep 4, 2026
0b45c27
Leave ember-scoped-css untouched by this PR
tylerturdenpants Sep 4, 2026
11629db
Say why indented Sass is skipped, having tried both parsers
tylerturdenpants Sep 4, 2026
b5bb820
Read template coordinates with content-tag-utils
tylerturdenpants Sep 4, 2026
cef5dd4
Take content-tag-utils 0.7.2 and lean on its BOM handling
tylerturdenpants Sep 8, 2026
ce5d50e
Note the content-tag floor in the README
tylerturdenpants Sep 8, 2026
9bf926c
Remove content-tag
NullVoxPopuli Sep 9, 2026
6d4942c
Cleanup comments
NullVoxPopuli Sep 9, 2026
b5ed953
Stop caring so much about BOMs
NullVoxPopuli Sep 9, 2026
0335294
Update README
NullVoxPopuli Sep 9, 2026
7319064
Cleanup
NullVoxPopuli Sep 9, 2026
3903bef
Fix preprocessor support
NullVoxPopuli Sep 9, 2026
419cccf
Break apart test files by topic
NullVoxPopuli Sep 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions stylelint-ember-scoped-css/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,88 @@ This adds ember-scoped-css specific rules to your existing stylelint config.
}
```

## Linting inline `<style scoped>` blocks

Stylelint reads only `.css` files by default, so CSS in an inline
`<style scoped>` block is never linted. This package ships a PostCSS custom
syntax that exposes those blocks to stylelint.

Add an override for your `.gts` and `.gjs` files. `.css` files keep the
default syntax:

```json
{
"overrides": [
{
"files": ["app/components/**/*.css", "app/templates/**/*.css"],
"extends": ["stylelint-ember-scoped-css/config"]
},
{
"files": ["app/components/**/*.{gts,gjs}"],
"extends": ["stylelint-ember-scoped-css/config"],
"customSyntax": "stylelint-ember-scoped-css/syntax"
}
]
}
```

Then widen the glob in your `lint:css` script:

```json
"lint:css": "stylelint 'app/**/*.{css,gts,gjs}'"
```

Warnings report the line and column in the `.gts` file. `--fix` rewrites only
the CSS and leaves the rest of the file byte-for-byte.

The syntax is opt-in. Nothing changes until you add `customSyntax` and widen
the glob. A codebase that has never linted its inline styles will get the whole
backlog at once, so adopt it one package at a time.

### What gets linted

Every `<style scoped>` at the root of a `<template>`. A `<style>` inside a JS
string is left alone. The build already rejects a `<style scoped>` nested
deeper than the template root.

`lang` picks the dialect, case-insensitively. Any other value, or no `lang`, is
plain CSS, which is what the build does too. Each dialect needs its parser
installed, the same way Vite needs the preprocessor:

```sh
# lang="scss"
npm add -D postcss-scss
# lang="sass"
npm add -D sugarss
# lang="less"
npm add -D postcss-less
# lang="styl" or lang="stylus"
npm add -D postcss-styl
```

A block whose parser is missing is reported at its `<style>` tag with the
package to install.

Skipped:

- `<style>` without `scoped`. That is global CSS, and `no-unscoped-selectors`
must not fire on it.
- Blocks that contain a `{{mustache}}`. Interpolated CSS cannot be parsed.
- Blocks in a component whose template does not parse. Glint or the template
compiler already reports that error.

### Known limitations

- `stylelint-disable` comments must sit inside the `<style>` block. A
file-level `/* stylelint-disable */` at the top of a `.gts` has no effect.
`stylelint-disable-next-line` inside the block works.
- A CSS syntax error in one block stops linting for the whole file, the same
as in a `.css` file. The error is reported at its line in the `.gts`.
- `--fix` drops a leading UTF-8 byte order mark.
- In indented Sass, the `=mixin` and `+include` shorthand is reported as a
syntax error. Use `@mixin` and `@include`. Nested properties such as `font:`
with indented children are read as a rule, so selector rules may report them.

## List of rules

- [`no-unscoped-selectors`](./src/rules/no-unscoped-selectors/README.md)
Expand Down
36 changes: 33 additions & 3 deletions stylelint-ember-scoped-css/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"main": "src/index.js",
"exports": {
".": "./src/index.js",
"./config": "./src/config.js"
"./config": "./src/config.js",
"./syntax": "./src/syntax.js"
},
"files": [
"src"
Expand All @@ -35,21 +36,50 @@
"lint:prettier": "prettier . -c",
"test": "vitest"
},
"dependencies": {
"@glimmer/syntax": "^0.94.8",
"content-tag-utils": "^0.7.2",
"postcss": "^8.4.0"
},
"devDependencies": {
"@glimmer/env": "^0.1.7",
"@nullvoxpopuli/eslint-configs": "^5.3.4",
"@tsconfig/strictest": "^2.0.5",
"concurrently": "^9.2.1",
"ember-scoped-css": "workspace:*",
"eslint": "^9.36.0",
"postcss": "^8.5.6",
"postcss-less": "^6.0.0",
"postcss-scss": "^4.0.9",
"postcss-selector-parser": "^6.1.0",
"postcss-styl": "^0.12.3",
"prettier": "^3.6.2",
"stylelint": "^17.0.0",
"sugarss": "^5.0.1",
"typescript": "^5.9.2",
"vitest": "^3.2.4",
"vitest-stylelint-utils": "^2.0.0"
},
"peerDependencies": {
Comment thread
NullVoxPopuli marked this conversation as resolved.
"stylelint": "^16.0.0 || ^17.0.0"
"ember-scoped-css": ">= 3.6.0",
"postcss-less": "^6.0.0",
"postcss-scss": "^4.0.9",
"postcss-styl": "^0.12.3",
"stylelint": "^16.0.0 || ^17.0.0",
"sugarss": "^5.0.1"
},
"peerDependenciesMeta": {
"postcss-less": {
"optional": true
},
"postcss-scss": {
"optional": true
},
"postcss-styl": {
"optional": true
},
"sugarss": {
"optional": true
}
},
"engines": {
"node": ">= 22.16"
Expand Down
Loading
Loading