├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── nodejs.yml │ └── release.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __tests__ ├── engines.test.mjs ├── index.test.mjs ├── invalid.css └── valid.css ├── eslint.config.mjs ├── index.js ├── package-lock.json └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = tab 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [package.json] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.{md,css,yml}] 16 | indent_style = space 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: '/' 5 | schedule: 6 | interval: monthly 7 | open-pull-requests-limit: 5 8 | versioning-strategy: increase 9 | labels: 10 | - 'pr: dependencies' 11 | groups: 12 | development-dependencies: 13 | dependency-type: 'development' 14 | exclude-patterns: ['stylelint'] 15 | ignore: 16 | - dependency-name: 'stylelint' # Avoid updating the peer dependency. 17 | 18 | - package-ecosystem: github-actions 19 | directory: '/' 20 | schedule: 21 | interval: monthly 22 | open-pull-requests-limit: 3 23 | labels: 24 | - 'pr: dependencies' 25 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | jobs: 12 | lint: 13 | uses: stylelint/.github/.github/workflows/lint.yml@main 14 | 15 | test: 16 | uses: stylelint/.github/.github/workflows/test.yml@main 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: ['**'] 6 | 7 | concurrency: 8 | group: ${{ github.workflow }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | timeout-minutes: 10 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | - name: Create release 21 | uses: stylelint/changelog-to-github-release-action@main 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .eslintcache 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx --no lint-staged 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix = "" 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 38.0.0 4 | 5 | - Removed: `stylelint` less than `16.18.0` from peer dependencies. 6 | - Changed: updated to [`stylelint-config-recommended@16.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/16.0.0). 7 | - Added: `color-function-alias-notation: "without-alpha"` rule. 8 | - Added: `container-name-pattern` (kebab-case with optional `--` prefix) rule. 9 | - Added: `layer-name-pattern` (kebab-case) rule. 10 | 11 | ## 37.0.0 12 | 13 | - Removed: `stylelint` less than `16.13.0` from peer dependencies. 14 | - Changed: updated to [`stylelint-config-recommended@15.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/15.0.0). 15 | 16 | ## 36.0.1 17 | 18 | - Fixed: missing `funding` field in `package.json`. 19 | 20 | ## 36.0.0 21 | 22 | - Removed: `stylelint` less than `16.1.0` from peer dependencies. 23 | - Added: `lightness-notation: "percentage"` rule. 24 | 25 | ## 35.0.0 26 | 27 | - Removed: `stylelint` less than `16.0.0` from peer dependencies. 28 | - Removed: Node.js less than `18.12.0` support. 29 | - Changed: updated to [`stylelint-config-recommended@14.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/14.0.0). 30 | 31 | ## 34.0.0 32 | 33 | - Removed: `stylelint` less than `15.10.0` from peer dependencies 34 | - Changed: updated to [`stylelint-config-recommended@13.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/13.0.0). 35 | 36 | ## 33.0.0 37 | 38 | - Removed: `stylelint` less than `15.5.0` from peer dependencies. 39 | - Changed: updated to [`stylelint-config-recommended@12.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/12.0.0). 40 | - Changed: restored `declaration-block-single-line-max-declarations` removed on `30.0.0`. 41 | 42 | ## 32.0.0 43 | 44 | - Removed: `stylelint` less than `15.4.0` from peer dependencies. 45 | - Added: `media-feature-range-notation: "context"` rule. 46 | 47 | ## 31.0.0 48 | 49 | - Removed: `stylelint` less than `15.3.0` from peer dependencies. 50 | - Changed: updated to [`stylelint-config-recommended@11.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/11.0.0). 51 | 52 | ## 30.0.1 53 | 54 | - Fixed: updated to [`stylelint-config-recommended@10.0.1`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/10.0.1). 55 | 56 | ## 30.0.0 57 | 58 | - Removed: `stylelint` less than `15.0.0` from peer dependencies. 59 | - Removed: 64 rules deprecated in [`stylelint@15.0.0`](https://github.com/stylelint/stylelint/releases/tag/15.0.0). For details, see the [migration guide](https://github.com/stylelint/stylelint/blob/15.0.0/docs/migration-guide/to-15.md). 60 | - Fixed: `length-zero-no-unit` to ignore custom properties. 61 | - Fixed: `value-no-vendor-prefix` to ignore `-webkit-inline-box`. 62 | 63 | ## 29.0.0 64 | 65 | - Removed: `stylelint` less than `14.14.0` from peer dependencies. 66 | - Changed: `import-notation` to be `"url"`. 67 | - Added: custom parameter messages to `*-pattern` rules. 68 | - Fixed: `value-no-vendor-prefix` to ignore `-webkit-box`. 69 | 70 | ## 28.0.0 71 | 72 | - Removed: `stylelint` less than `14.11.0` from peer dependencies. 73 | - Changed: `alpha-value-notation` to be `"number"` for SVG `*-opacity` properties. 74 | 75 | ## 27.0.0 76 | 77 | - Removed: `stylelint` less than `14.10.0` from peer dependencies. 78 | - Changed: updated to [`stylelint-config-recommended@9.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/9.0.0). 79 | - Added: `keyframe-selector-notation` rule. 80 | 81 | ## 26.0.0 82 | 83 | - Removed: `stylelint` less than `14.9.0` from peer dependencies. 84 | - Changed: updated to [`stylelint-config-recommended@8.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/8.0.0). 85 | - Added: `import-notation` rule. 86 | - Added: `selector-not-notation` rule. 87 | 88 | ## 25.0.0 89 | 90 | - Removed: `stylelint` less than `14.4.0` from peer dependencies. 91 | - Changed: updated to [`stylelint-config-recommended@7.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/7.0.0). 92 | 93 | ## 24.0.0 94 | 95 | - Changed: `alpha-value-notation` to be `"number"` for `opacity` property. 96 | 97 | ## 23.0.0 98 | 99 | This release adds over a dozen new rules. 100 | 101 | If needed, you can [extend the config](README.md#extending-the-config) to turn off or lower the severity of any of the new rules. 102 | 103 | We recommend using [Autoprefixer](https://github.com/postcss/autoprefixer) to automatically prefix your at-rules, properties, selectors and values. 104 | 105 | - Removed: `stylelint` less than `14.0.0` from peer dependencies. 106 | - Changed: updated to [`stylelint-config-recommended@6.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/6.0.0). 107 | - Added: `alpha-value-notation` rule. 108 | - Added: `at-rule-no-vendor-prefix` rule. 109 | - Added: `color-function-notation` rule. 110 | - Added: `custom-media-pattern` rule. 111 | - Added: `custom-property-pattern` rule. 112 | - Added: `declaration-block-no-redundant-longhand-properties` rule. 113 | - Added: `font-family-name-quotes` rule. 114 | - Added: `function-url-quotes` rule. 115 | - Added: `hue-degree-notation` rule. 116 | - Added: `keyframes-name-pattern` rule. 117 | - Added: `max-line-length` rule. 118 | - Added: `media-feature-name-no-vendor-prefix` rule. 119 | - Added: `no-empty-first-line` rule. 120 | - Added: `no-irregular-whitespace` rule. 121 | - Added: `number-max-precision` rule. 122 | - Added: `property-no-vendor-prefix` rule. 123 | - Added: `selector-attribute-quotes` rule. 124 | - Added: `selector-class-pattern` rule. 125 | - Added: `selector-id-pattern` rule. 126 | - Added: `selector-no-vendor-prefix` rule. 127 | - Added: `shorthand-property-no-redundant-values` rule. 128 | - Added: `string-quotes` rule. 129 | - Added: `value-no-vendor-prefix` rule. 130 | 131 | ## 22.0.0 132 | 133 | - Removed: `stylelint` less than `13.13.0` from peer dependencies. 134 | - Changed: updated to [`stylelint-config-recommended@5.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/5.0.0). 135 | 136 | ## 21.0.0 137 | 138 | - Removed: `stylelint` less than `13.12.0` from peer dependencies. 139 | - Changed: updated to [`stylelint-config-recommended@4.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/4.0.0). 140 | 141 | ## 20.0.0 142 | 143 | - Added: `value-keyword-case` rule. 144 | 145 | ## 19.0.0 146 | 147 | - Removed: `stylelint` < 10.1.0 from peer dependencies. `stylelint@10.1.0+` is required now. 148 | - Changed: updated to [`stylelint-config-recommended@3.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/3.0.0). 149 | 150 | ## 18.3.0 151 | 152 | - Added: `stylelint@10` to peer dependency range. 153 | 154 | ## 18.2.0 155 | 156 | - Added: `stylelint-config-recommended@2.1.0` as dependency 157 | 158 | ## 18.1.0 159 | 160 | - Added: `stylelint@9` to peer dependency range. 161 | 162 | ## 18.0.0 163 | 164 | - Changed: updated to [`stylelint-config-recommended@2.0.0`](https://github.com/stylelint/stylelint-config-recommended/releases/tag/2.0.0). 165 | 166 | ## 17.0.0 167 | 168 | - Changed: now extends [`stylelint-config-recommended`](https://github.com/stylelint/stylelint-config-recommended), which turns on the `at-rule-no-unknown` rule. Therefore, if you use non-standard at-rules, like those introduced in SCSS and Less (e.g. `@extends`, `@includes` etc), be sure to [extend the config](README.md#extending-the-config) and make use of `at-rule-no-unknown`'s [`ignoreAtRules: []` secondary option](https://github.com/stylelint/stylelint/tree/main/lib/rules/at-rule-no-unknown#ignoreatrules-regex-string). 169 | 170 | ## 16.0.0 171 | 172 | - Changed: replaced the deprecated `rule-nested-empty-line-before` and `rule-non-nested-empty-line-before` rules with the `rule-empty-line-before` rule. 173 | 174 | ## 15.0.1 175 | 176 | - Fixed: URLs to stylelint rules within README. 177 | 178 | ## 15.0.0 179 | 180 | - Removed: `declaration-block-no-ignored-properties` rule. 181 | - Removed: `media-feature-no-missing-punctuation` rule. 182 | - Removed: `selector-no-empty` rule. 183 | - Added: `font-family-no-duplicate-names` rule. 184 | 185 | ## 14.0.0 186 | 187 | - Added: `selector-no-empty` rule. 188 | 189 | ## 13.0.2 190 | 191 | - Fixed: the `ignore: ["consecutive-duplicates-with-different-values"` optional secondary option was wrongly assigned to `declaration-block-no-ignored-properties`. 192 | 193 | ## 13.0.1 194 | 195 | - Fixed: `declaration-block-no-duplicate-properties` now uses the `ignore: ["consecutive-duplicates-with-different-values"` optional secondary option. 196 | 197 | ## 13.0.0 198 | 199 | - Added: `declaration-block-no-duplicate-properties` rule. 200 | - Added: `declaration-block-no-redundant-longhand-properties` rule. 201 | - Added: `media-feature-name-no-unknown` rule. 202 | - Added: `property-no-unknown` rule. 203 | - Added: `selector-descendant-combinator-no-non-space` rule. 204 | - Added: `value-list-max-empty-lines` rule. 205 | 206 | ## 12.0.0 207 | 208 | - Changed: `at-rule-empty-line-before` now uses the `blockless-after-same-name-blockless` `except` option, rather than the `blockless-group` one. 209 | - Added: `block-closing-brace-empty-line-before` rule. 210 | - Added: `comment-no-empty` rule. 211 | - Added: `custom-property-empty-line-before` rule. 212 | - Added: `declaration-empty-line-before` rule. 213 | - Added: `media-feature-name-case` rule. 214 | - Added: `rule-nested-empty-line-before` rule. 215 | 216 | ## 11.0.0 217 | 218 | - Removed: `at-rule-no-unknown` rule. 219 | - Removed: `media-feature-parentheses-space-inside` rule. 220 | - Removed: `no-missing-eof-newline` rule. 221 | - Changed: `indentation` no longer uses the `indentInsideParens: "once"` option, as this is the default behaviour in `stylelint@7.0.0`. 222 | - Added: `media-feature-parentheses-space-inside` rule. 223 | - Added: `no-missing-end-of-source-newline` rule. 224 | 225 | ## 10.0.0 226 | 227 | - Changed: `indentation` now uses the `indentInsideParens: "once"` option. 228 | - Added: `at-rule-no-unknown` rule. 229 | - Added: `no-empty-source` rule. 230 | 231 | ## 9.0.0 232 | 233 | - Removed: `number-zero-length-no-unit` rule. 234 | - Added: `length-zero-no-unit` rule. 235 | 236 | ## 8.0.0 237 | 238 | - Added: `keyframe-declaration-no-important` rule. 239 | - Added: `selector-pseudo-class-no-unknown` rule. 240 | - Added: `selector-type-no-unknown` rule. 241 | 242 | ## 7.0.0 243 | 244 | - Added: `at-rule-name-space-after` rule. 245 | - Added: `function-max-empty-lines` rule. 246 | - Added: `no-extra-semicolons` rule. 247 | - Added: `selector-attribute-brackets-space-inside` rule. 248 | - Added: `selector-attribute-operator-space-after` rule. 249 | - Added: `selector-attribute-operator-space-before` rule. 250 | - Added: `selector-max-empty-lines` rule. 251 | - Added: `selector-pseudo-class-parentheses-space-inside` rule. 252 | - Added: `selector-pseudo-element-no-unknown` rule. 253 | - Added: `shorthand-property-no-redundant-values` rule. 254 | 255 | ## 6.0.0 256 | 257 | - Added: `at-rule-name-case` rule. 258 | - Added: `at-rule-semicolon-newline-after` rule. 259 | - Added: `function-name-case` rule. 260 | - Added: `property-case` rule. 261 | - Added: `selector-pseudo-class-case` rule. 262 | - Added: `selector-pseudo-element-case` rule. 263 | - Added: `selector-type-case` rule. 264 | - Added: `unit-case` rule. 265 | - Added: `unit-no-unknown` rule. 266 | 267 | ## 5.0.0 268 | 269 | - Removed: `font-family-name-quotes`, `function-url-quotes` and `string-quotes` rules. 270 | - Added: `declaration-block-no-ignored-properties` rule. 271 | 272 | ## 4.0.1 273 | 274 | - Fixed: include peerDependencies in `package.json` to expose compatibility. 275 | 276 | ## 4.0.0 277 | 278 | - Removed: `stylelint < 4.5.0` compatibility. 279 | - Added: `font-family-name-quotes` rule with `"double-where-recommended"` option. 280 | - Added: `function-linear-gradient-no-nonstandard-direction` rule. 281 | - Added: `media-feature-no-missing-punctuation` rule. 282 | - Added: `no-invalid-double-slash-comments` rule. 283 | - Added: `string-no-newline` rule. 284 | 285 | ## 3.0.0 286 | 287 | - Changed: first-nested at-rules now behave the same as first-nested comments i.e. they can no longer be preceded by an empty line. 288 | 289 | ## 2.0.0 290 | 291 | - Changed: first-nested comments can no longer be preceded by an empty line. 292 | - Fixed: `comment-empty-line-before` now ignores `stylelint` command comments. 293 | 294 | ## 1.0.0 295 | 296 | - Fixed: more forgiving empty lines rules when comments are present i.e. the `rule-non-nested-empty-line-before` and `at-rule-empty-line-before` now make use of the `ignore: ["after-comment"]` option. 297 | 298 | ## 0.2.0 299 | 300 | - Added: `block-no-empty` rule. 301 | 302 | ## 0.1.0 303 | 304 | - Initial release 305 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 - present stylelint authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stylelint-config-standard 2 | 3 | [![NPM version](https://img.shields.io/npm/v/stylelint-config-standard.svg)](https://www.npmjs.org/package/stylelint-config-standard) [![Build Status](https://github.com/stylelint/stylelint-config-standard/workflows/CI/badge.svg)](https://github.com/stylelint/stylelint-config-standard/actions) 4 | 5 | > The standard shareable config for Stylelint. 6 | 7 | It extends [`stylelint-config-recommended`](https://github.com/stylelint/stylelint-config-recommended) and turns on additional rules to enforce modern conventions found in the [CSS specifications](https://www.w3.org/Style/CSS/current-work) and within [Baseline Widely Available](https://web.dev/baseline). 8 | 9 | To see the rules that this config uses, please read the [config itself](./index.js). 10 | 11 | ## Example 12 | 13 | ```css 14 | @import url("foo.css"); 15 | @import url("bar.css"); 16 | 17 | @custom-media --foo (min-width: 30em); 18 | 19 | /** 20 | * Multi-line comment 21 | */ 22 | 23 | :root { 24 | --brand-red: hsl(5deg 10% 40%); 25 | } 26 | 27 | /* Single-line comment */ 28 | 29 | .class-foo:not(a, div) { 30 | margin: 0; 31 | top: calc(100% - 2rem); 32 | } 33 | 34 | /* Flush single line comment */ 35 | @media (width >= 60em) { 36 | #id-bar { 37 | /* Flush to parent comment */ 38 | --offset: 0px; 39 | 40 | color: #fff; 41 | font-family: Helvetica, "Arial Black", sans-serif; 42 | left: calc(var(--offset) + 50%); 43 | } 44 | 45 | /* Flush nested single line comment */ 46 | a::after { 47 | display: block; 48 | content: "→"; 49 | background-image: url("x.svg"); 50 | } 51 | } 52 | 53 | @keyframes fade-in { 54 | from { 55 | opacity: 0; 56 | } 57 | 58 | to { 59 | opacity: 1; 60 | } 61 | } 62 | ``` 63 | 64 | _Note: the config is tested against this example, as such the example contains plenty of CSS syntax and features._ 65 | 66 | ## Installation 67 | 68 | ```bash 69 | npm install stylelint-config-standard --save-dev 70 | ``` 71 | 72 | ## Usage 73 | 74 | Set your Stylelint config to: 75 | 76 | ```json 77 | { 78 | "extends": "stylelint-config-standard" 79 | } 80 | ``` 81 | 82 | ### Extending the config 83 | 84 | Add a `"rules"` key to your config, then add your overrides and additions there. 85 | 86 | You can turn off rules by setting its value to `null`. For example: 87 | 88 | ```json 89 | { 90 | "extends": "stylelint-config-standard", 91 | "rules": { 92 | "selector-class-pattern": null 93 | } 94 | } 95 | ``` 96 | 97 | Or lower the severity of a rule to a warning using the `severity` secondary option. For example: 98 | 99 | ```json 100 | { 101 | "extends": "stylelint-config-standard", 102 | "rules": { 103 | "property-no-vendor-prefix": [ 104 | true, 105 | { 106 | "severity": "warning" 107 | } 108 | ] 109 | } 110 | } 111 | ``` 112 | 113 | Or to add a rule, For example, the `unit-allowed-list` one: 114 | 115 | ```json 116 | { 117 | "extends": "stylelint-config-standard", 118 | "rules": { 119 | "unit-allowed-list": ["em", "rem", "s"] 120 | } 121 | } 122 | ``` 123 | 124 | We recommend adding more of [Stylelint's rules](https://stylelint.io/user-guide/rules/) to your config as these rules need to be configured to suit your specific needs. 125 | 126 | ## [Changelog](CHANGELOG.md) 127 | 128 | ## [License](LICENSE) 129 | -------------------------------------------------------------------------------- /__tests__/engines.test.mjs: -------------------------------------------------------------------------------- 1 | import { describe, it } from 'node:test'; // eslint-disable-line n/no-unsupported-features/node-builtins 2 | import assert from 'node:assert/strict'; 3 | import { execFileSync } from 'node:child_process'; 4 | import { readFileSync } from 'node:fs'; 5 | 6 | const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')); 7 | 8 | describe('engines.node', () => { 9 | it("is the same as stylelint's one", () => { 10 | const stylelintVersion = pkg.peerDependencies.stylelint; 11 | const nodeVersion = JSON.parse( 12 | execFileSync('npm', [ 13 | 'view', 14 | '--json', 15 | `stylelint@${stylelintVersion}`, 16 | 'engines.node', 17 | ]).toString(), 18 | ); 19 | 20 | // `^x.y.z` range can return multiple versions. 21 | const nodeVersions = Array.isArray(nodeVersion) ? [...new Set(nodeVersion)] : [nodeVersion]; 22 | 23 | assert.equal(nodeVersions.length, 1); 24 | assert.ok(nodeVersions.includes(pkg.engines.node)); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /__tests__/index.test.mjs: -------------------------------------------------------------------------------- 1 | import { beforeEach, describe, it } from 'node:test'; // eslint-disable-line n/no-unsupported-features/node-builtins 2 | import assert from 'node:assert/strict'; 3 | import fs from 'node:fs'; 4 | 5 | import stylelint from 'stylelint'; 6 | 7 | import config from '../index.js'; 8 | 9 | describe('flags no warnings with valid css', () => { 10 | const validCss = fs.readFileSync('./__tests__/valid.css', 'utf-8'); 11 | let result; 12 | 13 | beforeEach(async () => { 14 | result = await stylelint.lint({ 15 | code: validCss, 16 | config, 17 | }); 18 | }); 19 | 20 | it('did not error', () => { 21 | assert.equal(result.errored, false); 22 | }); 23 | 24 | it('flags no warnings', () => { 25 | assert.equal(result.results[0].warnings.length, 0); 26 | }); 27 | }); 28 | 29 | describe('flags warnings with invalid css', () => { 30 | const invalidCss = fs.readFileSync('./__tests__/invalid.css', 'utf-8'); 31 | let result; 32 | 33 | beforeEach(async () => { 34 | result = await stylelint.lint({ 35 | code: invalidCss, 36 | config, 37 | }); 38 | }); 39 | 40 | it('did error', () => { 41 | assert.equal(result.errored, true); 42 | }); 43 | 44 | it('flags warnings', () => { 45 | assert.equal(result.results[0].warnings.length, 5); 46 | }); 47 | 48 | it('correct warning text', () => { 49 | assert.deepEqual( 50 | result.results[0].warnings.map((w) => w.text), 51 | [ 52 | 'Expected custom media query name "--FOO" to be kebab-case', 53 | 'Expected custom property name "--FOO" to be kebab-case', 54 | 'Expected keyframe name "FOO" to be kebab-case', 55 | 'Expected class selector ".FOO" to be kebab-case', 56 | 'Expected id selector "#FOO" to be kebab-case', 57 | ], 58 | ); 59 | }); 60 | 61 | it('correct rule flagged', () => { 62 | assert.deepEqual( 63 | result.results[0].warnings.map((w) => w.rule), 64 | [ 65 | 'custom-media-pattern', 66 | 'custom-property-pattern', 67 | 'keyframes-name-pattern', 68 | 'selector-class-pattern', 69 | 'selector-id-pattern', 70 | ], 71 | ); 72 | }); 73 | 74 | it('correct severity flagged', () => { 75 | assert.equal(result.results[0].warnings[0].severity, 'error'); 76 | }); 77 | 78 | it('correct line number', () => { 79 | assert.equal(result.results[0].warnings[0].line, 1); 80 | }); 81 | 82 | it('correct column number', () => { 83 | assert.equal(result.results[0].warnings[0].column, 15); 84 | }); 85 | }); 86 | 87 | describe('deprecated rules are excluded', () => { 88 | const ruleNames = Object.keys(config.rules); 89 | 90 | it('is not empty', () => { 91 | assert.ok(ruleNames.length > 0); 92 | }); 93 | 94 | for (const ruleName of ruleNames) { 95 | it(`${ruleName}`, async () => { 96 | const rule = await stylelint.rules[ruleName]; 97 | 98 | assert.ok(!rule.meta.deprecated); 99 | }); 100 | } 101 | }); 102 | -------------------------------------------------------------------------------- /__tests__/invalid.css: -------------------------------------------------------------------------------- 1 | @custom-media --FOO; 2 | 3 | :root { 4 | --FOO: 1px; 5 | } 6 | 7 | @keyframes FOO { 8 | /* ... */ 9 | } 10 | 11 | .FOO { 12 | /* ... */ 13 | } 14 | 15 | #FOO { 16 | /* ... */ 17 | } 18 | -------------------------------------------------------------------------------- /__tests__/valid.css: -------------------------------------------------------------------------------- 1 | @import url('foo.css'); 2 | @import url('bar.css'); 3 | 4 | @custom-media --foo (min-width: 30em); 5 | 6 | /** 7 | * Multi-line comment 8 | */ 9 | 10 | :root { 11 | --brand-red: hsl(5deg 10% 40%); 12 | } 13 | 14 | /* Single-line comment */ 15 | 16 | .class-foo:not(a, div) { 17 | margin: 0; 18 | top: calc(100% - 2rem); 19 | } 20 | 21 | /* Flush single line comment */ 22 | @media (width >= 60em) { 23 | #id-bar { 24 | /* Flush to parent comment */ 25 | --offset: 0px; 26 | 27 | color: #fff; 28 | font-family: Helvetica, 'Arial Black', sans-serif; 29 | left: calc(var(--offset) + 50%); 30 | } 31 | 32 | /* Flush nested single line comment */ 33 | a::after { 34 | display: block; 35 | content: '→'; 36 | background-image: url('x.svg'); 37 | } 38 | } 39 | 40 | @keyframes fade-in { 41 | from { 42 | opacity: 0; 43 | } 44 | 45 | to { 46 | opacity: 1; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import stylelintConfig from 'eslint-config-stylelint'; 2 | 3 | export default [...stylelintConfig]; 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'stylelint-config-recommended', 5 | rules: { 6 | 'alpha-value-notation': [ 7 | 'percentage', 8 | { 9 | exceptProperties: [ 10 | 'opacity', 11 | 'fill-opacity', 12 | 'flood-opacity', 13 | 'stop-opacity', 14 | 'stroke-opacity', 15 | ], 16 | }, 17 | ], 18 | 'at-rule-empty-line-before': [ 19 | 'always', 20 | { 21 | except: ['blockless-after-same-name-blockless', 'first-nested'], 22 | ignore: ['after-comment'], 23 | }, 24 | ], 25 | 'at-rule-no-vendor-prefix': true, 26 | 'color-function-alias-notation': 'without-alpha', 27 | 'color-function-notation': 'modern', 28 | 'color-hex-length': 'short', 29 | 'comment-empty-line-before': [ 30 | 'always', 31 | { 32 | except: ['first-nested'], 33 | ignore: ['stylelint-commands'], 34 | }, 35 | ], 36 | 'comment-whitespace-inside': 'always', 37 | 'container-name-pattern': [ 38 | '^(--)?([a-z][a-z0-9]*)(-[a-z0-9]+)*$', 39 | { 40 | message: (name) => `Expected container name "${name}" to be kebab-case`, 41 | }, 42 | ], 43 | 'custom-property-empty-line-before': [ 44 | 'always', 45 | { 46 | except: ['after-custom-property', 'first-nested'], 47 | ignore: ['after-comment', 'inside-single-line-block'], 48 | }, 49 | ], 50 | 'custom-media-pattern': [ 51 | '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$', 52 | { 53 | message: (name) => `Expected custom media query name "${name}" to be kebab-case`, 54 | }, 55 | ], 56 | 'custom-property-pattern': [ 57 | '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$', 58 | { 59 | message: (name) => `Expected custom property name "${name}" to be kebab-case`, 60 | }, 61 | ], 62 | 'declaration-block-no-redundant-longhand-properties': true, 63 | 'declaration-block-single-line-max-declarations': 1, 64 | 'declaration-empty-line-before': [ 65 | 'always', 66 | { 67 | except: ['after-declaration', 'first-nested'], 68 | ignore: ['after-comment', 'inside-single-line-block'], 69 | }, 70 | ], 71 | 'font-family-name-quotes': 'always-where-recommended', 72 | 'function-name-case': 'lower', 73 | 'function-url-quotes': 'always', 74 | 'hue-degree-notation': 'angle', 75 | 'import-notation': 'url', 76 | 'keyframe-selector-notation': 'percentage-unless-within-keyword-only-block', 77 | 'keyframes-name-pattern': [ 78 | '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$', 79 | { 80 | message: (name) => `Expected keyframe name "${name}" to be kebab-case`, 81 | }, 82 | ], 83 | 'layer-name-pattern': [ 84 | '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$', 85 | { 86 | message: (name) => `Expected layer name "${name}" to be kebab-case`, 87 | }, 88 | ], 89 | 'length-zero-no-unit': [ 90 | true, 91 | { 92 | ignore: ['custom-properties'], 93 | }, 94 | ], 95 | 'lightness-notation': 'percentage', 96 | 'media-feature-name-no-vendor-prefix': true, 97 | 'media-feature-range-notation': 'context', 98 | 'number-max-precision': 4, 99 | 'property-no-vendor-prefix': true, 100 | 'rule-empty-line-before': [ 101 | 'always-multi-line', 102 | { 103 | except: ['first-nested'], 104 | ignore: ['after-comment'], 105 | }, 106 | ], 107 | 'selector-attribute-quotes': 'always', 108 | 'selector-class-pattern': [ 109 | '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$', 110 | { 111 | message: (selector) => `Expected class selector "${selector}" to be kebab-case`, 112 | }, 113 | ], 114 | 'selector-id-pattern': [ 115 | '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$', 116 | { 117 | message: (selector) => `Expected id selector "${selector}" to be kebab-case`, 118 | }, 119 | ], 120 | 'selector-no-vendor-prefix': true, 121 | 'selector-not-notation': 'complex', 122 | 'selector-pseudo-element-colon-notation': 'double', 123 | 'selector-type-case': 'lower', 124 | 'shorthand-property-no-redundant-values': true, 125 | 'value-keyword-case': 'lower', 126 | 'value-no-vendor-prefix': [ 127 | true, 128 | { 129 | // `-webkit-box` is allowed as standard. See https://www.w3.org/TR/css-overflow-3/#webkit-line-clamp 130 | ignoreValues: ['box', 'inline-box'], 131 | }, 132 | ], 133 | }, 134 | }; 135 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stylelint-config-standard", 3 | "version": "38.0.0", 4 | "description": "Standard shareable config for Stylelint", 5 | "keywords": [ 6 | "stylelint", 7 | "stylelint-config", 8 | "standard" 9 | ], 10 | "repository": "stylelint/stylelint-config-standard", 11 | "funding": [ 12 | { 13 | "type": "opencollective", 14 | "url": "https://opencollective.com/stylelint" 15 | }, 16 | { 17 | "type": "github", 18 | "url": "https://github.com/sponsors/stylelint" 19 | } 20 | ], 21 | "license": "MIT", 22 | "author": "Stylelint", 23 | "main": "index.js", 24 | "files": [ 25 | "index.js" 26 | ], 27 | "scripts": { 28 | "format": "prettier . --write", 29 | "prepare": "husky", 30 | "lint:formatting": "prettier . --check", 31 | "lint:js": "eslint", 32 | "lint:md": "remark . --quiet --frail --ignore-path .gitignore", 33 | "lint": "npm-run-all --parallel lint:*", 34 | "release": "np --no-release-draft", 35 | "pretest": "npm run lint", 36 | "test": "node --test", 37 | "watch": "npm test --ignore-scripts -- --watch" 38 | }, 39 | "lint-staged": { 40 | "*.js": "eslint --cache --fix", 41 | "*.{js,md,yml}": "prettier --write" 42 | }, 43 | "prettier": "@stylelint/prettier-config", 44 | "remarkConfig": { 45 | "plugins": [ 46 | "@stylelint/remark-preset" 47 | ] 48 | }, 49 | "dependencies": { 50 | "stylelint-config-recommended": "^16.0.0" 51 | }, 52 | "devDependencies": { 53 | "@stylelint/prettier-config": "^3.0.0", 54 | "@stylelint/remark-preset": "^5.1.1", 55 | "eslint": "^9.28.0", 56 | "eslint-config-stylelint": "^24.0.0", 57 | "husky": "^9.1.7", 58 | "lint-staged": "^16.1.0", 59 | "np": "^10.2.0", 60 | "npm-run-all": "^4.1.5", 61 | "prettier": "^3.5.3", 62 | "remark-cli": "^12.0.1", 63 | "stylelint": "^16.18.0" 64 | }, 65 | "peerDependencies": { 66 | "stylelint": "^16.18.0" 67 | }, 68 | "engines": { 69 | "node": ">=18.12.0" 70 | } 71 | } 72 | --------------------------------------------------------------------------------