├── .gitattributes ├── .npmrc ├── .husky └── pre-commit ├── .gitignore ├── __tests__ ├── invalid.css ├── valid.css ├── engines.test.mjs └── index.test.mjs ├── eslint.config.mjs ├── .editorconfig ├── .github ├── CODEOWNERS ├── workflows │ ├── release.yml │ ├── nodejs.yml │ └── release-pr.yml └── dependabot.yml ├── LICENSE ├── README.md ├── package.json ├── index.js └── CHANGELOG.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix = "" 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx --no lint-staged 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .eslintcache 3 | -------------------------------------------------------------------------------- /__tests__/invalid.css: -------------------------------------------------------------------------------- 1 | madeup { 2 | top: 0; 3 | } 4 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import stylelintConfig from 'eslint-config-stylelint'; 2 | 3 | export default [...stylelintConfig]; 4 | -------------------------------------------------------------------------------- /__tests__/valid.css: -------------------------------------------------------------------------------- 1 | @import url(x.css); 2 | 3 | .selector-1, 4 | .selector-2, 5 | .selector-3[type='text'] { 6 | background: linear-gradient(#fff, rgba(0, 0, 0, 0.8)); 7 | box-sizing: border-box; 8 | display: block; 9 | color: #333; 10 | } 11 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://help.github.com/articles/about-codeowners/ 2 | 3 | # All proposed file changes under the `.github/` folder must be approved by the owners team prior to merging. 4 | .github/ @stylelint/owners 5 | 6 | # Require approvals by the owners team when updating dependencies or package metadata. 7 | package.json @stylelint/owners 8 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | types: [closed] 7 | 8 | jobs: 9 | release: 10 | uses: stylelint/.github/.github/workflows/call-release.yml@f34faf02df201c8c204df4ae2543523ad54ceb07 # 0.3.1 11 | permissions: 12 | contents: write 13 | id-token: write 14 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | lint: 11 | uses: stylelint/.github/.github/workflows/call-lint.yml@f34faf02df201c8c204df4ae2543523ad54ceb07 # 0.3.1 12 | permissions: 13 | contents: read 14 | 15 | test: 16 | uses: stylelint/.github/.github/workflows/call-test.yml@f34faf02df201c8c204df4ae2543523ad54ceb07 # 0.3.1 17 | permissions: 18 | contents: read 19 | -------------------------------------------------------------------------------- /.github/workflows/release-pr.yml: -------------------------------------------------------------------------------- 1 | name: Release PR 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | new-version: 7 | description: New version to release 8 | required: true 9 | type: choice 10 | options: [patch, minor, major] 11 | 12 | jobs: 13 | release-pr: 14 | uses: stylelint/.github/.github/workflows/call-release-pr.yml@f34faf02df201c8c204df4ae2543523ad54ceb07 # 0.3.1 15 | with: 16 | new-version: ${{ github.event.inputs.new-version }} 17 | permissions: 18 | contents: write 19 | pull-requests: write 20 | -------------------------------------------------------------------------------- /.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 | dev-deps: 13 | dependency-type: development 14 | exclude-patterns: ['stylelint'] 15 | ignore: 16 | - dependency-name: 'stylelint' # Avoid updating the peer dependency. 17 | cooldown: 18 | default-days: 7 19 | 20 | - package-ecosystem: github-actions 21 | directory: '/' 22 | schedule: 23 | interval: monthly 24 | open-pull-requests-limit: 3 25 | labels: 26 | - 'pr: dependencies' 27 | cooldown: 28 | default-days: 7 29 | -------------------------------------------------------------------------------- /__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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 - present stylelint 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stylelint-config-recommended 2 | 3 | [![NPM version](https://img.shields.io/npm/v/stylelint-config-recommended.svg)](https://www.npmjs.org/package/stylelint-config-recommended) [![Build Status](https://github.com/stylelint/stylelint-config-recommended/workflows/CI/badge.svg)](https://github.com/stylelint/stylelint-config-recommended/actions) 4 | 5 | > The recommended shareable config for Stylelint. 6 | 7 | It turns on most of the Stylelint rules that help you [_avoid errors_](https://stylelint.io/user-guide/rules/#avoid-errors). 8 | 9 | You can use this as a foundation for your own config, but we suggest most people use our [standard config](https://www.npmjs.com/package/stylelint-config-standard) instead which extends this config and adds a few more rules to enforce common conventions. 10 | 11 | ## Installation 12 | 13 | ```bash 14 | npm install stylelint-config-recommended --save-dev 15 | ``` 16 | 17 | ## Usage 18 | 19 | Set your `stylelint` config to: 20 | 21 | ```json 22 | { 23 | "extends": "stylelint-config-recommended" 24 | } 25 | ``` 26 | 27 | ### Extending the config 28 | 29 | Add a `"rules"` key to your config, then add your overrides and additions there. 30 | 31 | For example, to change the `at-rule-no-unknown` rule to use its `ignoreAtRules` option, turn off the `block-no-empty` rule, and add the `unit-allowed-list` rule: 32 | 33 | ```json 34 | { 35 | "extends": "stylelint-config-recommended", 36 | "rules": { 37 | "at-rule-no-unknown": [ 38 | true, 39 | { 40 | "ignoreAtRules": ["extends"] 41 | } 42 | ], 43 | "block-no-empty": null, 44 | "unit-allowed-list": ["em", "rem", "s"] 45 | } 46 | } 47 | ``` 48 | 49 | ## [Changelog](CHANGELOG.md) 50 | 51 | ## [License](LICENSE) 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stylelint-config-recommended", 3 | "version": "17.0.0", 4 | "description": "Recommended shareable config for Stylelint", 5 | "keywords": [ 6 | "stylelint", 7 | "stylelint-config", 8 | "recommended" 9 | ], 10 | "repository": "stylelint/stylelint-config-recommended", 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 | "lint": "npm-run-all --parallel lint:*", 30 | "lint:formatting": "prettier . --check", 31 | "lint:js": "eslint", 32 | "lint:md": "remark . --quiet --frail --ignore-path .gitignore", 33 | "prepare": "husky", 34 | "pretest": "npm run lint", 35 | "test": "node --test", 36 | "watch": "npm test --ignore-scripts -- --watch" 37 | }, 38 | "lint-staged": { 39 | "*.js": "eslint --cache --fix", 40 | "*.{js,md,yml}": "prettier --write" 41 | }, 42 | "prettier": "@stylelint/prettier-config", 43 | "remarkConfig": { 44 | "plugins": [ 45 | "@stylelint/remark-preset" 46 | ] 47 | }, 48 | "devDependencies": { 49 | "@stylelint/prettier-config": "^4.0.0", 50 | "@stylelint/remark-preset": "^5.1.1", 51 | "eslint": "^9.39.1", 52 | "eslint-config-stylelint": "^25.0.1", 53 | "husky": "^9.1.7", 54 | "lint-staged": "^16.2.7", 55 | "npm-run-all": "^4.1.5", 56 | "prettier": "^3.6.2", 57 | "remark-cli": "^12.0.1", 58 | "stylelint": "^16.23.0" 59 | }, 60 | "peerDependencies": { 61 | "stylelint": "^16.23.0" 62 | }, 63 | "engines": { 64 | "node": ">=18.12.0" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | rules: { 5 | 'annotation-no-unknown': true, 6 | 'at-rule-descriptor-no-unknown': true, 7 | 'at-rule-descriptor-value-no-unknown': true, 8 | 'at-rule-no-deprecated': true, 9 | 'at-rule-no-unknown': true, 10 | 'at-rule-prelude-no-invalid': [true, { ignoreAtRules: ['media'] }], 11 | 'block-no-empty': true, 12 | 'comment-no-empty': true, 13 | 'custom-property-no-missing-var-function': true, 14 | 'declaration-block-no-duplicate-custom-properties': true, 15 | 'declaration-block-no-duplicate-properties': [ 16 | true, 17 | { 18 | ignore: ['consecutive-duplicates-with-different-syntaxes'], 19 | }, 20 | ], 21 | 'declaration-block-no-shorthand-property-overrides': true, 22 | 'declaration-property-value-keyword-no-deprecated': true, 23 | 'declaration-property-value-no-unknown': true, 24 | 'font-family-no-duplicate-names': true, 25 | 'font-family-no-missing-generic-family-keyword': true, 26 | 'function-calc-no-unspaced-operator': true, 27 | 'keyframe-block-no-duplicate-selectors': true, 28 | 'keyframe-declaration-no-important': true, 29 | 'media-feature-name-no-unknown': true, 30 | 'media-feature-name-value-no-unknown': true, 31 | 'media-query-no-invalid': true, 32 | 'media-type-no-deprecated': true, 33 | 'named-grid-areas-no-invalid': true, 34 | 'nesting-selector-no-missing-scoping-root': true, 35 | 'no-descending-specificity': true, 36 | 'no-duplicate-at-import-rules': true, 37 | 'no-duplicate-selectors': true, 38 | 'no-empty-source': true, 39 | 'no-invalid-double-slash-comments': true, 40 | 'no-invalid-position-at-import-rule': true, 41 | 'no-invalid-position-declaration': true, 42 | 'no-irregular-whitespace': true, 43 | 'property-no-deprecated': true, 44 | 'property-no-unknown': true, 45 | 'selector-anb-no-unmatchable': true, 46 | 'selector-pseudo-class-no-unknown': true, 47 | 'selector-pseudo-element-no-unknown': true, 48 | 'selector-type-no-unknown': [ 49 | true, 50 | { 51 | ignore: ['custom-elements'], 52 | }, 53 | ], 54 | 'string-no-newline': [true, { ignore: ['at-rule-preludes', 'declaration-values'] }], 55 | 'syntax-string-no-invalid': true, 56 | }, 57 | }; 58 | -------------------------------------------------------------------------------- /__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 { readFileSync } from 'node:fs'; 4 | 5 | import stylelint from 'stylelint'; 6 | 7 | import config from '../index.js'; 8 | 9 | const validCss = readFileSync('./__tests__/valid.css', 'utf-8'); 10 | const invalidCss = readFileSync('./__tests__/invalid.css', 'utf-8'); 11 | 12 | describe('flags no warnings with valid css', () => { 13 | let result; 14 | 15 | beforeEach(async () => { 16 | result = await stylelint.lint({ 17 | code: validCss, 18 | config, 19 | }); 20 | }); 21 | 22 | it('has no errors', () => { 23 | assert.equal(result.errored, false); 24 | }); 25 | 26 | it('flags no warnings', () => { 27 | assert.equal(result.results[0].warnings.length, 0); 28 | }); 29 | }); 30 | 31 | describe('flags warnings with invalid css', () => { 32 | let result; 33 | 34 | beforeEach(async () => { 35 | result = await stylelint.lint({ 36 | code: invalidCss, 37 | config, 38 | }); 39 | }); 40 | 41 | it('includes an error', () => { 42 | assert.equal(result.errored, true); 43 | }); 44 | 45 | it('flags one warning', () => { 46 | assert.equal(result.results[0].warnings.length, 1); 47 | }); 48 | 49 | it('corrects warning text', () => { 50 | assert.equal( 51 | result.results[0].warnings[0].text, 52 | 'Unexpected unknown type selector "madeup" (selector-type-no-unknown)', 53 | ); 54 | }); 55 | 56 | it('corrects rule flagged', () => { 57 | assert.equal(result.results[0].warnings[0].rule, 'selector-type-no-unknown'); 58 | }); 59 | 60 | it('corrects severity flagged', () => { 61 | assert.equal(result.results[0].warnings[0].severity, 'error'); 62 | }); 63 | 64 | it('corrects line number', () => { 65 | assert.equal(result.results[0].warnings[0].line, 1); 66 | }); 67 | 68 | it('corrects column number', () => { 69 | assert.equal(result.results[0].warnings[0].column, 1); 70 | }); 71 | }); 72 | 73 | describe('deprecated rules are excluded', () => { 74 | const ruleNames = Object.keys(config.rules); 75 | 76 | it('is not empty', () => { 77 | assert.ok(ruleNames.length > 0); 78 | }); 79 | 80 | for (const ruleName of ruleNames) { 81 | it(`${ruleName}`, async () => { 82 | const rule = await stylelint.rules[ruleName]; 83 | 84 | assert.ok(!rule.meta.deprecated); 85 | }); 86 | } 87 | }); 88 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 17.0.0 4 | 5 | - Removed: `stylelint` less than `16.23.0` from peer dependencies. 6 | - Added: `media-type-no-deprecated` rule. 7 | - Added: `nesting-selector-no-missing-scoping-root` rule. 8 | - Added: `no-invalid-position-declaration` rule. 9 | - Added: `property-no-deprecated` rule. 10 | 11 | ## 16.0.0 12 | 13 | - Removed: `stylelint` less than `16.16.0` from peer dependencies. 14 | - Added: `syntax-string-no-invalid` rule. 15 | 16 | ## 15.0.0 17 | 18 | This release replaces four rules with broader and more capable ones that validate CSS against syntaxes defined by W3C: `at-rule-descriptor-value-no-unknown`, `at-rule-prelude-no-invalid` and `declaration-property-value-no-unknown`. 19 | 20 | - Removed: `stylelint` less than `16.13.0` from peer dependencies. 21 | - Removed: `color-no-invalid-hex` rule. 22 | - Removed: `function-linear-gradient-no-nonstandard-direction` rule. 23 | - Removed: `function-no-unknown` rule. 24 | - Removed: `unit-no-unknown` rule. 25 | - Changed: `string-no-newline` rule option to `{ ignore: ['at-rule-preludes', 'declaration-values'] }]`. 26 | - Added: `at-rule-descriptor-no-unknown` rule. 27 | - Added: `at-rule-descriptor-value-no-unknown` rule. 28 | - Added: `at-rule-no-deprecated` rule. 29 | - Added: `at-rule-prelude-no-invalid` rule. 30 | - Added: `declaration-property-value-keyword-no-deprecated` rule. 31 | - Added: `declaration-property-value-no-unknown` rule. 32 | - Added: `media-feature-name-value-no-unknown` rule. 33 | 34 | ## 14.0.1 35 | 36 | - Fixed: missing `funding` field in `package.json`. 37 | 38 | ## 14.0.0 39 | 40 | - Removed: `stylelint` less than `16.0.0` from peer dependencies. 41 | - Removed: Node.js less than `18.12.0` support. 42 | 43 | ## 13.0.0 44 | 45 | - Removed: `stylelint` less than `15.10.0` from peer dependencies. 46 | - Added: `media-query-no-invalid` rule. 47 | 48 | ## 12.0.0 49 | 50 | - Removed: `stylelint` less than `15.5.0` from peer dependencies. 51 | - Changed: `declaration-block-no-duplicate-properties` option to `ignore: ['consecutive-duplicates-with-different-syntaxes']`. 52 | 53 | ## 11.0.0 54 | 55 | - Removed: `stylelint` less than `15.3.0` from peer dependencies. 56 | - Added: `selector-anb-no-unmatchable` rule. 57 | 58 | ## 10.0.1 59 | 60 | - Removed: deprecated `no-extra-semicolons` rule. 61 | 62 | ## 10.0.0 63 | 64 | - Removed: `stylelint` less than `15.0.0` from peer dependencies. 65 | 66 | ## 9.0.0 67 | 68 | - Removed: `stylelint` less than `14.10.0` from peer dependencies. 69 | - Added: `annotation-no-unknown` rule. 70 | 71 | ## 8.0.0 72 | 73 | - Removed: `stylelint` less than `14.8.0` from peer dependencies. 74 | - Added: `keyframe-block-no-duplicate-selectors` rule. 75 | 76 | ## 7.0.0 77 | 78 | - Removed: `stylelint` less than `14.4.0` from peer dependencies. 79 | - Added: `function-no-unknown` rule. 80 | 81 | ## 6.0.0 82 | 83 | - Removed: `stylelint` less than `14.0.0` from peer dependencies. 84 | - Removed: `function-calc-no-invalid` rule. 85 | - Added: `custom-property-no-missing-var-function` rule. 86 | 87 | ## 5.0.0 88 | 89 | - Removed: `stylelint` less than `13.13.0` from peer dependencies. 90 | - Added: `no-invalid-position-at-import-rule` rule. 91 | - Added: `no-irregular-whitespace` rule. 92 | - Fixed: `selector-type-no-unknown` for custom elements. 93 | 94 | ## 4.0.0 95 | 96 | - Removed: `stylelint` less than `13.12.0` from peer dependencies. 97 | - Added: `declaration-block-no-duplicate-custom-properties` rule. 98 | - Added: `named-grid-areas-no-invalid` rule. 99 | 100 | ## 3.0.0 101 | 102 | - Removed: `stylelint` < 10.1.0 from peer dependencies. `stylelint@10.1.0+` is required now. 103 | - Added: `function-calc-no-invalid` rule. 104 | 105 | ## 2.2.0 106 | 107 | - Added: `stylelint@10` to peer dependency range. 108 | 109 | ## 2.1.0 110 | 111 | - Added: `stylelint@9` to peer dependency range. 112 | 113 | ## 2.0.1 114 | 115 | - Removed: `declaration-block-no-redundant-longhand-properties` rule. This time it is actually removed. 116 | 117 | ## 2.0.0 118 | 119 | - Removed: `declaration-block-no-redundant-longhand-properties` rule. 120 | - Removed: `shorthand-property-no-redundant-values` rule. 121 | - Added: `font-family-no-missing-generic-family-keyword` rule. 122 | - Added: `no-descending-specificity` rule. 123 | - Added: `no-duplicate-at-import-rules` rule. 124 | - Added: `no-duplicate-selectors` rule. 125 | 126 | ## 1.0.0 127 | 128 | - Use `stylelint@8`. 129 | 130 | ## 0.1.0 131 | 132 | - Initial release 133 | --------------------------------------------------------------------------------