💨

CSS詳細度の基本と例外

2024/03/17に公開

CSS が適用されないことがあり、詳細度が原因ということを知ったのでその備忘録として残します

詳細度とは?

MDN の説明だとこんな感じに書かれています。

詳細度 (Specificity) は、ある要素に最も関連性の高い  CSS 宣言を決定するためにブラウザーが使用するアルゴリズムで、これによって、その要素に使用するプロパティ値が決定されます。詳細度のアルゴリズムは、CSS セレクターの重みを計算し、競合する CSS 宣言の中からどのルールを要素に適用するかを決定します。

https://developer.mozilla.org/ja/docs/Web/CSS/Specificity

ある要素に対して、競合する複数の CSS 宣言があったときに、どのルールを適用するかを決めるために用いられるアルゴリズムのことのようです。
そのアルゴリズムでは、競合する CSS 宣言から最も関連性の高い CSS 宣言を決め、適用します。

詳細度の計算方法

では、そのアルゴリズムではどうやって詳細度を計算し、関連性の高い CSS 宣言を決定しているのでしょう?
基本的には ID, クラス, 要素の 3 種類のセレクタに対応する ID, CLASS, TYPE という 3 つの分類に分けて計算します。(ID - CLASS - TYPEと記述するようです)

セレクタの 3 つの分類

  • ID
    • ID セレクタが該当します。#hogeみたいな感じ。
    • 一致するセレクタに含まれる ID ごとに、1 - 0 - 0 を追加します。
  • CLASS
    • クラス .fuga や、属性 [type=”radio”] 、擬似クラス :hover などのセレクタが該当します。
    • それぞれで、0 - 1 - 0 を追加します。
  • TYPE
    • 要素 h1, h2, p など、擬似要素 ::before などのセレクタが該当します。
    • それぞれで0 - 0 - 1を追加します。

上記の 3 種類の分類を左から右の順番で比較し、数値が大きいほうの CSS 宣言を適用します。
例を示すと以下のようになります。

#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; が適用されます。

#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 列として計算されません。ただし、括弧内の引数は詳細度の計算に利用されます。

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()とも違い、括弧内の引数も詳細度の計算に利用されません。

:where(#defaultTheme) a {
  /* 0-0-1 */
  color: red;
}

footer a {
  /* 0-0-2 */
  color: blue;
}

インラインスタイル

HTML の各要素には style 属性を使用してインラインスタイルを追加することができます。

<p style="color: red;">これは赤になります。</p>

通常の CSS 宣言を常に上書きすることができるので、最も高い詳細度を持つと考えることができます。
インラインスタイルのみ例外として、1 - 0 - 0 - 0 という ID 列よりも高い詳細度を保有すると考えても問題ないです。

!important

!importantを使用することで、詳細度を無視しインラインスタイルをも凌駕してスタイルを適用することができる、最強の CSS 宣言になります。

<p class="yellow" style="color: red;">これは青になります。</p>
p {
  color: blue !important;
}

.yellow {
  color: yellow;
}

MDN にも記述されていますが、!importantを使用して詳細度を上書きすることは悪しき習慣です。
!importantを使うことでどんなスタイルでも上書きすることができます。
しかし、!importantをさらに上書きすることは難しく、重ねて!importantを使うしかなくなります。
CSS のいたる所に!importantが付与されてしまい、新しくスタイルを適用しようにもなかなか反映されない。。。保守性の欠片もない CSS になりかねないのです。。。
!importantを使用する際は本当に必要なのか確認してから付与する必要があります。
また、なぜ必要なのかはコメントとして残しておきましょう。

GitHubで編集を提案

Discussion