This directory contains all of our custom stylelint plugins, each of which provides a single stylelint rule.
These were intended for use with Primer CSS and GitHub projects, but you may find them useful elsewhere.
If you're using or extending @primer/stylelint-config
already, then you're using all of these plugins by default. See index.js
for the config defaults.
If you're not using or extending @primer/stylelint-config
, you can still reference the plugins by referencing their module paths like so:
// stylelint.config.js
module.exports = {
plugins: ['@primer/stylelint-config/plugins/no-override', '@primer/stylelint-config/plugins/no-unused-vars']
}
This rule prohibits "overriding" class selectors defined in Primer CSS. By default, it will only fail selectors that target utility classes:
// FAIL
.mt-0 {
/* literally anything */
}
You can further constrain overrides to exclude any class selector in Primer by providing additional names in the bundles
option:
// stylelint.config.js
module.exports = {
// ...
rules: {
'primer/no-override': [
true,
{
bundles: ['utilities', core', 'product', 'marketing']
}
]
}
}
This rule helps you find SCSS variables that may not be used, and can be safely deleted. It works by scanning all of the SCSS files in your project and looking for anything that appears to be either a Sass variable declaration or reference:
$name: value;
/** ↑
* The colon is what makes this a declaration */
/* Anything starting with a $ and followed by word chars or hyphens
* and _not_ followed by a colon is considered a reference: */
margin: $value;
/** ↑
* Not a colon */
padding: $value 1px;
/** ↑
* Not a colon */
@media screen and (max-width: $break-lg) {
/** ↑
* Also not a colon */
Equipped with a list of all the variable declarations and references, the linting rule walks all of the declarations in the file being linted, finds any that look like declarations (using the same pattern as the project-wide scan), and generates a warning for any that have zero references in the files it's scanned.
Because there isn't any good way for a stylelint plugin to know all of the files being linted, it needs to be told where to find all of the declarations and references in its options:
-
files
is a single path, glob, or array of paths and globs, that tells the plugin which files to scan relative to the current working directory. The default is['**/*.scss', '!node_modules']
, which tells globby to find all the.scss
files recursively and ignore thenode_modules
directory. -
variablePattern
is a regular expression that matches a single variable in either a source file string or theprop
of a postcss Declaration node ({type: 'decl'}
). The default matches Sass/SCSS variables:/\$[-\w]/g
. Note that theg
("global") flag is required to match multiple variable references on a single line. -
verbose
is a boolean that enables chattyconsole.warn()
messages telling you what the plugin found, which can aid in debugging more complicated project layouts.
This rule identifies deprecated color variables from [primer/primitives]](https://github.com/primer/primitives) deprecated.json file and suggests replacements.
body {
color: var(--color-fg-default);
}
/** ↑
* OK: --color-text-primary is defined */
body {
color: var(--color-text-primary);
}
/** ↑
* FAIL: --color-text-primary is deprecated. */
This rule prohibits any usages of undefined CSS variables.
:root {
--color-text-primary: #000;
}
body {
color: var(--color-text-primary);
}
/** ↑
* OK: --color-text-primary is defined */
body {
color: var(--color-foo);
}
/** ↑
* FAIL: --color-foo is not defined */
For the purposes of this rule, a CSS variable declaration is any text starting with --
and immediately followed by a colon.
Because there isn't a good way for a stylelint plugin to know what CSS variables are defined, it needs to be told where to look for declarations in its options:
files
is a single path, glob, or array of paths and globs, that tells the plugin which files (relative to the current working directory) to scan for CSS variable declarations. The default is['**/*.scss', '!node_modules']
, which tells globby to find all the.scss
files recursively and ignore thenode_modules
directory.verbose
is a boolean that enables chattyconsole.warn()
messages telling you what the plugin found, which can aid in debugging more complicated project layouts.
This rule prohibits the use of non-functional scale CSS variables like var(--color-scale-blue-1)
in all cases except the color-variables
mixin.
// Okay; using scale colors while defining new variables
@include color-scale-var('new-var-name', var(--color-scale-blue-1), var(--color-scale-blue-2))
// Fail; using scale colors directly as a property value
.selector {
color: var(--color-scale-blue-1)
}
This variable rule enforces the use of Primer color system variables for color
and background-color
CSS properties. Generally speaking, variables matching the pattern $text-*
are acceptable for color
(and fill
), and $bg-*
are acceptable for background-color
. See the configuration for more info.
body {
color: black;
}
/** ↑
* FAIL: Use a variable. */
body {
color: $gray-900;
}
/** ↑
* FAIL: Use $text-gray-dark instead. */
This variable rule enforces the use of Primer spacing variables in margin and padding CSS properties. See the configuration for more info.
ul {
margin: 0 0 $spacer-3;
}
/** ↑
* OK: "0" and "$spacer-*" are allowed values */
ul {
margin: 0 0 16px;
}
/** ↑
* FAIL: Use "$spacer-3" (auto-fixable!) */
This variable rule enforces the use of typography variables for font-size
, font-weight
, and line-height
CSS properties. See the configuration for more info.
This variable rule enforces the use of border-specific variables ($border-width
, $border-style
, and $border-color*
, and the $border
shorthand) for all border CSS properties (including the border
shorthand). The values 0
and none
are also allowed; see the configuration for more info.
This variable rule enforces the use of $box-shadow*
variables for the box-shadow
CSS property. See the configuration for more info.
This plugin checks for width
and min-width
declarations that use a value less than the minimum browser size. 320px
Checks for selectors with single declarations that can be replaced with primer/css utilities.
.foo {
color: var(--color-fg-default);
}
/** ↑
* FAIL: --color-fg-default can be replaced with .color-fg-default */
.foo {
color: #custom;
}
/** ↑
* OK: Color value doesn't match a utility. */
.foo {
color: var(--color-fg-default);
padding: 0;
}
/** ↑
* OK: Has more than one declaration, not considered */
Variable rules are created using a general-purpose helper that can validate constraints for matching CSS properties and values. In general, the Primer CSS variable rules enforce two basic principles for custom CSS:
- Use Primer variables whenever possible.
- Use functional variables (
$text-gray
vs.$gray-700
) whenever possible.
Validations take the form of an object literal in which each key/value pair defines a set of named constraints that apply to one or more CSS properties:
{
'background color': {
props: 'background-color',
// ...
},
'foreground color': {
props: ['color', 'fill'],
// ...
}
}
The objects in each named rule may have the following keys:
-
props
is an array or string of glob patterns that match CSS properties. For individual properties likecolor
, a string without any special glob characters works just fine. You can use brace expansion to match directional properties:'border width': { props: 'border{,-top,-right,-bottom,-left}-width', // which expands to: props: [ 'border-width', 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width' ] }
Note: if no
props
are listed, the name of the rule is assumed to be the CSS property. -
values
is, similar toprops
, an array or string of glob patterns that match individual CSS values in a single property. Property values are parsed with postcss-value-parser, so they respect parentheses, functions, and values within them. If a property has more than one value (e.g.margin: 0 auto
), each one is compared against thevalues
list to determine its validity, and a warning is generated for each invalid value.For example, if we fleshed out the
border width
rule defined above withvalues
:'border width': { props: 'border{,-top,-right,-bottom,-left}-width', values: ['$border-*', '0'] }
Then the following SCSS checks out:
.Box { border-width: 0 0 $border-width; /** ↑ ↑ ↑ * ↑ ↑ OK! * ↑ OK! * OK! */ }
-
components
tells the rule that multiple values resolve to a list of ordered properties with their own, separately defined rules. This makes it possible for shorthand CSS properties likeborder
,background
, orfont
to "delegate" validation to a rule with more specific constraints. For example, you could enforce different types of border variables for most of the CSS border properties with:'border': { props: 'border{,-top,-right,-bottom,-left}', components: ['border-width', 'border-style', 'border-color'] }, 'border width': { props: 'border{,-top,-right,-bottom,-left}-width', values: ['$border-width', '0'] }, 'border style': { props: 'border{,-top,-right,-bottom,-left}-style', values: ['$border-style', 'none'] }, 'border color': { props: 'border{,-top,-right,-bottom,-left}-color', values: ['$border-*', 'transparent'] }
-
replacements
is an object listing property values that can safely be replaced viastylelint --fix
with other variable or static values, as in the Primer CSSfont-size
rule:'font-size': { props: 'font-size', values: ['$h{0,1,2,3,4,5,6}-size', '$font-size-small'], replacements: { '40px': '$h0-size', '32px': '$h1-size', '24px': '$h2-size', '20px': '$h3-size', '16px': '$h4-size', '14px': '$h5-size', '12px': '$h6-size' } }
All variable rules respect the following rule options, as in:
// stylelint.config.js
module.exports = {
extends: '@primer/stylelint-config',
rules: {
'primer/colors': [true /* options here */]
/* ↑
* false disables the rule */
}
}
-
rules
extends the validations for the rule, and can be used to disable specific validations, as e.g.rules: { 'primer/colors': [true, { rules: { 'background color': false, // disabled 'text color': { // override the text color validation rules here } }] }
-
verbose
is a boolean that enables chattyconsole.warn()
messages that may help you debug more complicated configurations. -
disableFix
is a boolean that can disable auto-fixing of this rule when runningstylelint --fix
to auto-fix other rules.