こんにちは!
CSSを使っていると、時々うまくスタイルが適用されないことがありますよね。
私もフロントエンドエンジニアとして、日々JavaScriptなどのプログラミング言語で開発を行っていますが、CSSをうまく使いこなせずに困ってしまうことがよくあります。
プログラミング言語には詳しいけれども、HTML/CSSについてはあまり詳しくないまま使ってしまっているエンジニアも少なくないでしょう。
そこで今回は、私のようなエンジニアでも美しいCSSを書きたいと思い、CSSの詳細度について学びました。その学んだ内容を、説明しながら個人的にも良いアウトプットにつなげたいと考え、解説していきます。
詳細度とは?
MDNの説明だとこんな感じに書かれています。
詳細度 (Specificity) は、ある要素に最も関連性の高い CSS 宣言を決定するためにブラウザーが使用するアルゴリズムで、これによって、その要素に使用するプロパティ値が決定されます。詳細度のアルゴリズムは、CSS セレクターの重みを計算し、競合する CSS 宣言の中からどのルールを要素に適用するかを決定します。
詳細度 – CSS: カスケーティングスタイルシート | MDN
ある要素に対して、競合する複数のCSS宣言があったときに、どのルールを適用するかを決めるために用いられるアルゴリズムのことのようです。
そのアルゴリズムでは、競合するCSS宣言から最も関連性の高いCSS宣言を決め、適用します。
詳細度の計算方法
では、そのアルゴリズムではどうやって詳細度を計算し、関連性の高いCSS宣言を決定しているのでしょう?
基本的にはID, クラス, 要素の3種類のセレクタに対応するID, CLASS, TYPEという3つの分類に分けて計算します。(ID – CLASS – TYPEと記述するようです)
セレクタの3つの分類
- ID
- IDセレクタが該当します。
#hoge
みたいな感じ。 - 一致するセレクタに含まれるIDごとに、1 – 0 – 0 を追加します。
- IDセレクタが該当します。
- CLASS
- クラス
.fuga
や、属性[type=”radio”]
、擬似クラス:hover
などのセレクタが該当します。 - それぞれで、0 – 1 – 0 を追加します。
- クラス
- TYPE
- 要素
h1
,h2
,p
など、擬似要素::before
などのセレクタが該当します。 - それぞれで0 – 0 – 1を追加します。
- 要素
上記の3種類の分類を左から右の順番で比較し、数値が大きいほうのCSS宣言を適用します。
例を示すと以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 |
#myElement input.myClass { color: red; } /* 1-1-1 - ID列が一番大きいのでこのCSS宣言が適用される */ input[type="password"]:required { color: blue; } /* 0-2-1 */ html body main input { color: green; } /* 0-0-4 */ |
上記のセレクタが全て同じinput
要素を対象とした場合、IDで一番大きい値を示している color: red;
が適用されます。
1 2 3 4 5 6 7 |
#myElement { color: yellow; /* 1-0-0 */ } #myApp [id="myElement"] { color: green; /* 1-1-0 - CLASS列がより大きいのでこのCSS宣言が適用される */ } |
上記の例では、ID列が同じ値になっています。その場合はCLASS列の優劣で判定します。
CLASS列がより大きい、color: green;
が適用されます。
示してきた2つの例より、ID → CLASS → TYPEの順に数の大小を判定し、詳細度の大きい値のCSS宣言を適用していることがわかります。
3つの例外
擬似クラスの例外
擬似クラスはCLASS列として計算されますが、:is()
, :not()
, :has()
は詳細度の計算で、擬似クラスとして見なされず、CLASS列として計算されません。ただし、括弧内の引数は詳細度の計算に利用されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
p { /* 0-0-1 */ } :is(p) { /* 0-0-1 */ } h2:nth-last-of-type(n + 2) { /* 0-1-1 */ } h2:has(~ h2) { /* 0-0-2 */ } div.outer p { /* 0-1-2 */ } div:not(.inner) p { /* 0-1-2 */ } |
:where()
も擬似クラスの例外の一つで、CLASS列として計算されません。
また、:is()
, :not()
, :has()
とも違い、括弧内の引数も詳細度の計算に利用されません。
1 2 3 4 5 6 7 8 9 10 |
:where(#defaultTheme) a { /* 0-0-1 */ color: red; } footer a { /* 0-0-2 */ color: blue; } |
インラインスタイル
HTMLの各要素にはstyle属性を使用してインラインスタイルを追加することができます。
1 2 |
<p style="color: red;">これは赤になります。</p> |
通常のCSS宣言を常に上書きすることができるので、最も高い詳細度を持つと考えることができます。
インラインスタイルのみ例外として、1 – 0 – 0 – 0 というID列よりも高い詳細度を保有すると考えても問題ないです。
!important
!important
を使用することで、詳細度を無視しインラインスタイルをも凌駕してスタイルを適用することができる、最強のCSS宣言になります。
1 2 |
<p class="yellow" style="color: red;">これは青になります。</p> |
1 2 3 4 5 6 7 8 |
p { color: blue !important; } .yellow { color: yellow; } |
MDNにも記述されていますが、!important
を使用して詳細度を上書きすることは悪しき習慣です。
!important
を使うことでどんなスタイルでも上書きすることができます。
しかし、!important
をさらに上書きすることは難しく、重ねて!important
を使うしかなくなります。
CSSのいたる所に!important
が付与されてしまい、新しくスタイルを適用しようにもなかなか反映されない。。。保守性の欠片もないCSSになりかねないのです。。。
!important
を使用する際は本当に必要なのか確認してから付与する必要があります。
また、なぜ必要なのかはコメントとして残しておきましょう。
セレクタの用途
各セレクタとその詳細度について説明してきましたが、そもそも色々あるセレクタはどう使い分ければいいのでしょうか?
idセレクタ、クラスセレクタ、要素セレクタにわけて説明していきます。
要素セレクタ
HTML要素名で指定する要素セレクタは指定した要素全てが対象となるためCSSのリセットや初期設定に利用します。
1 2 3 4 5 6 7 8 9 10 11 12 |
span { background-color: yellow; } p { color: blue; } a { color: red; } |
クラスセレクタ
クラスセレクタはピリオド.
でクラスを指定し、クラスが適用されている全ての要素にスタイルを適用します。
クラスセレクタは以下の理由から一番よく使われるセレクタだと個人的には考えています。
- 作成者自身がスタイルの適用範囲を決めることができる
- 他のクラスを要素に割り当てることで、上書きも容易
Webサイトを作成していて、何らかのスタイルを適用したい時にまず一番最初に試すのが、クラスセレクタです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
.notebox { border: 4px solid #666; padding: .5em; } .notebox.warning { border-color: orange; font-weight: bold; } .notebox.danger { border-color: red; font-weight: bold; } |
IDセレクタ
#
でIDを指定します。IDセレクタはそのidが設定されている要素を選択します。
IDはドキュメントの中で一度しか使用できず、非常に特別なセレクタとなります。
なので前述した詳細度も一番大きい値になります。
IDセレクタに関しては、他のセレクタを上書きすることができます。
しかし、IDセレクタに適用されたスタイルをさらに上書きすることは、より高い詳細度を適用する必要があるので非常に難しくなります。
なので可能ならIDセレクタは使用せずに、クラスセレクタを使用するほうが良さそうです。
どうしてもIDセレクタを使わざるを得ない場合のみ、この方法を使用しますが基本的には使いません。
1 2 3 4 5 6 7 8 |
#one { background-color: yellow; } h1#heading { color: rebeccapurple; } |
おわりに
今回は詳細度の話でした。
CSSはまだまだ奥深いので、色々勉強していきたいです。
エンジニアは割とプログラミング言語を重視しがちですが、HTMLやCSSも気に掛ける必要があるかなと思います。
「モダンで保守性の高いJavaScriptで書かれたソースコードだけど、CSSは汚い」みたいなことが意外とありますよね。綺麗なCSSを書けるようにしていきたいです!