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 them 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 the CSS in custom modules
only (web/modules/custom). The custom theme keeps its own configuration
in its directory, described under Theme
configuration.
Usage
Check for violations
To check the stylesheets, run 1 of the following commands:
- Ahoy
- Docker Compose
# Lint all front-end code: Twig, JavaScript and CSS.
ahoy lint-fe
# Lint only CSS in custom modules.
ahoy cli "npm run lint-css"
docker compose exec cli npm run lint-css
Confirm that the check passed: Stylelint lists no problems, and echo $?,
run straight after the command, prints 0.
Fix violations
Stylelint fixes many violations automatically using the --fix flag. To
apply the fixes, run 1 of the following commands:
- Ahoy
- Docker Compose
# Fix all front-end lint issues.
ahoy lint-fe-fix
# Fix only CSS issues in custom modules.
ahoy cli "npm run lint-fix-css"
docker compose exec cli npm run lint-fix-css
ahoy lint-fe and ahoy lint-fe-fix also run the lint and lint-fix
scripts of the custom theme, which lint the theme's JavaScript and SCSS
together. To lint or fix only the theme's stylesheets, run its own
lint-css and lint-css-fix scripts:
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.
By default, Stylelint uses the following shared configuration and plugin:
stylelint-config-standard- the standard shareable configurationstylelint-order- plugin that provides theorder/*rules
The following rules are set on top of the standard configuration:
order/properties-alphabetical-order- declarations within a block must be in alphabetical orderat-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@contentselector-class-pattern,selector-id-pattern,custom-property-pattern, andkeyframes-name-pattern- disabled, so class, ID, custom property, and keyframes names aren't forced into a naming patternno-descending-specificity- disabledfont-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, but @use and
@forward aren't on the list, so Stylelint reports them as unknown.
Targets include custom modules only. The --allow-empty-input flag 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 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, and ahoy fei
installs them.
Theme configuration
The custom theme ships its own
.stylelintrc.json
and its own package.json, because a theme carries its own front-end
tooling and can be moved into a separate repository.
The theme configuration extends stylelint-config-standard and
stylelint-config-standard-scss, loads the stylelint-scss plugin, uses
the string formatter, and sets the following rules:
scss/at-extend-no-missing-placeholder-@extendmust target a placeholder selectorscss/comment-no-empty- disabledscss/dollar-variable-pattern-$variablenames must match^_?[a-z]+([a-z0-9-]+[a-z0-9]+)?$
Unlike the root configuration, it doesn't load stylelint-order, so the
theme's SCSS doesn't have to list declarations in alphabetical order.
The theme's lint-css script targets scss/**/*.scss, and the
configuration ignores scss/_components.scss, fonts/**/*.scss, and
build/.* through the ignoreFiles key. 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.
Ignoring
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, and skips the theme step when
VORTEX_FRONTEND_BUILD_SKIP
is set to 1. On GitHub Actions, the theme step reads a repository
variable of that name, not the value in .env.
Each step runs the lint script of its package.json, root or theme,
which is npm run lint-js && npm run lint-css. It stops at the first linter
that fails, so Stylelint reports nothing until ESLint passes.
Ignoring failures
To let the build pass anyway, set the VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE
CI variable to 1. Stylelint still runs and prints its violations in the
job log, as long as ESLint passes first. The variable is shared with
ESLint, which runs in the same step.