stylelintの導入方法と各ルール解説

2021/05/20追記
各ルールについての説明部分は最新の情報に基づいた内容ではありません。
適宜、一次情報である[stylelintのドキュメント](https://stylelint.io/user-guide/rules/about)をご参照ください。

---

フロントエンドエンジニアの松田です。
先日、副業で開発に参加している[Findy](https://findy-code.io/)でstylelintの導入を行ったので、導入方法と各ルールについてまとめてみました。

## stylelintとは
eslint, tslint等と同じようなCSSのlinterです。導入することによって、詳細度や`!important`の使用制限など、CSSがカオスになってしまうのを防いでくれる効果があります。

## 導入方法（技術面）
[User Guide](https://stylelint.io/user-guide/)に書かれているように、stylelintを利用する方法は幾つかあります。チーム開発する場合はCLIツールとして導入し、npm scriptsで実行できるようにするのが良いでしょう。各種CIツールで実行する際にも便利です。

### インストール
他のCLIツールと同様にnpmもしくはyarnでパッケージを追加し、package.jsonのscriptsで実行できるようにしましょう。

```
npm install stylelint --save-dev
// or
yarn add -D stylelint
```

```package.json
"scripts": {
  "lint:css": "stylelint **/*.scss",
}
```
## 導入方法（運用面）
stylelintはデフォルトではルールが設定されていないので、とりあえず導入だけしてルールを広げていくという方法がとれます。prettierなどの場合はデフォルトでもある程度ルールが決まっているので、導入の際にはルールを無効化しておいて徐々に有効化していくことが必要になりますが、stylelintではその必要はありません。

まずは導入してCIツールなどの設定をしておき、徐々にルールを拡張してきましょう。

## Findyのstylelint設定
Findyではこの記事の執筆時点（2019/2/26）で、以下のルール設定になっています。まだ導入はしたばかりで、導入の際にチェックしたかった簡単なルールしか設定していませんが、徐々に拡張していきたいと考えています。

FindyではSCSSを使っているため、プラグインを追加してSCSSのルールも設定できるようにしています。

```stylelintrc.json
{
  "plugins": [
    "stylelint-scss"
  ],
  "rules": {
    "color-no-invalid-hex": [true],
    "color-named": "never",
    "color-hex-case": ["lower"]
  }
}
```

## 各ルールについて
### ルールごとの導入敷居
Possible errosは、そもそもCSSが機能していない部分を指摘するルールなので比較的導入が容易です。しかしPossible errosで指摘されている箇所が発生している場合は、修正の際に影響箇所の調査を必要とする場合があるので、導入ハードルはかなり上がります。ここでエラーが出ているルールは後回しにしておきましょう。

Limit language featureとStylistic issuesについては、表記の変更だけ（color-hex-lengthなど）のルールが多く、スタイル崩れが発生しづらいので導入が容易です。導入してもwarningが発生しない箇所から導入していき、warningが出る箇所については影響範囲を調査しつつ地道に導入していくのが良いでしょう。


ここからは各ルールの内容解説になります。

### Possible erros

#### Color
**color-no-invalid-hex**
16進数表記が正しいかチェックしてくれます。

```css
/* NG */
.ng { color: #00; }
/* OK */
.ok { color: #000; }
```

#### Font family
**font-family-no-duplicate-names** 
指定が重複していないかチェックしてくれます。

```css
/* NG */
.ng { font-family: sans-serif, sans-serif; }
/* OK */
.ok { font-family: sans-serif; }
```

**font-family-no-missing-generic-family-keyword**
sans-serifなどの一般的な使えるようなフォントを１つ以上指定しないとエラーになります。もちろんフォントがない場合は使えるフォントで自動的に表示されるため、文字自体が消えることはないですが、明示的に書きましょうというルールです。

```css
/* NG */
.ng { font-family: Times; }
/* OK */
.ok { font-family: Times, sans-serif; }
```

#### Function
**function-calc-no-invalid** 
calc()の表記が間違っていないかをチェックしてくれます。

```css
/* NG */
.ng { width: calc(100% -20px); }
/* OK */
.ok { width: calc(100% - 20px); }
```

**function-calc-no-unspaced-operator**
calc()の演算子の左右にスペースが入っているかをチェックしてくれます。
NGパターンでも一見すると動作するように見えますが、スペースが入っていないとダメです。 

```css
/* NG */
.ng { width: calc(100%+20px); }
/* OK */
.ok { width: calc(100% + 20px); }
```

**function-linear-gradient-no-nonstandard-direction**
linear-gradientの表記が間違っていないかチェックしてくれます。
toが入っていない、同じ位置が指定されている、などがエラーの対象です。

```css
/* NG */
.ng1 { background: linear-gradient(top, #fff, #000); }
.ng2 { background: linear-gradient(to top top, #fff, #000); }
/* OK */
.ok { background: linear-gradient(to bottom right, #fff, #000); }
```

#### String
**string-no-newline**
文字列の途中で改行が入っていないかをチェックしてくれます。CSSには文字列連結がないため、改行はできません。

```css
/* NG */
.ng {
  content: "first
    second";     
}  
/* OK */
.ok { content: "first\\nsecond";  }
```
#### Unit
**unit-no-unknown**
正しい単位が使われているかをチェックしてくれます。

```css
/* NG */
.ng { width: 200pixels; }
/* OK */
.ok { width: 200px; }
```

#### Property
**property-no-unknown**
存在しないプロパティが使われていないかをチェックしてくれます。

```css
/* NG */
.ng { widt: 200px; }
/* OK */
.ok { width: 200px; }
```

#### Keyframe declaration
**keyframe-declaration-no-important**
keyframeで`!important`が使われていないかをチェックしてくれます。
ブラウザによって、keyframe内の`!important`指定が無視される場合があります。

```css
/* NG */
@keyframes important1 {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100px) !important;
  }
}
/* OK */
@keyframes important1 {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100px);
  }
}
```

#### Declaration block
**declaration-block-no-duplicate-properties** 
同一セレクタ内で同じプロパティが使われていないかをチェックしてくれます。displayやfont-sizeなどのコピー＆ペーストでヒューマンエラーが出やすく、エラーになる場合もあるかもしれません。

```css
/* NG */
.ng {
  display: block;
  display: inline-block;
}
/* OK */
.ok { display: block; }
```

**declaration-block-no-shorthand-property-overrides**

```css
/* NG */
.ng {
  padding-left: 10px;
  padding: 20px;
}
/* OK */
.ok {
 padding: 20px;
 padding-left: 10px;
}
```

#### Block
**block-no-empty**
セレクタ指定内にプロパティがない場合にエラーになります。

```css
/* NG */
.ng { }
/* OK */
.ok { width: 200px; }
```

#### Selector
**selector-pseudo-class-no-unknown**
擬似クラスの名前があっているかをチェックしてくれます。

```css
/* NG */
.ng:hoverr { }
/* OK */
.ok:hover { }
```

**selector-pseudo-element-no-unknown**
擬似要素の名前があっているかをチェックしてくれます。

```css
/* NG */
.ng::beforee { }
/* OK */
.ok::before { }
```

**selector-type-no-unknown**
存在しないHTML要素を指定していないかをチェックしてくれます。

```css
/* NG */
aa { }
/* OK */
a { }
```

#### Media feature
**media-feature-name-no-unknown**
メディアクエリの指定が正しいかチェックしてくれます。

```css
/* NG */
@media screen and (unknown) {}
/* OK */
@media screen and (min-width: 700px) {}
```

#### At-rule
**at-rule-no-unknown**
@で始まる指定が正しいかどうかチェックしてくれます。

```css
/* NG */
@unknown {}
/* OK */
@charset "UTF-8";
```

#### Comment
**comment-no-empty**
空のコメントがないかチェックしてくれます。

```css
/* NG */
/* */
/* OK */
/* Comment */
```

#### General / Sheet
**no-descending-specificity** 
以下のような指定がないかチェックしてくれます。NGの`.ng a`は次の行の指定で無効化されてしまうため、意味のない指定になってしまいます。

```css
/* NG */
.ng a { }
a { }
/* OK */
a { }
.ok a { }
```

**no-duplicate-at-import-rules**
同一ファイルをimportしていないかチェックしてくれます。

```css
/* NG */
@import 'a.css';
@import 'a.css';
/* OK */
@import 'a.css';
@import 'b.css';
```

**no-duplicate-selectors**
重複セレクタがないかをチェックしてくれます。

```css
/* NG */
.ng .ng1 { }
.ng .ng1 { }
/* OK */
.ok .ok1 { }
.ok1 .ok { }
```

**no-empty-source**
CSSファイルが空でないかをチェックしてくれます。

**no-extra-semicolons(Autofixable)**
セミコロンが重複していないかをチェックしてくれます。

**no-invalid-double-slash-comments**
`//`で始まるコメントがないかをチェックしてくれます。

### Limit language features

#### Color
**color-named**
`"always-where-possible"`では色名をつける、`"never"`では色名をつけないことを強制します。必ずしも色名がついているカラーコードだけが使われているわけではなく、表記が混在してしまうので、neverがオススメです。

```css
/* NG */
.ng { color: black; }
/* OK */
.ok { color: #000; }
```

#### Function
**function-blacklist**
**function-whitelist**
オプションによってscaleなどの関数の利用を制限できます。

**function-url-no-scheme-relative**
`//example.com`のような`//`で始まるURL指定がないかをチェックしてくれます。

**function-url-scheme-blacklist**
**function-url-scheme-whitelist**
オプションによってURLのscheme制限ができます。

#### Keyframes
**keyframes-name-pattern**
正規表現によってkeyframeの名前を制限できます。

#### Number
**number-max-precision**
小数点以下の桁数を制限できます。

#### Time
**time-min-milliseconds**
時間を指定する場合の最小ミリ秒を制限できます。早すぎるアニメーションが実装されてしまうことを防ぐことができます。

#### Unit
**unit-blacklist**
**unit-whitelist**
em, remなどの特定の単位のしようを制限できます。

#### Shorthand property
**shorthand-property-no-redundant-values (Autofixable)**

```css
/* NG */
.ng { margin: 1px 1px 1px 1px; }
/* OK */
.ok { margin: 1px; }
```

#### Value
**value-no-vendor-prefix**
ベンダープレフィックスが書かれていないかをチェックしてくれます。バージョン管理するソースにはベンダープレフィックスを含めずに書いて、autoprefixerを使って自動追加するようにしましょう。

#### Custom property
**custom-property-pattern**
正規表現でカスタムプロパティの名前を制限できます。

#### Property
**property-blacklist**
**property-whitelist**
プロパティの使用を制限できます。

**property-no-vendor-prefix**
ベンダープレフィックスが書かれていないかをチェックしてくれます。バージョン管理するソースにはベンダープレフィックスを含めずに書いて、autoprefixerを使って自動追加するようにしましょう。

#### Declaration
**declaration-block-no-redundant-longhand-properties**

```css
/* NG */
.ng {
  padding-top: 1px;    
  padding-right: 2px;    
  padding-bottom: 3px;    
  padding-left: 4px;    
}
/* OK */
.ok { padding: 1px 2px 3px 4px; }
```

**declaration-no-important**
`!important`が使われていないかをチェックできます。ユーティリティクラスを集めたファイルなど、特定の箇所でのみ無効化すると良いでしょう。既存のプロジェクトに導入するのは現実的にかなり難しいので、新規プロジェクトの初期段階で設定しておきましょう。

**declaration-property-unit-blacklist**
**declaration-property-unit-whitelist**
**declaration-property-value-blacklist**
**declaration-property-value-whitelist**
特定のピクセル指定や、プロパティと値の設定を制限できます。

### Declaration block
**declaration-block-single-line-max-declarations**
1行に設定できるプロパティの数を制限できます。設定する場合は1にしましょう。

#### Selector
**selector-attribute-operator-blacklist**
**selector-attribute-operator-whitelist**
**selector-class-pattern**
**selector-combinator-blacklist**
**selector-combinator-whitelist**
**selector-id-pattern**
特定のセレクタ指定を制限できます。combinatorは`+`などの演算子のことです。

**selector-max-attribute**
**selector-max-class**
**selector-max-combinators**
**selector-max-compound-selectors**
**selector-max-empty-lines**
**selector-max-id**
**selector-max-pseudo-class**
**selector-max-specificity**
**selector-max-type**
**selector-max-universal**
１つのセレクタ指定で使える最大値を指定できます。
compound-selectorsは`.a .b`などクラスの連結の回数、specificityは詳細度の制限、typeは`a`などのタグ指定の回数制限を、universalは`*`の回数を制限できます。

selector-max-typeはスタイル崩れを防ぐためにかなり有効です。spanタグなどの指定を使っていると、HTMLの構成を変更してspanタグではなくなってしまった場合にスタイルが崩れてしまいますが、selector-max-typeをチェックしておくと事前に防げます。

selector-max-specificityで詳細度の上限を決めておけば、複雑なCSSになることを防げます。

**selector-nested-pattern**
ネストのパターンを指定できます。通常のCSSではネストを使った場合はエラーになります。

**selector-no-qualifying-type**
aタグなどタグ指定を一切できないようにします。特定のファイル内でのみ無効化しておくと良いでしょう。

**selector-no-vendor-prefix**
ベンダープレフィックスが書かれていないかをチェックしてくれます。バージョン管理するソースにはベンダープレフィックスを含めずに書いて、autoprefixerを使って自動追加するようにしましょう。

**selector-pseudo-class-blacklist**
**selector-pseudo-class-whitelist**
**selector-pseudo-element-blacklist**
**selector-pseudo-element-whitelist**
擬似クラス、擬似要素の制限ができます。

#### Media feature
**media-feature-name-blacklist**
**media-feature-name-whitelist**
**media-feature-name-value-whitelist**
メディアクエリの指定を制限できます。

**media-feature-name-no-vendor-prefix**
ベンダープレフィックスが書かれていないかをチェックしてくれます。バージョン管理するソースにはベンダープレフィックスを含めずに書いて、autoprefixerを使って自動追加するようにしましょう。

#### Custom media
**custom-media-pattern**
正規表現でカスタムメディアクエリのパターンを指定できます。

#### At-rule
**at-rule-blacklist**
**at-rule-whitelist**
@ルールの制限ができます。

**at-rule-no-vendor-prefix**
ベンダープレフィックスが書かれていないかをチェックしてくれます。バージョン管理するソースにはベンダープレフィックスを含めずに書いて、autoprefixerを使って自動追加するようにしましょう。

#### Comment
**comment-word-blacklist**
正規表現でコメントの文言を制限できます。TODOを残さないようにする、などが可能です。

#### General / Sheet
**max-nesting-depth**
ネストの最大深さを設定できます。

**no-unknown-animations**
keyframeで定義されていないアニメーション名を使用していないかチェックしてくれます。

### Stylistic issues
#### Color
**color-hex-case（(Autofixable)）**
**color-hex-length（(Autofixable)）**
16進数の大文字小文字(lower or upper)、長さ(short or long)の指定ができます。

#### Font family
**font-family-name-quotes**
スペースが含まれるフォントをクォートで囲むかどうかを指定できます。

#### Font weight
**font-weight-notation**
`"numeric"`にすると700などの数値指定を、`"named-where-possible"`にするとboldなどの名前での指定を強制できます。フォントによってはboldの数値が700でない場合もあるので、設定するなら`"named-where-possible"`が良いでしょう。

#### Function
**function-comma-newline-after (Autofixable)**
**function-comma-newline-before (Autofixable)**
**function-comma-space-after (Autofixable)**
**function-comma-space-before (Autofixable)**
**function-max-empty-lines(Autofixable)**
**function-name-case(Autofixable).**
**function-parentheses-newline-inside(Autofixable)**
**function-parentheses-space-inside (Autofixable)**
**function-url-quotes**
**function-whitespace-after(Autofixable)**
概ね名前の通りですが、表記のスタイルをチェックしてくれます。

#### Number
**number-leading-zero (Autofixable)**
1未満の数の表記を制御できます。`"always"`なら`0.5`, `"never"`なら`.5`を強制できます。これはどちらでも良いでしょう。

**number-no-trailing-zeros(Autofixable)**
1.0など数字が0で終わっていないかをチェックしてくれます。

#### String
**string-quotes(Autofixable)**
シングルクォートかダブルクォートかを指定できます。

#### Length
**length-zero-no-unit(Autofixable)**
`0px`を`0`にするかどうかを指定できます。

以下については、カンマや空白・改行のスタイルについてのチェックなので割愛します。
Unit
Value
Value list
Custom property
Property
Declaration
Declaration block
Block
Selector
Selector list
Rule
Media feature
Media query list
At-rule
Comment
General / Sheet