はじめに
既存のコードに対してCSSLintを走らせてCSSのチェックをしてみると結構色々な警告が出ていました。
本記事はCSSLintのルールをベースにして、CSSはこう書いた方が 保守・再利用・拡張しやすい 、またこう書くと 破綻しにくい というポイントを紹介していきます。
CSSLintとは
その名の通り、CSSのLinterです。
CSSに問題点ないか自動でチェックしてくれるツールです。
CSSLintにはルールが存在して「この書き方はちょっとなぁ・・・」という箇所を警告してくれます。
警告されたものの「このコードの何がダメ?」「どう直せば良い?」などパッとわからなかったところがありました。
本記事では、公式の ルール を元に説明を加えていきます。(一部、紹介する順番を公式と変えている部分がありますが内容は同じです)
また、ブラウザ版( http://csslint.net/ )も提供されているため、すぐに試してみたいという方はそちらで体験されるのも良いかと思います。
CSSLintの導入/実行方法
インストール
CSSLintはコマンドラインで実行できるため、ビルドシステムに組み込むことができます。
現在、Node.js用とRhino用の2つのCLIが提供されています。
詳しくは公式の Wiki をご覧ください。
ここでは、Node.jsで実行する場合のインストール手順を書いておきます。
Node.jsでCSSLintを実行するには、npmがインストールされている必要があります。
npmがインストールされていない場合は、http://npmjs.org/ の手順に従ってインストールしてください。
既にnpmがインストールされていれば npm install -g csslint
を実行するだけで利用可能となります。
本記事を書いている時の最新バージョン(v1.0.5)を使用しています。
実行方法
$ csslint [チェックしたいCSSファイルのパス]
ルールの除外
特定のルールを除外したいときは以下のいずれの方法でもできます。
Rule IDids
,box-model
を除外する場合の例を示します。
① コマンドラインで実行するときに --ignore
オプションで除外する
$ csslint sample.css --ignore=ids,box-model
② .csslintrcを配置して除外する
カレントディレクトリに .csslintrc というファイルを作成します。
以下のように除外するルールを指定します。
実行時の --ignore
オプションは不要です。
Github issue Missing global options によると、あるバージョンを境に書き方が全面的に変更されたようです。
{
"ignore": [
"ids",
"box-model"
]
}
③ CSSファイルのコメントで特定の箇所のみルールの除外をする
以下は、ids
のルールに沿ってないため警告が出ますが、それを特定の箇所だけ除外する例です。
行単位で指定する書き方
#id { color: red; } /* csslint allow: ids */
範囲を指定する書き方
/* csslint ignore:start */
#id2 { color: blue; }
#id3 { color: green; }
/* csslint ignore:end */
CSSLintで検出されるルール一覧
2018/10/26現在、CSSLintには32個のルールがあります。
本記事では、 どのようなルールなのか、なぜ良くないのか、どのような書き方をすると警告が出るのか を解説していきます。
未指定の場合は、全てのルールが対象となります。
ルールに沿っていないものが検出された場合、該当するルールのメッセージと警告された対象のコードが出力されます。
CSSの潜在的なエラーをチェックするルール
CSSの意図しないバグを埋め込む可能性があるような箇所がないかチェックします。
1. Beware of box model size
Rule ID : box-model
https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size
borderとpaddingはwidthやheight等に含まれないため、これらを同時に指定すると意図しないスタイルを当ててしまう可能性があるため「混ぜるな危険!」と警告が出ます。
/** paddingとwidthの組み合わせ[NG] **/
.box {
padding: 30px;
width: 120px;
}
/** borderとheightの組み合わせ[NG] **/
.box2 {
border: medium solid red;
height: 60px;
}
$ csslint sample.css
1: warning at line 3, col 3
Using width with padding can sometimes make elements larger than you expect.
padding: 30px;
2: warning at line 9, col 3
Using height with border can sometimes make elements larger than you expect.
border: medium solid red;
2. Require properties appropriate for display
Rule ID : display-property-grouping
https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display
1つのセレクタに対して複数のプロパティを同時に指定できますが、組み合わせによっては無意味(指定したスタイルが当たらない)となってしまうケースがあります。
例えば、以下のようにinline
な要素に対してheight
を指定しても高さは指定通りにはなりません。
このままにしておくとゴミを残してしまうだけではなく、今まで機能していなかったheight
がdisplay: block;
にした途端に有効になって高さが変わって戸惑うなんてことも考えられます。
/** インライン要素なので、heightを指定しても効かないのでheightの定義が無意味[NG] **/
.box {
display: inline;
height: 100px;
}
/** 組み合わせたプロパティによって干渉や無効化されることなくスタイルがあたる[OK] **/
.box2 {
display: block;
height: 100px;
}
$ csslint sample.css
1: warning at line 4, col 3
height can't be used with display: inline.
height: 100px;
3. Disallow duplicate properties
Rule ID : duplicate-properties
https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties
意図的に同じプロパティを定義するケースはあるけれど、離して書くのはやめましょうという警告です。
たくさんのプロパティを指定したものだと、意図したものであってもどこにあるのかわからなくなる元です。
/** 同じプロパティを離して定義[NG] **/
.box {
background: black;
color: white;
background: rgba(0, 0, 0, 0.5);
}
/** 同じプロパティを続けて定義[OK] **/
.box2 {
background: black;
background: rgba(0, 0, 0, 0.5);
color: white;
}
$ csslint sample.css --ignore=order-alphabetical,fallback-colors
1: warning at line 5, col 3
Duplicate property 'background' found.
background: rgba(0, 0, 0, 0.5);
後述するorder-alphabetical
,fallback-colors
の警告も同時に出るためここでは除外しています。
4. Disallow empty rules
Rule ID : empty-rules
https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules
プロパティが何もないセレクタのみの記述を残しておくのはやめましょうという警告です。
ただ、進め方によってはHTML側だけ先に実装しておいて、スタイル適用は後回しでセレクタだけCSSに定義する場合もあるかもしれないため臨機応変にやった方が良いと思います。
.box {
}
$ csslint sample.css
1: warning at line 1, col 1
Rule is empty.
.box {
5. Require use of known properties
Rule ID : known-properties
https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties
存在しないプロパティ名や値を指定すると「そんなものはない!」と警告がでます。
typo防止にも役立ちます。color
の値をgray
と書いたつもりがglay
になってるなど
また、ベンダー接頭辞付きのプロパティはスルーされるためプロパティ名やその値が正しくなくても警告はでません。
.box {
background-color: hoge; /* カラーネームに'hoge'は存在しない[NG] */
bg: black; /* backgroundを勝手にbgと省略[NG] */
-moz-bg: hoge; /* ベンダー接頭辞付きのプロパティはチェックされない */
}
$ csslint sample.css
1: warning at line 2, col 3
Expected (<color>) but found 'hoge'.
background-color: hoge;
2: warning at line 3, col 3
Unknown property 'bg'.
bg: black;
CSSのパフォーマンスに関するルール
実行時のパフォーマンスや全体的なファイルサイズなど、CSSのパフォーマンスを下げていないかチェックします。
6. Don't use too many web fonts
Rule ID : font-faces
https://github.com/CSSLint/csslint/wiki/Don't-use-too-many-web-fonts
最近はWebフォントの人気が高まり、Webフォントの指定(@font-face
)を使用することが増えていますが、これによりフォントファイルがかなり大きくなります。
ダウンロード中にレンダリングをブロックしてパフォーマンスに影響を与えてしまうブラウザもあります。
このため、CSSLintはスタイルシートに6つ以上のWebフォントがある場合には警告が出ます。
次のようなHTMLがある場合、フォントを指定した表示は以下のようになります。
ここではローカルにあるフォントをMyFont1〜MyFont5として定義して、スタイルを当てる側でfont-family
としてそれを指定しています。
<div class="font1">abcあいう123(Ayuthaya)</div>
<div class="font2">abcあいう123(Savoye LET)</div>
<div class="font3">abcあいう123(Arial Narrow Bold Italic)</div>
<div class="font4">abcあいう123(PTMono)</div>
<div class="font5">abcあいう123(SignPainter)</div>
@font-face {
font-family: 'MyFont1';
src: local('Ayuthaya');
}
@font-face {
font-family: 'MyFont2';
src: local('Savoye LET');
}
@font-face {
font-family: 'MyFont3';
src: local('Arial Narrow Bold Italic');
}
@font-face {
font-family: 'MyFont4';
src: local('PTMono');
}
@font-face {
font-family: 'MyFont5';
src: local('SignPainter');
}
/** 6つ以上の指定しているので警告が出る[NG] **/
@font-face {
font-family: 'MyFont6';
src: local('ChalkboardSE');
}
.font1 { font-family: 'MyFont1'; }
.font2 { font-family: 'MyFont2'; }
.font3 { font-family: 'MyFont3'; }
.font4 { font-family: 'MyFont4'; }
.font5 { font-family: 'MyFont5'; }
.font6 { font-family: 'MyFont6'; }
$ csslint sample.css --ignore=bulletproof-font-face
1: warning
Too many @font-face declarations (6).
ここではシンプルな例を示すために前述した古いIEをサポートする記述を省略しています。これにより、bulletproof-font-face
による警告が出るためルールを除外しています。
7. Disallow @import
Rule ID : import
https://github.com/CSSLint/csslint/wiki/Disallow-@import
@import
は別のCSSファイルを読み込むことができます。
1つのCSSに書くと膨大になってしまう時にいくつかのCSSファイルに分割することはあると思います。
@import
で分割したCSSファイルを読み込むことができるため便利な機能です。(@import
は最上部に書かれていないと機能しません)
ですが、注意しないといけないこともあるため紹介します。
以下の例では、「sample.css」が「sample2.css」を読み込んでいますが、並列にダウンロードが走らないため待ちができてロードが遅くなります。
<link type="text/css" rel="stylesheet" href="sample.css">
@import url('sample2.css');
/** sample.css内でsample2.cssを読み込みしている
以降に、sample.cssのコードがズラーっと書かれているものと考えてください **/
$ csslint sample.css
1: warning at line 1, col 1
@import prevents parallel downloads, use <link> instead.
@import url('sample2.css');
HTML側に<link>
要素を使って以下のように記述している場合は、ファイルを同時に読み込みできます。
すなわち、最初のCSSファイルの読み込み完了を待たずに次のファイルをリクエストできるため、回線をより効率よく使ってダウンロードができます。
<link type="text/css" rel="stylesheet" href="sample.css">
<link type="text/css" rel="stylesheet" href="sample2.css">
詳しくまとめてある記事がこちらにありましたのでご覧ください。
8. Disallow selectors that look like regular expressions
Rule ID : regex-selectors
https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions
CSS3では、属性値を正規表現でのマッチングができます。
属性セレクタは、正規表現のマッチングが単純なclassベースのマッチングよりも遅いため、パフォーマンスに影響を与えます。
パフォーマンス観点でのCSSセレクタ指定方法にわかりやすく説明してありましたので詳しくはそちらをご覧下さい。
以下の属性セレクタは注意が必要なため使用すると警告が出ます。
-
^=
: 前方一致 -
$=
: 後方一致 -
*=
: 部分一致 -
~=
: 単語一致 -
|=
: パターンの一致
/** 完全一致[OK] **/
a[href='https://www.yahoo.co.jp/'] {
color: blue;
}
/** 前方一致[NG] **/
a[href^='https://www'] {
color: red;
}
/** 後方一致[NG] **/
a[href$='.co.jp/'] {
color: green;
}
/** 部分一致[NG] **/
a[href*='www'] {
color: yellow;
}
/** 単語一致[NG] **/
a[title~='サイト'] {
color: purple;
}
/** パターンの一致[NG] **/
a[lang|='ja'] {
color: pink;
}
$ csslint sample.css
1: warning at line 7, col 2
Attribute selectors with ^= are slow!
a[href^='https://www'] {
2: warning at line 12, col 2
Attribute selectors with $= are slow!
a[href$='.co.jp/'] {
3: warning at line 17, col 2
Attribute selectors with *= are slow!
a[href*='www'] {
4: warning at line 22, col 2
Attribute selectors with ~= are slow!
a[title~='サイト'] {
5: warning at line 27, col 2
Attribute selectors with |= are slow!
a[lang|='ja'] {
9. Disallow universal selector
Rule ID : universal-selector
https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector
ユニバーサルセレクタ(*
)は全ての要素に一致します。
ユニバーサルセレクタは一度に要素のグループを選択するのに便利ですが、セレクタのキーセレクタ(右端)として使用するとパフォーマンスの問題を引き起こします。
ブラウザはセレクタを右から左に評価するため、ユニバーサルセレクタを使用すると全ての要素を一致させることから始まります。
その後にそれぞれの要素を検査して、"box"をクラスを持つ祖先の一致を確認します。
ユニバーサルセレクタを含むセレクタが複雑になればなるほど、評価は遅くなります。
このため、ユニバーサルセレクタをセレクタのキーセレクタとして使用した場合、パフォーマンスを下げる可能性があるため使用しない方が良いという警告が出ます。
/** ユンバーサルセレクタのみ[NG] **/
* {
color: red;
}
/** キーセレクタがユンバーサルセレクタ[NG] **/
.box * {
color: red;
}
/** ユンバーサルセレクタを使っているが、キーセレクタではない[OK] **/
.box * a {
color: red;
}
$ csslint sample.css
1: warning at line 2, col 1
The universal selector (*) is known to be slow.
* {
2: warning at line 7, col 6
The universal selector (*) is known to be slow.
.box * {
10. Disallow unqualified attribute selectors
Rule ID : unqualified-attributes
https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors
[type='text']
などの修飾されていない属性セレクタは、全ての要素を最初に一致させてからその属性を確認します。
つまり、修飾されていない属性セレクタはユニバーサルセレクタ(*
)と同様にキーセレクタとして使用するとパフォーマンスの問題を引き起こします。
このため、修飾されていない属性セレクタをセレクタのキーセレクタとして使用した場合、パフォーマンスを下げる可能性があるため使用しない方が良いという警告が出ます。
/** キーセレクタが修飾されていない属性セレクタ[NG] **/
[type='text'] {
color: red;
}
/** キーセレクタが修飾されていない属性セレクタ[NG] **/
.box [type='text'] {
color: red;
}
/** キーセレクタが修飾されている属性セレクタ[OK] **/
input[type='text'] {
color: red;
}
/** 修飾されていない属性セレクタを使っているが、キーセレクタではない[OK] **/
[type='text'] .box2 {
color: red;
}
$ csslint sample.css
1: warning at line 2, col 1
Unqualified attribute selectors are known to be slow.
[type='text'] {
2: warning at line 7, col 6
Unqualified attribute selectors are known to be slow.
.box [type='text'] {
11. Disallow units for zero values
Rule ID : zero-units
https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values
margin
やpadding
などを初期化する際に0px
と書かれているコードを目にすることがありますが、値0px
, 0em
, 0%
と0
は同じものとして解釈されます。
つまり、値が0
の時は単位を省略して書くことができます。
値が0
で単位を付けている場合、単位は不要で0
と書けば良いという警告が出ます。
省略して書くことはCSSのサイズを抑える手段としても有効です。
/** 単位付き[NG] **/
.box {
margin: 0px;
padding: 100px 0px;
width: 0%;
}
/** 単位を省略[OK] **/
.box2 {
margin: 0;
padding: 100px 0;
width: 0;
}
$ csslint sample.css
1: warning at line 3, col 11
Values of 0 shouldn't have units specified.
margin: 0px;
2: warning at line 4, col 18
Values of 0 shouldn't have units specified.
padding: 100px 0px;
3: warning at line 5, col 10
Values of 0 shouldn't have units specified.
width: 0%;
12. Disallow overqualified elements
Rule ID : overqualified-elements
https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements
要素名によってクラスの動作が異なる場合を除けば、.active
のような要素名付きのセレクタの記述は不要(.active
で十分)です。
従って、多くの場合はセレクタから要素名を削除しても問題ないはずです。
要素名を削除することで、セレクタのパフォーマンスを向上させるだけでなく、CSSのサイズを小さくすることもできます。
また、要素名を削除すると、CSSとHTML間の結合も緩和され、CSSを更新することなく既存のクラスを付与することができます。
以下のような要素に対して、color: red;
のスタイルをあてるクラスred
を例に説明します。
<div class='red box'>
<div class='summary'>hoge</div>
</div>
<p class='red'>p.red</p>
.red
(redクラス)の動作がどの要素も同じ場合は、div.red { color: red; }
とp.red { color: red; }
を個別で書く必要がなく、.red { color: red; }
と書いておけば十分です。
このように書いておけば新しい要素<span class='red'>span.red</span>
をHTMLに追加した時、CSS側の修正を入れなくても既存のスタイルを利用することができます。
/** .summaryはdiv要素に限定する必要はないが要素名を付けている[NG] **/
.box div.summary {
background: lightblue;
}
/** 要素によってクラスの動作が異なる場合に要素名を付けて判別している[OK] **/
div.red {
color: red;
}
p.red {
color: red;
font-weight: bold;
}
$ csslint sample.css
1: warning at line 2, col 6
Element (div.summary) is overqualified, just use .summary without element name.
.box div.summary {
13. Require shorthand properties
Rule ID : shorthand
https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties
複数のプロパティを1つずつ書いている場合、一括指定できるものはしておいたほうが好ましいという警告が出ます。
margin
やpadding
がその対象となり、一括指定できるプロパティをまとめてファイルサイズを減らすことを目的としています。
一括指定のやり方についてはスタイルシートリファレンス(margin), スタイルシートリファレンス(padding)をご覧ください。
/** 一括指定できるがしていないケースその1[NG] **/
.box { /* margin: 10px; と一括指定で書ける*/
margin-bottom: 10px;
margin-left: 10px;
margin-right: 10px;
margin-top: 10px;
}
/** 一括指定できるがしていないケースその2[NG] **/
.box2 { /* padding: 20px 10px; と一括指定で書ける */
padding-bottom: 20px;
padding-left: 10px;
padding-right: 10px;
padding-top: 20px;
}
/** 4つのプロパティを指定していないため一括指定はできない[OK] **/
.box3 {
padding-right: 10px;
padding-top: 20px;
}
$ csslint sample.css
1: warning at line 2, col 1
The properties margin-top, margin-bottom, margin-left, margin-right can be replaced by margin.
.box { /* margin: 10px; と一括指定で書ける*/
2: warning at line 10, col 1
The properties padding-top, padding-bottom, padding-left, padding-right can be replaced by padding.
.box2 { /* padding: 20px 10px; と一括指定で書ける */
14. Disallow duplicate background images
Rule ID : duplicate-background-images
https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images
背景画像を指定するurl
の定義は同じ画像を使用する場合、パフォーマンスの観点からCSS内では一度だけ使用するのが好ましいです。
同じ背景画像を使用する必要があるクラスが複数ある場合は、url
を定義するクラス(下の例では.icon
)と開始位置の指定などを定義するクラス(下の例ではtop-icon
と.bottom-icon
)のように分けておけばurl
の定義は一度だけですみます。
ファイル名を変更するようなときにも、一箇所に書いておくと修正量も少なくてすみます。
<div class="icon top-icon"></div>
<div class="icon bottom-icon"></div>
/** urlを複数定義[NG] **/
.top-icon {
background: url('./sample.png') top left no-repeat;
height: 200px;
width: 400px;
}
.bottom-icon {
background: url('./sample.png') bottom center no-repeat;
height: 200px;
width: 400px;
}
/** urlを1回定義[OK] **/
.icon {
background: url('./sample2.png') no-repeat; /* 上の例と区別するためにsample2.png(別名)としています */
height: 200px;
width: 400px;
}
.top-icon {
background-position: top left;
}
.bottom-icon {
background-position: bottom center;
}
$ csslint sample.css
1: warning at line 9, col 3
Background image './sample.png' was used multiple times, first declared at line 3, col 3.
background: url('./sample.png') bottom center no-repeat;
CSSの保守性や再利用性に関するルール
これらのルールは、コードが他の人によって読みやすく保守可能であることを保証するのに役立ちます。
15. Disallow too many floats
Rule ID : floats
https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats
float
はレイアウトを実現するために色々なところで使われることが多いですが、必要以上に使ってしまっていることもあります。
CSSLintでは10回以上float
の定義があれば警告が出ます。
/** floatの定義が合計10個以上[NG] **/
.box1 { float: left; }
.box2 { float: left; }
.box3 { float: left; }
.box4 { float: center; }
.box5 { float: center; }
.box6 { float: center; }
.box7 { float: right; }
.box8 { float: right; }
.box9 { float: right; }
.box10 { float: right; }
$ csslint sample.css
1: warning
Too many floats (10), you're probably using them for layout. Consider using a grid system instead.
最近ではグリッドレイアウトが普及しているため、float
を多用することは少なくなっていると思います。
グリッドレイアウトを使用するとこのようなレイアウトを簡単に作成することができます。
グリッドレイアウトについてはグリッドレイアウトの基本概念をご覧下さい。
<div class='grid-container'>
<div class='grid-child'>コンテナ1</div>
<div class='grid-child'>コンテナ2</div>
<div class='grid-child'>コンテナ3</div>
<div class='grid-child'>コンテナ4</div>
<div class='grid-child'>コンテナ5</div>
<div class='grid-child'>コンテナ6</div>
</div>
.grid-container {
display: grid; /* グリッドコンテナの作成 */
grid-template-columns: 1fr 4fr 2fr; /* 縦向きのグリッドラインを定義 */
grid-template-rows: 1fr 5fr 1fr; /* 横向きのグリッドラインを定義 */
}
.grid-child {
border-radius: 5px;
color: white;
margin: 5px;
padding: 10px;
}
.grid-child:nth-child(1) { background-color: lightblue; }
.grid-child:nth-child(2) { background-color: lightcoral; }
.grid-child:nth-child(3) { background-color: lightgreen; }
.grid-child:nth-child(4) { background-color: lightpink; }
.grid-child:nth-child(5) { background-color: lightseagreen; }
.grid-child:nth-child(6) { background-color: lightskyblue; }
16. Don't use too many font-size declarations
Rule ID : font-sizes
https://github.com/CSSLint/csslint/wiki/Don't-use-too-many-font-size-declarations
フォントサイズは、サイト内の任意の要素に適用できる抽象化されたクラスとして表現されるのが好ましいです。
抽象化されていない場合は、フォントサイズを表示するために要素毎に何度もそれを書く必要があります。
抽象化されたクラスとして表現されていれば、フォントサイズを一貫して使用することができてデザインの変更にも対応しやすく保守/再利用しやすくなります。
また、CSSにfont-size
と書く回数も減ります。
抽象化したフォントサイズクラスを作成した例を書きます。
ここでは要素自体がサイズを持っているヘッダー要素(h1
〜h6
)を使わずに、div
要素を使って書いています。
<div class='large'>題名1</div>
<div class='medium'>概要1</div>
<div class='small'>あいうえおかきくけこさしすせそ</div>
<div class='medium'>内容1</div>
<div class='small'>アイウエオカキクケコサシスセソ</div>
<div class='large'>題名2</div>
<div class='medium'>概要2</div>
<div class='small'>あいうえおかきくけこさしすせそ</div>
<div class='medium'>内容2</div>
<div class='small'>アイウエオカキクケコサシスセソ</div>
.small { font-size: 8px; }
.medium { font-size: 12px; }
.large { font-size: 16px; }
CSSLintでは10回以上font-size
の定義があれば警告が出ます。
/** font-sizeの定義が合計10個以上[NG] **/
.small-box1 { font-size: 8px; }
.small-box2 { font-size: 8px; }
.small-box3 { font-size: 8px; }
.small-box4 { font-size: 8px; }
.medium-box1 { font-size: 12px; }
.medium-box2 { font-size: 12px; }
.medium-box3 { font-size: 12px; }
.medium-box4 { font-size: 12px; }
.large-box1 { font-size: 16px; }
.large-box2 { font-size: 16px; }
$ csslint sample.css
1: warning
Too many font-size declarations (10), abstraction needed.
17. Disallow IDs in selectors
Rule ID : ids
https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors
CSSを使う利点の1つはスタイルのルールを複数の場所で再利用できることです。
id
はユニークであるためページ上の全ての要素をidセレクタで表現できますが、ルールの再利用はできません。
当然、id
出ないと表現できないことはないため、id
をclass
に変更して書き換えることができます。
一方で、id
を使う方がパフォーマンスが良いのも事実です。
id
をキーセレクタに使うと速度面では優れていることはよく知られていますが、id
をキーセレクタに使う場合に限って言えることです。
パフォーマンスについてはDon’t use IDs in CSS selectors?に詳しく書かれていますが、抜粋して説明します。
id
をキーセレクタに使うとclass
よりは速いですが、キーセレクタでない場合はパフォーマンスを出せていません。
1000個の要素を用意してパフォーマンステストした結果です。
#id | .class | #id a |
---|---|---|
90.3ms | 91.5 ms | 104ms |
ただ、id
を一切使うな!というルールは個人的には厳しすぎる気もします。
CSSLintのルール気にして書いていなければ、おそらく一番これを目にすることが多いのではないかと思います。
/** セレクタにidを含むもの[NG] **/
#box { color: red; }
div #box2 { color: green; }
#box3 div { color: blue; }
$ csslint sample.css
1: warning at line 2, col 1
Don't use IDs in selectors.
#box { color: red; }
2: warning at line 3, col 1
Don't use IDs in selectors.
div #box2 { color: green; }
3: warning at line 4, col 1
Don't use IDs in selectors.
#box3 div { color: blue; }
18. Disallow !important
Rule ID : important
https://github.com/CSSLint/csslint/wiki/Disallow-!important
CSSには優先順位があり、それはセレクタの指定方法で決まります。詳しくはCSSセレクタの優先順位の計算方法をご覧ください。
優先順位を決めるために詳細度という尺度があります。
http://specificity.keegan.st/ などセレクタを入力するだけで自動計算してくれるツールもあります。
以下のHTMLの同じ要素に対して、文字色(color
)をあてる例で説明します。
<div class='boxes'>
<span class='box' id='box'>BOX</span>
</div>
#box { color: blue; } /* idセレクタ100点 x 1 = 100点 */
.box { color: red; } /* classセレクタ10点 x 1 = 10点 */
.boxes .box { color: yellow; } /* classセレクタ10点 x 2 = 20点 */
div span { color: purple; } /* 要素セレクタ1点 x 2 = 2点 */
span { color: maroon !important; } /* 要素セレクタ1点 x 1 = 1点(但し、!importantが付いているので最優先) */
上の例であれば、通常は点数の最も高いidをセレクタに指定したcolor: blue;
が適用されます。
ところが、!important
をつけるとその優先度に関係なく強制的にそこで指定したルールが適用されます。
つまりいくら真面目に設計していても!important
を付けてしまうと、既存のスタイルのルールを破壊してしまったり、機能追加時にも新規のルールを書いても適用されないなどの不幸が訪れます。
デバッグ時に一時的に付けて動作を確認する場合にはサクッと試せるため手段の一つとしては有効ですが、最終奥義だと肝に命じておいた方が良いと思います。
span { color: maroon !important; }
$ csslint sample.css
1: warning at line 1, col 8
Use of !important
span { color: maroon !important; }
19. Disallow non alphabetical
Rule ID : order-alphabetical
https://github.com/CSSLint/csslint/wiki/Disallow-non-alphabetical
CSSのプロパティの記述をアルファベット順に書いた方が良いというルールです。
個人的には、正直どちらでも良いのではと思いますが、私が感じたメリットとデメリットを書いてきます。
プロパティがたくさんある場合に意図しないオーバーライドを防止できるのがメリットとしては大きいのかなと思います。
意図しないオーバーライドを招く可能性がある例を示します。
.box {
color: red; /* 先頭のプロパティ */
/***********
:
: ここにいくつものプロパティが書かれていると想定して下さい
:
************/
color: blue; /* 先頭のプロパティで書いているのを忘れて末尾のプロパティでredを上書きしている */
}
.box2 {
margin-top: 20px;
margin-bottom: 10px;
/***********
:
: ここにいくつものプロパティが書かれていると想定して下さい
:
************/
margin: 0; /* 先頭で書いた上下のmarginをリセットしてしまっているため、
margin: 0;が期待通りのデザインであれば、
先頭のコードはゴミとして残してしまう可能性がある */
}
また、width
(幅)とheight
(高さ)のようなプロパティはセットで続けて書いた方が見やすいと感じる方いるのではないでしょうか。
こういう場合に、アルファベット順にソートすると他のプロパティに挟まれたり離れてしまうため人間的には見辛いと感じる人もいるかもしれません。
.box {
font-size: 16px;
border-radius: 10px;
color: red;
}
$ csslint sample.css
1: warning at line 1, col 1
Rule doesn't have all its properties in alphabetical order.
.box {
CSSのアクセシビリティに関するルール
20. Disallow outline:none
Rule ID : outline-none
https://github.com/CSSLint/csslint/wiki/Disallow-outline:none
outline
プロパティは要素の周りを線で囲んでくれますが、border
プロパティのようにレイアウトやサイズは変わりません。
フォーカスのアウトラインはページ上のどこにフォーカスがあるかを視覚的に示すためのものであり、アクセシビリティにとって重要です。
例えば、IEやFirefoxでは以下のように選択した要素が点線で囲まれます。
これを消したくなってoutline: none;
と書いてしまうことはあるかもしれませんが、アクセシビリティ的にはあまり好ましくはありません。
デザインを適用するためにブラウザが標準で出している点線をどうしても消したいという場合は:focus
付きのセレクタで定義すると良いです。
a:focus {
border: 0; /* 囲み枠を付けたい場合は、border: 1px solid red;のように指定すれば良い */
outline: none;
}
/** outlne: noneを指定[NG] **/
a {
outline: none;
}
$ csslint sample.css
1: warning at line 2, col 1
Outlines should only be modified using :focus.
.box {
OOCSS(オブジェクト指向CSS)に関するルール
21. Disallow qualified headings
Rule ID : qualified-headings
https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings
見出し要素(h1
〜h6
)は、オブジェクト指向CSSの組み込みオブジェクトと見なされ、外観はサイト全体で一貫している必要があります。
そのため、トップレベルのスタイルとして定義します。
これにより 視覚的な一貫性とパフォーマンスが向上するだけでなく、メンテナンスも容易になり、サイト全体でスタイルを再利用 することができます。
/** ページの特定の要素(h3)のルールを定義[NG] **/
.box h3 {
color: red;
}
/** トップレベルで定義[OK] **/
h1 { color: green; }
h2 { color: blue; }
$ csslint sample.css
1: warning at line 2, col 6
Heading (h3) should not be qualified.
.box h3 {
22. Headings should only be defined once
Rule ID : unique-headings
https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once
qualified-headings
でも説明しましたが、見出し要素は何度も定義する必要がありません。
仮に何か見た目などに差があるのであれば、別のクラスを定義すれば見出し要素の再定義をする必要はありません。
例えば、2つのh1
要素対して、一方を下線付きのものにしてみるとこのようになります。
h1
要素を使って一方だけを再定義することもできますが、他との違いをクラスとして表現することで対応することもできます。
以下の例であれば当然ですが.underline
はh1
要素以外の要素に対しても再利用できます。
<h1>見出しh1</h1>
<h1 class='underline'>見出しh1(下線付き)</div>
h1 { color: blue; }
.underline { text-decoration: underline; }
/** 見出し要素を再定義[NG] **/
h1 { color: blue; }
.box h1 { text-decoration: underline; }
/** 擬似要素は問題ない[OK] **/
h2 { color: red; }
h2:hover { color: green; }
h2:last-child { text-decoration: underline; }
$ csslint sample.css --ignore=qualified-headings
1: warning at line 3, col 6
Heading (h1) has already been defined.
.box h1 { text-decoration: underline; }
2: warning
You have 2 h1s defined in this stylesheet.
各種ブラウザの互換性に関するチェックするルール
ブラウザの互換性に関するルールがいくつかあります。
互換性に関するルールの多くは古いブラウザ向けのものです。
個人的にはアプリケーションのサポートするブラウザがある程度新しいものであれば、あまり注目するものではないかなと思いました。
23. Disallow adjoining classes
Rule ID : adjoining-classes
https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes
複数のクラス名を続けて書く絞り込みに対応してないブラウザ(IE6以前)もあります。
例えば次のようなHTMLに対して、.large-box.box
の要素にスタイルをあてようとした場合、クラス名の絞り込みに対応していないブラウザであれば最後のクラス(ここでは.box
)がセレクタとして扱われるため意図しない結果(BOX/LARGE BOX/SMALL BOX全てにfont-size: large
が適用される)となってしまいます。
<div class="box">BOX</div>
<div class="large-box box">LARGE BOX</div> <!-- この要素だけスタイルをあてたい -->
<div class="small-box box">SMALL BOX</div>
/** 複数のクラス名を付けて絞り込み[NG] **/
.large-box.box {
font-size: large;
}
/** 単一のクラス名のみで定義[OK] **/
.small-box {
font-size: small;
}
$ csslint sample.css
1: warning at line 2, col 1
Adjoining classes: .large-box.box
.large-box.box {
24. Disallow box-sizing
Rule ID : box-sizing
https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing
box-sizing
プロパティはIE6, IE7ではサポートされないため警告が出ます。
.box {
box-sizing: content-box;
}
$ csslint sample.css
1: warning at line 2, col 3
The box-sizing property isn't supported in IE6 and IE7.
box-sizing: content-box;
25. Require compatible vendor prefixes
Rule ID : compatible-vendor-prefixes
https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes
CSS3のプロパティの多くはベンダー接頭辞付き(-moz-
,-webkit-
,-o-
,-ms-
)プロパティで多くのブラウザを対応しています。
ベンダー接頭辞付きのプロパティを指定するなら他のものもまとめて書いておいたほうが良いという警告が出ます。
/** -webkit-transformのみを定義[NG] **/
.box {
-webkit-transform: translate(50px, 100px);
transform: translate(50px, 100px);
}
/** ベンダー接頭辞付きのプロパティを全て定義[OK] **/
.box2 {
-ms-transform: translate(50px, 100px);
-moz-transform: translate(50px, 100px);
-o-transform: translate(50px, 100px);
-webkit-transform: translate(50px, 100px);
transform: translate(50px, 100px);
}
$ csslint sample.css
1: warning at line 3, col 3
The property -ms-transform is compatible with -webkit-transform and should be included as well.
-webkit-transform: translate(50px, 100px);
上の結果では、-ms-transform
のみの警告が表示されていますが、http://csslint.net/ で確認すると残りの全て(-moz-
,-o-
,-ms-
)の警告が出ていました。(これについては詳しくは調べていません)
26. Require all gradient definitions
Rule ID : gradients
https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions
compatible-vendor-prefixes
のチェックとで同じような感じでグラデーションに関してのルールです。
CSSグラデーションはまだ標準の実装がないため、指定が足らなければ全てのベンダー接頭辞付きの指定で書くのが好ましいという警告が出ます。
/** -moz-linear-gradientのみを定義[NG] **/
.box {
background: -moz-linear-gradient(left, blue, white);
}
/** ベンダー接頭辞付きで全てを定義[OK] **/
.box2 {
background: -moz-linear-gradient(left, blue, white);
background: -webkit-gradient(left, blue, white);
background: -webkit-linear-gradient(left, blue, white);
background: -ms-linear-gradient(left, blue, white);
background: -o-linear-gradient(left, blue, white);
}
$ csslint sample.css
1: warning at line 2, col 1
Missing vendor-prefixed CSS gradients for Webkit (Safari 5+, Chrome), Old Webkit (Safari 4+, Chrome), Opera 11.1+.
.box {
27. Disallow negative text-indent
Rule ID : text-indent
https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent
direction
プロパティの値がrtl
(文字表記の方向が右から左)または未指定の場合に、テキストのインデントに負の数を指定すると正しく表示されない可能性があるため警告が出ます。
明示的にdirection: ltr;
としておけば負の数でも警告が出ません。
また、text-indent
の値にpx
やem
の有無はチェックに関係しません。
/** 表示方向が未定義[NG] **/
.box {
text-indent: -999px;
}
/** 表示方向を右から左と定義[NG] **/
.box2 {
direction: rtl;
text-indent: -999px;
}
/** 表示方向を左から右と定義[OK] **/
.box3 {
direction: ltr;
text-indent: -999px;
}
$ csslint sample.css
1: warning at line 3, col 3
Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.
text-indent: -999px;
2: warning at line 9, col 3
Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.
text-indent: -999px;
28. Require standard property with vendor prefix
Rule ID : vendor-prefix
https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix
ベンダー接頭辞付きのプロパティを書いたらそのあとに標準プロパティも書くようにした方が好ましいという警告が出ます。
ベンダー接頭辞付きのプロパティのみの定義、または標準プロパティは書かれていてもベンダー接頭辞付きのプロパティの後に書かれていない場合にも警告が出ます。
/** ベンダー接頭辞付きのプロパティのみを定義[NG] **/
.box {
-moz-border-radius: 10px;
}
/** ベンダー接頭辞付きのプロパティの前に標準プロパティを定義[NG] **/
.box2 {
border-radius: 20px;
-moz-border-radius: 20px;
}
/** ベンダー接頭辞付きのプロパティの後に標準プロパティを定義[OK] **/
.box3 {
-moz-border-radius: 30px;
border-radius: 30px;
}
$ csslint sample.css
1: warning at line 3, col 3
Missing standard property 'border-radius' to go along with '-moz-border-radius'.
-moz-border-radius: 10px;
2: warning at line 9, col 3
Standard property 'border-radius' should come after vendor-prefixed property '-moz-border-radius'.
-moz-border-radius: 20px;
29. Require fallback colors
Rule ID : fallback-colors
https://github.com/CSSLint/csslint/wiki/Require-fallback-colors
CSS3ではrgba()
, hsl()
, hsla()
を含むいくつかの新しいカラーフォーマットが追加されました。
ブラウザのCSSパーサーは名前や値が不明なプロパティはスルーされます。
例えば、IE8以前の古いブラウザではrgba()
, hsl()
, hsla()
は認識されないためそれらを含む定義は全て破棄されます。
新しいカラーフォーマットのみで色指定をすると、一部のブラウザでは認識されないためスタイルが当たらないという警告が出ます。
保険として、古いフォーマットでも書いておいた方が良いということだと思います。
/** rgba()のみで定義[NG] **/
.box {
color: rgba(255, 0, 0, 0.5);
}
/** 新旧フォーマットを定義しているが、順番が逆で上書きされてしまう書き方[NG] **/
.box2 {
color: rgba(255, 0, 0, 0.5);
color: red;
}
/** 新旧フォーマットがあり、記述する順番も正しい[OK] **/
.box3 {
color: red;
color: rgba(255, 0, 0, 0.5);
}
$ csslint sample.css
1: warning at line 3, col 3
Fallback color (hex or RGB) should precede RGBA color.
color: rgba(255, 0, 0, 0.5);
2: warning at line 8, col 3
Fallback color (hex or RGB) should precede RGBA color.
color: rgba(255, 0, 0, 0.5);
30. Disallow star hack
Rule ID : star-property-hack
https://github.com/CSSLint/csslint/wiki/Disallow-star-hack
スターハックとは、IE8以前のブラウザにのみCSSプロパティを適用するテクニックです。
*(アスタリスク)をプロパティ名の直前に置くことで、古いIEは*(アスタリスク)を読み飛ばして通常のプロパティとして読み込み、他のブラウザはそれ自体が無視されます。
.box {
width: 100px;
*width: 200px; /* IE8以前のブラウザにのみwidth:200pxを指定 */
}
$ csslint sample.css
1: warning at line 3, col 3
Property with star prefix found.
*width: 200px; /* IE8以前のブラウザにのみwidth:200pxを指定 */
31. Disallow underscore hack
Rule ID : underscore-property-hack
https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack
アンダースコアハックはIE7以前にのみCSSプロパティを適用するテクニックです。
_(アンダースコア)をプロパティ名の直前に置くことで、古いIEは_(アンダースコア)を読み飛ばして通常のプロパティとして読み込み、他のブラウザはそれ自体が無視されます。
.box {
width: 100px;
_width: 200px; /* IE7以前のブラウザにのみwidth:200pxを指定 */
}
$ csslint sample.css
1: warning at line 3, col 3
Property with underscore prefix found.
_width: 200px; /* IE7以前のブラウザにのみwidth:200pxを指定 */
32. Bulletproof font-face
Rule ID : bulletproof-font-face
https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face
@font-face
を使用して複数のフォントタイプを宣言すると、古いIE(IE8以下)で404が表示されます。
原因はIEがフォント宣言を解析する方法のバグで、括弧の始めから終わりまでをファイルとしてロードしようとするためです。
回避方法としては、最初のurlの直後に?(疑問符)を追加することでEOTファイルだけをロードして、残りのプロパティ値をクエリ文字列として認識させる方法があります。
/** 最初のurlの直後に?を付けない[NG] **/
@font-face {
font-family: 'MyWebFont';
src: url('webfont.eot') format('embedded-opentype'), /* IE6-IE8 */
url('webfont.woff') format('woff'), /* Modern Browsers */
url('webfont.ttf') format('truetype'), /* Safari, Android, iOS */
url('webfont.svg#svgFontName') format('svg'); /* Legacy iOS */
}
/** 最初のurlの直後に?を付ける[OK] **/
@font-face {
font-family: 'MyWebFont';
src: url('webfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('webfont.woff') format('woff'), /* Modern Browsers */
url('webfont.ttf') format('truetype'), /* Safari, Android, iOS */
url('webfont.svg#svgFontName') format('svg'); /* Legacy iOS */
}
$ csslint sample.css
@font-face declaration doesn't follow the fontspring bulletproof syntax.
src: url('webfont.eot') format('embedded-opentype'), /* IE6-IE8 */
最後に
全てのルールを眺めてみて、一通り使ってみたので所感を書きます。
基本的にルール1つ1つはシンプルで、「まぁ、確かにそうしたほうが良いかもね!」と思えるものが結構ありました。
複数人で開発する場合は、自分たちで決めなくても共通のルールができます。
また、CSSLintを走らせてみると「結構警告出てるな・・・」や「自分たち結構良いコード書いてるらしい」など現状を把握することもできます。
導入する上で、このルールは自分たちの開発には向いていないという場合には、そのルールを除外すれば良いだけです。
ソースコードレビューで「CSSLintはパスしています」と書いていれば、レビュアーも他の所を見るのにより集中できるかもしれません。
それに、こういうのは人間がいちいち口を出すよりも機械に指摘される方が良いかなと思います。
まとめ記事を書きながら、初めて知ったことや今まで気にしてなかったことなど非常に勉強になりました。
この記事を読んでいただいている皆様の役に立てれば幸いです。
最後まで読んでいただきありがとうございました m_ _m