Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/stylelint-config/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

### New Features

- Added a `project` preset holding the rules shared by Gutenberg and WordPress Core, so both projects can extend a single source instead of duplicating the set ([#81349](https://github.com/WordPress/gutenberg/issues/81349)).

## 24.1.0 (2026-07-29)

Expand Down
14 changes: 13 additions & 1 deletion packages/stylelint-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ If you've globally installed `@wordpress/stylelint-config` using the `-g` flag,

## Presets

In addition to the default preset, there is also a SCSS preset and 2 stylistic variant presets.
In addition to the default preset, there is also a SCSS preset, 2 stylistic variant presets, and a preset for the WordPress projects themselves.

### SCSS

Expand Down Expand Up @@ -62,6 +62,18 @@ This preset extends`@wordpress/stylelint-config`, `@wordpress/stylelint-config/s
}
```

### Project

This preset extends `@wordpress/stylelint-config/scss-stylistic` with the rules shared by the WordPress projects that lint their own stylesheets — Gutenberg and WordPress Core. Compared to `scss-stylistic` it turns off the stylistic and SCSS rules those codebases do not follow, adds accessibility rules for the `order` property and reversed `flex-direction` values, and relaxes `selector-class-pattern` to allow BEM-style class names.

Use it if you want to match how WordPress itself is linted. If you are configuring a theme or plugin, one of the presets above is likely a better fit.

```json
{
"extends": [ "@wordpress/stylelint-config/project" ]
}
```

## Extending the config

Simply add a `"rules"` key to your config and add your overrides there.
Expand Down
2 changes: 2 additions & 0 deletions packages/stylelint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"LICENSE",
"README.md",
"index.js",
"project.js",
"scss.js",
"scss-stylistic.js",
"stylistic.js"
Expand All @@ -36,6 +37,7 @@
"main": "index.js",
"exports": {
".": "./index.js",
"./project": "./project.js",
"./scss": "./scss.js",
"./scss-stylistic": "./scss-stylistic.js",
"./stylistic": "./stylistic.js",
Expand Down
71 changes: 71 additions & 0 deletions packages/stylelint-config/project.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { fileURLToPath } from 'node:url';

/**
* The set of rules shared by the WordPress projects that lint their own
* stylesheets — Gutenberg and WordPress Core.
*
* It layers on top of `scss-stylistic` and mostly does three things:
*
* - Turns off the stylistic and SCSS rules that the existing WordPress
* codebases do not follow, so the config can be adopted without a mass
* reformat.
* - Adds the accessibility rules that keep visual, reading, and DOM order in
* sync (`order`, reversed `flex-direction`).
* - Relaxes `selector-class-pattern` to allow BEM-style class names.
*
* Project-specific rules do not belong here. Extend this config and add them
* in the consuming project instead.
*
* @type {import('stylelint').Config}
*/
export default {
extends: [ './scss-stylistic' ].map( ( m ) =>
fileURLToPath( import.meta.resolve( m ) )
),
reportNeedlessDisables: true,
reportDescriptionlessDisables: true,
rules: {
'at-rule-empty-line-before': null,
'at-rule-no-unknown': null,
'comment-empty-line-before': null,
'declaration-property-value-allowed-list': [
{
'flex-direction': '/^(?!(row|column)-reverse).*$/',
},
{
message: ( property, value ) =>
`Avoid "${ value }" value for the "${ property }" property. For accessibility reasons, visual, reading, and DOM order must match. Only use the reverse values when they do not affect reading order, meaning, and interaction.`,
},
],
'font-weight-notation': null,
'@stylistic/max-line-length': null,
'no-descending-specificity': null,
'property-disallowed-list': [
[ 'order' ],
{
message:
'Avoid the order property. For accessibility reasons, visual, reading, and DOM order must match. Only use the order property when it does not affect reading order, meaning, and interaction.',
},
],
'rule-empty-line-before': null,
'selector-class-pattern': [
'^[a-z][a-z0-9]*(?:(?:__|--|-)[a-z0-9]+)*$',
{
message:
'Selector should use lowercase class segments separated with hyphens, double hyphens, or double underscores (selector-class-pattern)',
},
],
'value-keyword-case': null,
'scss/operator-no-unspaced': null,
'scss/selector-no-redundant-nesting-selector': null,
'scss/load-partial-extension': null,
'scss/no-global-function-names': null,
'scss/comment-no-empty': null,
'scss/at-extend-no-missing-placeholder': null,
'scss/operator-no-newline-after': null,
'scss/at-if-closing-brace-newline-after': null,
'scss/at-else-empty-line-before': null,
'scss/at-if-closing-brace-space-after': null,
'no-invalid-position-at-import-rule': null,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "@wordpress/stylelint-config/project"
}
24 changes: 24 additions & 0 deletions packages/stylelint-config/test/__snapshots__/project.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`flags warnings with invalid project scss snapshot matches warnings 1`] = `
[
{
"column": 18,
"endColumn": 29,
"endLine": 3,
"line": 3,
"rule": "declaration-property-value-allowed-list",
"severity": "error",
"text": "Avoid "row-reverse" value for the "flex-direction" property. For accessibility reasons, visual, reading, and DOM order must match. Only use the reverse values when they do not affect reading order, meaning, and interaction. (declaration-property-value-allowed-list)",
},
{
"column": 2,
"endColumn": 7,
"endLine": 4,
"line": 4,
"rule": "property-disallowed-list",
"severity": "error",
"text": "Avoid the order property. For accessibility reasons, visual, reading, and DOM order must match. Only use the order property when it does not affect reading order, meaning, and interaction. (property-disallowed-list)",
},
]
`;
5 changes: 5 additions & 0 deletions packages/stylelint-config/test/project-invalid.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Both declarations break the reading-order rules this preset adds.
.block-editor__inserter-menu {
flex-direction: row-reverse;
order: 1;
}
14 changes: 14 additions & 0 deletions packages/stylelint-config/test/project-valid.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// BEM-style class segments are allowed by this preset.
.block-editor__inserter-menu--open {
display: flex;
flex-direction: row;
}

// Rules this preset turns off relative to `scss-stylistic`.
.block-editor-inserter {
@if $condition {
color: #fff;
} @else {
color: #000;
}
}
46 changes: 46 additions & 0 deletions packages/stylelint-config/test/project.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const utils = require( './utils' );
const getStylelintResult = utils.getStylelintResult;

const CONFIG = './.stylelintrc.project.tests.json';

describe( 'flags no warnings with valid project scss', () => {
let result;

beforeEach( () => {
result = getStylelintResult( './project-valid.scss', CONFIG );
} );

it( 'did not error', () => {
return result.then( ( data ) => expect( data.errored ).toBeFalsy() );
} );

it( 'flags no warnings', () => {
return result.then( ( data ) =>
expect( data.results[ 0 ].warnings ).toHaveLength( 0 )
);
} );
} );

describe( 'flags warnings with invalid project scss', () => {
let result;

beforeEach( () => {
result = getStylelintResult( './project-invalid.scss', CONFIG );
} );

it( 'did error', () => {
return result.then( ( data ) => expect( data.errored ).toBeTruthy() );
} );

it( 'flags correct number of warnings', () => {
return result.then( ( data ) =>
expect( data.results[ 0 ].warnings ).toHaveLength( 2 )
);
} );

it( 'snapshot matches warnings', () => {
return result.then( ( data ) =>
expect( data.results[ 0 ].warnings ).toMatchSnapshot()
);
} );
} );
11 changes: 7 additions & 4 deletions packages/stylelint-config/test/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,23 @@ const stylelintBin = path.join(
'bin/stylelint.mjs'
);

const generateStylelintCommand = ( filename ) =>
const generateStylelintCommand = ( filename, configFile ) =>
'node ' +
stylelintBin +
' ' +
path.resolve( __dirname, '../', filename ) +
' -c' +
path.resolve( __dirname, '../', './.stylelintrc.tests.json' ) +
path.resolve( __dirname, '../', configFile ) +
' --formatter json' +
' --ignore-path ' +
path.resolve( __dirname, '../', './.stylelintignore' );

module.exports = {
getStylelintResult: ( filename ) =>
execute( generateStylelintCommand( filename ) )
getStylelintResult: (
filename,
configFile = './.stylelintrc.tests.json'
) =>
execute( generateStylelintCommand( filename, configFile ) )
.then( ( { stderr } ) => {
return {
errored: false,
Expand Down
55 changes: 5 additions & 50 deletions tools/stylelint/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,12 @@ const CSS_BASELINE_2024_FUNCTIONS = [ 'round', 'rem', 'mod' ];

/** @type {import('stylelint').Config} */
module.exports = {
extends: '@wordpress/stylelint-config/scss-stylistic',
plugins: [
'stylelint-plugin-logical-css',
'@wordpress/theme/stylelint-plugins/no-token-fallback-values',
],
reportNeedlessDisables: true,
// The rules shared with WordPress Core live in the published config so both
// projects have a single source for them. Only Gutenberg-specific
// configuration belongs below.
extends: '@wordpress/stylelint-config/project',
plugins: [ 'stylelint-plugin-logical-css' ],
rules: {
'at-rule-empty-line-before': null,
'at-rule-no-unknown': null,
'comment-empty-line-before': null,
'declaration-property-value-allowed-list': [
{
'flex-direction': '/^(?!(row|column)-reverse).*$/',
},
{
message: ( property, value ) =>
`Avoid "${ value }" value for the "${ property }" property. For accessibility reasons, visual, reading, and DOM order must match. Only use the reverse values when they do not affect reading order, meaning, and interaction.`,
},
],
'declaration-property-value-disallowed-list': [
{
'/.*/': [
Expand Down Expand Up @@ -51,37 +38,6 @@ module.exports = {
},
},
],
'font-weight-notation': null,
'@stylistic/max-line-length': null,
'no-descending-specificity': null,
'property-disallowed-list': [
[ 'order' ],
{
message:
'Avoid the order property. For accessibility reasons, visual, reading, and DOM order must match. Only use the order property when it does not affect reading order, meaning, and interaction.',
},
],
'rule-empty-line-before': null,
'selector-class-pattern': [
'^[a-z][a-z0-9]*(?:(?:__|--|-)[a-z0-9]+)*$',
{
message:
'Selector should use lowercase class segments separated with hyphens, double hyphens, or double underscores (selector-class-pattern)',
},
],
'value-keyword-case': null,
'scss/operator-no-unspaced': null,
'scss/selector-no-redundant-nesting-selector': null,
'scss/load-partial-extension': null,
'scss/no-global-function-names': null,
'scss/comment-no-empty': null,
'scss/at-extend-no-missing-placeholder': null,
'scss/operator-no-newline-after': null,
'scss/at-if-closing-brace-newline-after': null,
'scss/at-else-empty-line-before': null,
'scss/at-if-closing-brace-space-after': null,
'no-invalid-position-at-import-rule': null,
'plugin-wpds/no-token-fallback-values': true,
},
overrides: [
{
Expand Down Expand Up @@ -169,5 +125,4 @@ module.exports = {
},
},
],
reportDescriptionlessDisables: true,
};
1 change: 0 additions & 1 deletion tools/stylelint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
},
"dependencies": {
"@wordpress/stylelint-config": "file:../../packages/stylelint-config",
"@wordpress/theme": "file:../../packages/theme",
"stylelint": "^16.26.1",
"stylelint-plugin-logical-css": "^1.2.3"
},
Expand Down
Loading