# Primer stylelint plugins

This directory contains all of our custom stylelint plugins, each of which provides a single stylelint rule.

### Rules

- [Primer stylelint plugins](#primer-stylelint-plugins)
    - [Rules](#rules)
  - [Usage](#usage)
  - [`primer/colors`](#primercolors)
  - [`primer/spacing`](#primerspacing)
  - [`primer/typography`](#primertypography)
  - [`primer/borders`](#primerborders)
  - [`primer/box-shadow`](#primerbox-shadow)
  - [`primer/responsive-widths`](#primerresponsive-widths)
  - [Variable rules](#variable-rules)
    - [Variable rule options](#variable-rule-options)

These were intended for use with [Primer CSS] and GitHub projects, but you may find them useful elsewhere.

## Usage

If you're using or extending `@primer/stylelint-config` already, then you're using all of these plugins by default. See [`index.js`](../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:

```js
// stylelint.config.js
module.exports = {
  plugins: ['@primer/stylelint-config/plugins/colors']
}
```

## `primer/colors`

This [variable rule](#variable-rules) enforces the use of Primer [color system](https://primer.style/css/support/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](./colors.js) for more info.

```scss
body {
  color: black;
}
/**           ↑
 *            FAIL: Use a variable. */

body {
  color: $gray-900;
}
/**           ↑
 *            FAIL: Use $text-gray-dark instead. */
```

## `primer/spacing`

This [variable rule](#variable-rules) enforces the use of Primer [spacing variables](https://primer.style/css/support/spacing) in margin and padding CSS properties. See [the configuration](./spacing.js) for more info.

```scss
ul {
  margin: 0 0 $spacer-3;
}
/**          ↑
 *           OK: "0" and "$spacer-*" are allowed values */

ul {
  margin: 0 0 16px;
}
/**              ↑
 *               FAIL: Use "$spacer-3" (auto-fixable!) */
```

## `primer/typography`

This [variable rule](#variable-rules) enforces the use of [typography variables](https://primer.style/css/support/typography#typography-variables) for `font-size`, `font-weight`, and `line-height` CSS properties. See [the configuration](./typography.js) for more info.

## `primer/borders`

This [variable rule](#variable-rules) 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](./borders.js) for more info.

## `primer/box-shadow`

This [variable rule](#variable-rules) enforces the use of `$box-shadow*` variables for the `box-shadow` CSS property. See [the configuration](./box-shadow.js) for more info.

## `primer/responsive-widths`

This plugin checks for `width` and `min-width` declarations that use a value less than the minimum browser size. `320px`

## Variable rules

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:

```js
{
  '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 like `color`, a string without any special glob characters works just fine. You can use brace expansion to match directional properties:

  ```js
  '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 to `props`, an array or string of [glob patterns] that match _individual CSS values_ in a single property. Property values are parsed with [postcss-value-parser](https://www.npmjs.com/package/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 the `values` 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 with `values`:

  ```js
  'border width': {
    props: 'border{,-top,-right,-bottom,-left}-width',
    values: ['$border-*', '0']
  }
  ```

  Then the following SCSS checks out:

  ```scss
  .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 like `border`, `background`, or `font` 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:

  ```js
  '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 via `stylelint --fix` with other variable or static values, as in the Primer CSS `font-size` rule:

  ```js
  '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'
    }
  }
  ```

### Variable rule options

All variable rules respect the following rule options, as in:

```js
// 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.

  ```js
  rules: {
    'primer/colors': [true, {
      rules: {
        'background color': false, // disabled
        'text color': {
          // override the text color validation rules here
        }
    }]
  }
  ```

- `verbose` is a boolean that enables chatty `console.warn()` messages that may help you debug more complicated configurations.

- `disableFix` is a boolean that can disable auto-fixing of this rule when running `stylelint --fix` to auto-fix other rules.

[primer css]: https://primer.style/css
[glob patterns]: http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm
