ウミ
クル

CSS優先順位 カスケードと継承について

CSS は Cascading Style Sheets の略ですが、今回はカスケード=“階段状に連続する滝”という名前がついている理由に迫ります。
一つの要素に複数のスタイルが指定されて競合した場合、どのように優先順位が付けられているのか。まさに“階段状に連続する滝”のような流れで適用が決定されていく様子を、下の図とともに説明します。

CSS優先順位

カスケード

スタイルの競合が発生した場合は、以下の3段階のチェックにより優先順位が決定されます。

  • 重要性
  • 詳細度
  • コード順

まずは重要性の各項目をチェックし、優先順位を決定。
重要性が同じで優先順位がつかない場合は次の詳細度チェックに進み、詳細度の各項目をチェック。
詳細度も同じ場合には最後にコード順を確認し、一番下に書かれたコードのスタイルを採用します。

1. 重要性

カスケードの上流にある重要性チェックには、5つの項目があります。

  • ① ユーザーによる !important 宣言
  • ② web開発者による !important 宣言
  • ③ web開発者による通常スタイル宣言
  • ④ ユーザーによる通常スタイル宣言
  • ⑤ ブラウザデフォルト (user agent)

優先順位は①が一番高く、下に下がるにつれて優先順位が下がります。
フォントサイズを例にとって説明します。
重要性の中で一番優先順位の低い⑤ブラウザデフォルトのフォントサイズは16px。
ブラウザの環境設定で、ユーザーがフォントサイズを大きくした場合は④としてスタイルが宣言され、⑤を上書きします。
私たちがweb開発者として書いているCSSは、③に当てはまるため、④のユーザーによるスタイル宣言を上書きします。
そのため④の宣言を尊重したい場合には、ルートのフォントサイズ設定を px ではなく % にすると、④のフォントサイズをもとに計算されることになり、ユーザーによる宣言を反映することができます。
例)html { font-size: 62.5%; }

2. 詳細度

詳細度は4つの値を使用して測定されます。

  • ① インラインスタイル指定(html style属性)
  • ② IDセレクタ
  • ③ クラスセレクタ、疑似クラス、属性セレクタ
  • ④ 要素セレクタ、疑似要素

優先順位は①が一番高く、下に下がるにつれて優先順位が下がります。
それぞれの値は別々に集計され、バージョン表記のように( 0, 0, 0, 0 )と並べられます。
例えば、クラスセレクタを120個組み合わせたとしても ( 0, 0, 120, 0 )、一つのIDセレクタ( 0, 1, 0, 0 )のスタイルを上書きすることはできません。
ユニバーサルセレクタ (*)、コンビネータ (+、>、~) 、否定擬似クラス(:not)は詳細度に影響を及ぼしません。
以下にEstelle Weylさんが作成した、とてもわかりやすいイラストがあるのでご覧ください。
プランクトン(要素)は何匹いても魚(クラス)には勝てず、魚が何匹いてもサメ(ID)には勝てないことが示されています。
standardista.com "specificity3.pdf"

詳細度 specifishity

3. コード順

重要性と詳細度が同じ場合は、コード順が優先順位の最後の判断材料となります。
一番下に書かれたコードが最新のものと判断され、採用されます。
これは、一つのcssファイル内のコード順だけでなく、html内でのcssファイルのリンク順に関しても言えることなので、注意が必要です。
reset.cssなど、外部のcssを使用する場合は、最初にリンクを貼る。
自分で書いたcssは外部cssの下にリンクし、上書きできる状態に。
メディアクエリのcssは上書きするためのものなので最後にリンクを貼りましょう。

継承

スタイルが採用される過程には、カスケードの他にも継承 (inherit) という重要な仕組みがあります。
継承とは親要素から子要素へスタイルが引き継がれる仕組みで、プロパティにより継承されるものと、継承されないものがあります。

  • 継承されるもの:font-family、font-size、color、line-height などの文字に関連するプロパティ
  • 継承されないもの:width、margin、padding、border など文字関連以外のプロパティ

          .parent {
                font-size: 20px;
                line-height: 150%;
            }
            .child {
                font-size: 10px;
            }
        

継承される値は宣言された値そのものではなく、px に計算された値が継承される。
上の例において、親要素 (.parent) に line-height: 150%; を設定した場合、子要素 (.child) には 150% が継承されるのではなく、計算された 30px(20px × 150% = 30px)が継承されます。
子要素のフォントサイズ 10px に対する150% = 15px にはならないので注意が必要です。


          html {
                box-sizing: border-box;
            }
            *, *::before, *::after {
                box-sizing: inherit;
            }
        

通常は継承されないプロパティに対しては inherit の値を記述することで継承が可能になる。
box-sizing は継承されないプロパティの一つですが、上のようにユニバーサルセレクタに inherit を指定すると、html に設定した border-box を全てのセレクタに継承することができます。

最後に

CSS設計手法の一つであるBEMでは、要素セレクタやIDセレクタによるスタイル指定は推奨されておらず、クラスセレクタを使ってスタイル指定することを基本としています。
これは、上書きしたい時には狙い通りに上書きできるように、詳細度をなるべく均一に保つための手段です。
CSSの優先順位について理解した上で、このようなCSS設計手法を取り入れ、意図した通りにスタイルがはまる、メンテナンスがしやすいコードを書いていきたいですね。

参考

Udemy "Advanced CSS and Sass: Flexbox, Grid, Animations and More!"
standardista.com "css-specificity"
MDN "カスケードと継承"