Skip to main content
Version: v1

Stylelint - CSS linter

The Stylelint website describes the tool as follows:

A mighty CSS linter that helps you avoid errors and enforce conventions.

Stylelint catches mistakes in stylesheets, such as unknown properties, invalid property values and duplicate selectors. It also enforces the conventions that keep stylesheets consistent, such as alphabetically ordered declarations. Vortex comes with a pre-configured Stylelint ruleset for Drupal projects.

This page is for developers who write CSS in custom modules or SCSS in the custom theme. It shows how to run Stylelint and fix what it reports, what the root and theme configurations enforce and how to ignore a rule.

In Vortex, the root lint-css script lints only the CSS in web/modules/custom. The custom theme has its own configuration, described in Theme configuration.

Usage​

Check for violations​

To check the stylesheets, run 1 of the following commands:

# Lint all front-end code: Twig, JavaScript and CSS.
ahoy lint-fe
# Lint only CSS in custom modules.
ahoy cli "npm run lint-css"

Confirm that the check passed: Stylelint lists no problems, and echo $? prints 0 right after the command.

Fix violations​

Stylelint's --fix flag repairs many violations automatically. To apply the fixes, run 1 of the following commands:

# Fix all front-end lint issues.
ahoy lint-fe-fix
# Fix only CSS issues in custom modules.
ahoy cli "npm run lint-fix-css"

ahoy lint-fe and ahoy lint-fe-fix also run the custom theme's lint and lint-fix scripts, which cover the theme's JavaScript and SCSS together. To lint or fix only the theme's stylesheets, run the theme's lint-css or lint-css-fix script, named unlike the root lint-fix-css:

ahoy cli "npm run --prefix=web/themes/custom/your_site_theme lint-css"
ahoy cli "npm run --prefix=web/themes/custom/your_site_theme lint-css-fix"

Configuration​

See the Stylelint configuration reference.

All global configuration lives in the .stylelintrc.js file.

The root configuration uses the following shared configuration and plugin:

  • stylelint-config-standard - the standard shareable configuration
  • stylelint-order - plugin that provides the order/* rules

The configuration sets the following rules on top of the standard configuration:

  • order/properties-alphabetical-order - declarations within a block must be in alphabetical order
  • at-rule-no-unknown - unknown at-rules are reported, except the Sass at-rules @extend, @at-root, @debug, @warn, @error, @if, @else, @for, @each, @while, @include, @mixin, @function, @return and @content
  • selector-class-pattern, selector-id-pattern, custom-property-pattern and keyframes-name-pattern - disabled, so class, ID, custom property and keyframes names can follow any naming pattern
  • no-descending-specificity - disabled
  • font-family-no-missing-generic-family-keyword - disabled

The lint-css script checks only .css files. In them, at-rule-no-unknown skips the listed Sass at-rules. @use and @forward are missing from the list, so Stylelint reports them as unknown.

The --allow-empty-input flag in the root scripts makes the run exit without an error when the glob matches no files:

{
"scripts": {
"lint-css": "stylelint --allow-empty-input \"web/modules/custom/**/*.css\"",
"lint-fix-css": "stylelint --allow-empty-input \"web/modules/custom/**/*.css\" --fix"
}
}

To add or remove targets, change the globs in the lint-css and lint-fix-css scripts in the root package.json. The following lint-css script also lints web/sites/default:

{
"scripts": {
"lint-css": "stylelint --allow-empty-input \"web/modules/custom/**/*.css\" \"web/sites/default/**/*.css\""
}
}

The root package.json lists the stylelint, stylelint-config-standard and stylelint-order packages in its devDependencies. ahoy fei installs them.

Theme configuration​

The custom theme ships its own .stylelintrc.json and package.json, so the theme can move to a separate repository with its tooling.

The theme configuration extends stylelint-config-standard and stylelint-config-standard-scss. It loads the stylelint-scss plugin, uses the string formatter and sets the following rules:

  • scss/at-extend-no-missing-placeholder - @extend must target a placeholder selector
  • scss/comment-no-empty - disabled
  • scss/dollar-variable-pattern - $variable names must match ^_?[a-z]+([a-z0-9-]+[a-z0-9]+)?$

The theme configuration skips stylelint-order, so the theme's SCSS can list declarations in any order.

The theme's lint-css script targets scss/**/*.scss. The ignoreFiles key skips scss/_components.scss, fonts/**/*.scss and build/.*. Stylelint reads these entries as globs relative to the configuration file, so build/.* matches only names that start with a dot directly inside build. The scss/**/*.scss target never reaches build anyway.

Ignore violations​

To ignore paths globally, list them in the ignoreFiles key of the configuration file, as the theme configuration does:

{
"ignoreFiles": [
"scss/_components.scss",
"fonts/**/*.scss",
"build/.*"
]
}

To ignore all Stylelint rules in a file, add this comment to the file header:

/* stylelint-disable */

To ignore specific rules in a file, add this comment to the file header:

/* stylelint-disable selector-max-id, declaration-no-important */

To ignore rules for a block of code, put it between a disable and an enable comment:

/* stylelint-disable declaration-no-important */
a {
color: red !important;
}
/* stylelint-enable declaration-no-important */

To ignore rules for the next line only, add this comment on the line before it:

a {
/* stylelint-disable-next-line declaration-no-important */
color: red !important;
}

To ignore rules for a single line, add this comment at the end of it:

a {
color: red !important; /* stylelint-disable-line declaration-no-important */
}

Continuous integration​

Stylelint runs in the lint job of the continuous integration pipeline and fails the build on violations. CI lints the custom modules and the custom theme in separate steps. It skips the theme step when VORTEX_FRONTEND_BUILD_SKIP is set to 1. On GitHub Actions, the theme step reads VORTEX_FRONTEND_BUILD_SKIP from a repository variable and ignores .env.

Each step runs the lint script of the root or theme package.json: npm run lint-js && npm run lint-css. The script stops at the first linter that fails, so Stylelint reports nothing until ESLint passes.

Ignore failures​

To let the build pass anyway, set the VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE CI variable to 1. Stylelint then prints its violations in the job log, but only when ESLint passes first. The variable is shared with ESLint, which runs in the same step.

➡️ See Continuous integration > Ignore tool failures