!important だらけの CSS にお別れを

CSS を記述するときに記述したスタイルの適用優先順位を理解していないと思ったようにスタイルが適用されなかったり、後からメンテナンスするときに効率的な作業ができ...

CSS を記述するときに記述したスタイルの適用優先順位を理解していないと思ったようにスタイルが適用されなかったり、後からメンテナンスするときに効率的な作業ができなかったりと、思わぬところで無駄な時間をとられたりします。

で、結果として !important 宣言を連発、気が付いたら CSS が !important 宣言だらけになるなんてオチが待ってたりするわけですが、そうならないためにも CSS が適用される際の優先順位について簡単におさらいをしてみたいと思います。

CSS は、「Cascading Style Sheets」 という名前の通り、「Cascading」 されて (段階的に) 適用されます。よって、簡単に言ってしまえば先頭に記述されたものから適用されていき、後に記述したものが上書きされていくので、結果、「後に記述したものほど優先順位が高くなる」 というルールが基本になります。

また、合わせてスタイルの 「specificity」 (個別性) により、優先順位の決定をしています。この 「specificity」 を理解していないと、冒頭に書いた !important 宣言を連発の悲劇が発生するというわけですね。

さて、その 「specificity」 はどのような決まりになっているかというと、W3C の CSS2.1 仕様書には下記のように記載されています。

  • count 1 if the declaration is from is a 'style' attribute rather than a rule with a selector, 0 otherwise (= a) (In HTML, values of an element's "style" attribute are style sheet rules. These rules have no selectors, so a=1, b=0, c=0, and d=0.)
  • count the number of ID attributes in the selector (= b)
  • count the number of other attributes and pseudo-classes in the selector (= c)
  • count the number of element names and pseudo-elements in the selector (= d)

簡単に訳すと、

  • style 属性がある場合は、1をカウント (= a)
  • セレクタに含まれている id 属性の数をカウント (= b)
  • セレクタに含まれている id 以外の属性と、擬似クラスの数をカウント (= c)
  • セレクタに含まれている 要素、擬似要素の数をカウント (= d)

で、同じく 仕様書に記載されている、下記の計算例を見てみましょう。

Some examples:

*             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li            {}  /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li         {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li      {}  /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up]{}  /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red  {}  /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level  {}  /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y         {}  /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style=""          /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

例えば、

/*style 1*/
 div#sample p.test span {} 
/*style 2*/
 div#sample p span {}

なんて 2種類の記述があった場合、

/*style 1*/
 a=0 b=1 c=1 d=3 -> specificity = 0,1,1,3
/*style 2*/
 a=0 b=1 c=0 d=3 -> specificity = 0,1,0,3

となるので、113 > 103 となり、「style 1」 が優先的に適用されると。

単純に該当する数を数え、a を 1000 の位、以下順に b=100、c=10、d=1 の位と当てはめて値をだすだけなので、計算自体は簡単ですね。

要は、大まかに指定されたものよりも、より詳細に指定されたものの方が優先順位が高いわけです。例えば、同じ山田さんを示すにも、「日本の山田さん」 より、「東京都○○町の山田さん」 を優先するわけです。この指定の詳細さを 「specificity」 (個別性) というわけ。

その上で上記の計算からでた値が同じだった場合は、先に書いた CSS の基本ルール、「後に記述したものほど優先順位が高くなる」 が適用されます。例えば、

<head>
<style type="text/css">
 p.exp {color:red;}
 p.exp2 {color:blue;}
</style>
</head>
<body>
 <p class="exp exp2">sample text</p>
</body>

のようなソースがあったとき、2つのスタイルの計算上の優先順位は同列ですが、HTML に読み込む際の記述で 「exp2」 の方が後に記述されていますので、そちらが優先されて適用されます。

で、問題の !important 宣言ですが、上記を踏まえた上でどうしても優先順位を逆転させたいときなどに使用するにとどめるのが良いかと思いますよ。

なお、最終的なスタイル適用の優先順位は、

  1. !important 宣言付きユーザースタイルシート
  2. !important 宣言付き制作者スタイルシート
  3. 制作者スタイルシート
  4. ユーザースタイルシート

となります。

The first rule in the user's style sheet in the following example contains an "!important" declaration, which overrides the corresponding declaration in the author's style sheet. The second declaration will also win due to being marked "!important". However, the third rule in the user's style sheet is not "!important" and will therefore lose to the second rule in the author's style sheet (which happens to set style on a shorthand property). Also, the third author rule will lose to the second author rule since the second rule is "!important". This shows that "!important" declarations have a function also within author style sheets. (CSS2.1 !important rules)

ちなみに、上記の説明はすべて CSS2.1 (Working Draft) の仕様書を基に書いていますが、CSS1 の時と変更されている点があります。また、あくまで仕様書での話ですので、ブラウザの実装によっては相違があるかもしれません。

記事をここまで御覧頂きありがとうございます。
この記事が気に入ったらサポートしてみませんか?