From 916c3f483c3f6fc88841ad438264025599145307 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Thu, 30 Jul 2026 15:36:50 +0100 Subject: [PATCH 1/3] Wire up stylelint for CSS/SCSS linting Adds a .stylelintrc.json (extending @wordpress/stylelint-config), lint:css/lint:css:fix npm scripts, a lint-staged entry so husky's pre-commit hook enforces it on staged *.scss files, and a lint-css.yml CI workflow mirroring the existing lint-js.yml pattern. stylelint was already an indirect dependency via @wordpress/scripts but was never actually wired into the lint pipeline. --- .github/workflows/lint-css.yml | 74 ++++++++++++++++++++++++++++++++++ .stylelintrc.json | 6 +++ package.json | 7 +++- 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/lint-css.yml create mode 100644 .stylelintrc.json diff --git a/.github/workflows/lint-css.yml b/.github/workflows/lint-css.yml new file mode 100644 index 000000000..35cef4bf9 --- /dev/null +++ b/.github/workflows/lint-css.yml @@ -0,0 +1,74 @@ +name: Lint CSS + +on: + # Run on direct pushes to integration branches and on all pull requests. + push: + branches: + - develop + - main + - master + - trunk + pull_request: + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + runs-on: ubuntu-latest + + name: "Lint: CSS" + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Cache Node.js modules + id: cache-node-modules + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node_modules- + # The lint stage doesn't run the unit tests or use code style, so no need for PHPUnit, WPCS or phpcompatibility. + - name: 'Install NPM packages' + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci --prefer-offline --no-audit --ignore-scripts + + - name: Get only files changed in this PR + id: changed-files + uses: actions/github-script@v6 + with: + script: | + if (!context.payload.pull_request) { + core.warning("No pull request context available. Skipping changed files retrieval."); + core.setOutput('files', ''); + return ''; + } + + const changedFiles = await github.paginate( + github.rest.pulls.listFiles, + { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + } + ); + const cssFiles = changedFiles + .filter(file => file.status !== 'removed') + .map(file => file.filename) + .filter(filename => filename.endsWith('.scss') || filename.endsWith('.css')) + .join(' '); + + core.setOutput('files', cssFiles); + return cssFiles; + + # If this is a PR then lint only css/scss changed in the PR, if not a PR then lint them all. + - name: Run stylelint + run: npx wp-scripts lint-style ${{ steps.changed-files.outputs.files || '' }} diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 000000000..96c4ccca7 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "@wordpress/stylelint-config/scss-stylistic", + "rules": { + "selector-class-pattern": null + } +} diff --git a/package.json b/package.json index 0cb1e6217..5772ca5f8 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,14 @@ "dist": "npx webpack --mode production && composer install --no-dev && composer dump-autoload --no-dev -o && ./scripts/dist.sh && composer install", "dist:dotorg": "npm run dist:keep-build-folder", "dist:keep-build-folder": "npx webpack --mode production && composer install --no-dev && composer dump-autoload --no-dev -o && ./scripts/dist.sh --keep-build-folder", - "lint": "./vendor/bin/phpcs && npm run lint:js", + "lint": "./vendor/bin/phpcs && npm run lint:js && npm run lint:css", "lint-staged-precommit": "lint-staged", "lint:php": "./vendor/bin/phpcs", "lint:php:fix": "./vendor/bin/phpcbf", "lint:js": "wp-scripts lint-js", "lint:js:fix": "wp-scripts lint-js --fix", + "lint:css": "wp-scripts lint-style", + "lint:css:fix": "wp-scripts lint-style --fix", "test:php": "./scripts/setup-phpunit.sh", "test:php:run": "docker compose exec phpunit vendor/bin/phpunit", "test:php:coverage": "docker compose exec phpunit vendor/bin/phpunit --coverage-clover=./coverage/clover.xml --coverage-html=./coverage/html", @@ -28,7 +30,8 @@ }, "lint-staged": { "*.php": "./vendor/bin/phpcs", - "*.js": "wp-scripts lint-js" + "*.js": "wp-scripts lint-js", + "*.scss": "wp-scripts lint-style" }, "files": [ "LICENSE.txt", From a044b52da62ec9647c7f6894a49b8d146f1b62b6 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Thu, 30 Jul 2026 15:53:38 +0100 Subject: [PATCH 2/3] Exclude vendored coverage-report CSS from stylelint coverage/html/_css/*.css is third-party CSS bundled into the PHPUnit coverage HTML report, not plugin source. It wasn't covered by the default @wordpress/scripts ignore file (which only skips build/ and vendor/), so it was inflating lint output. build/, dist/, and vendor/ are added explicitly too so exclusion doesn't depend on the bundled default ignore file. --- .stylelintignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .stylelintignore diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 000000000..23cbd831f --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,5 @@ +build +dist +vendor +coverage +node_modules From a92d699b37ba6d377b076d1093fafbd6f99f6ddf Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Thu, 30 Jul 2026 16:18:56 +0100 Subject: [PATCH 3/3] Disable scss/load-no-partial-leading-underscore This rule wants @use/@import paths written without the file's leading underscore (e.g. @use "variables" for _variables.scss). Not a convention we want to enforce here. --- .stylelintrc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.stylelintrc.json b/.stylelintrc.json index 96c4ccca7..cb6cd0493 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,6 +1,7 @@ { "extends": "@wordpress/stylelint-config/scss-stylistic", "rules": { - "selector-class-pattern": null + "selector-class-pattern": null, + "scss/load-no-partial-leading-underscore": null } }