カスケード、詳細度、継承

このレッスンの目的は、CSS の最も基本的な概念であるカスケード、詳細度、継承について理解を深めることです。これらの概念は、CSS を HTML に適用する方法とスタイル宣言の間の競合を解決する方法を制御するものです。

このレッスンは、他の記事よりもすぐに役立つことが少なく、少し学問的に見えるかもしれませんが、これらの概念を理解することは、後で多くの困難から救ってくれることでしょう。この章を注意深く作業し、概念を理解していることを確認してから、次に進むことをお勧めします。

前提条件: 基本的なソフトウェアがインストールされていること、ファイルの扱い、HTML の基本(HTML 入門の学習)、CSS がどのように動作するかの考え(CSS の第一歩で学習)の基本的な知識を得ていること。
目的: カスケードと詳細度、および CSS の継承の仕組みについて学ぶ。

競合するルール

CSS は Cascading Style Sheets の略で、最初の単語であるカスケード (cascading) を理解することは非常に重要です。カスケードのふるまいは、CSS を理解するための鍵となりえます。

プロジェクトに取り組んでいるとき、要素に適用されているはずの CSS が機能していないと感じることがあります。大抵の場合、この問題は同じ要素に適用される可能性のある 2 つ のルールを作ってしまったことが原因です。カスケードおよび、それと密接に関連する詳細度の概念は、そのような競合が存在する際にどちらのルールを適用するかを制御するメカニズムです。実際に要素にスタイルを設定しているルールがどれなのかは期待と異なる場合があるため、このような仕組みがどのように動作するのかを理解しておく必要があります。

このほかに重要なのは継承という概念です。CSS プロパティによって、親要素の値を既定で継承するものもあれば、継承しないものもあります。これにより、予期しない動作が発生する可能性もあります。

まず重要なものを簡単に見てみましょう。それぞれ順に追っていって CSS でどのように相互作用するかを見ていきます。これらはトリッキーな概念のように見えるかもしれませんが、CSS を書く練習を積んでいくと、その動作が明らかになってくるでしょう。

カスケード

スタイルシートのカスケードは、とてもシンプルに考えるなら、これは発生元と、カスケードレイヤーと、CSS ルールの順序によるということです。同じカスケードレイヤーからの 2 つのルールが適用されており、どちらも詳細度が同じである場合、スタイルシートで最後に定義されたものが使用されます。

下記の例では、<h1> 要素に適用できる 2 つのルールがあります。<h1> のコンテンツは結果的に青く色づけされています。これは、どちらのルールも同じソースにあり、同じ要素セレクターを持ち、したがって詳細度が同じですが、ソース上の順番が最後のものが勝者となるからです。

詳細度

詳細度とは、ある要素にどのプロパティの値を使用するかを決めるために、ブラウザーが使用するアルゴリズムです。複数のスタイルブロックに、同じプロパティを異なる値で設定する異なるセレクターがあり、同じ要素を対象としている場合、その要素に適用されるプロパティの値は、詳細度によって決定されます。詳細度とは、基本的にセレクターの選択がどの程度具体的であるかを示す指標です。

  • 要素セレクターは詳細度が低く、ページ上に現れるその種類のすべての要素を選択するので、あまり重みがありません。擬似要素セレクターは、通常の要素セレクターと同じ詳細度を持ちます。
  • クラスセレクターはより詳細度が高く、ページ上にある特定の class 属性値を保有する要素のみを選択するので、より高い重みを持っています。属性セレクターと擬似クラスは、クラスと同じ重みを持ちます。

下記の例では、再び <h1> 要素に適用できる 2 つのルールがあります。下記の <h1> のコンテンツは、クラスセレクター main-heading によりそのルールが高い詳細度を持つため、結果として赤で表示されています。たとえ、<h1> 要素のセレクターを使用したルールがソースの順序でより下に現れても、クラスセレクターを使用して定義された、より高い詳細度を持つルールが使用されることになります。

詳細度のアルゴリズムについては、後ほど説明します。

継承

ここでは継承についても理解する必要があります。親要素に設定された CSS プロパティ値には、子要素に継承されるものとそうでないものがあります。

例えば、ある要素に colorfont-family を設定すると、異なる色やフォントの値を直接適用しない限り、その中にあるすべての要素もその色やフォントでスタイル設定されます。

いくつかのプロパティは継承されません。例えば、要素に width 50% と設定した場合、すべての子孫要素は親の幅の 50% の幅を取得しません。もしそんなことになるのら CSS を使うととてもイライラするでしょう。

メモ: MDN の CSS プロパティリファレンスページには、「公式定義」という技術情報ボックスがあり、そのプロパティに関するいくつかのデータポイント(継承されるかどうかなど)が記載されています。例として、color プロパティの公式定義の節を参照してください。

これらの概念がどう連携するか理解する

これら3つの概念(カスケード、詳細度、継承)は、どの CSS がどの要素に適用されるかを制御するものです。以下の節では、これらがどのように連携して動作するのかを見ていきます。少し複雑に感じることもあるかもしれませんが、CSS を取得するにつれて覚え始めるでしょうし、忘れたときには常に詳細を調べておくことができます 経験豊富な開発者でも、すべての詳細を覚えているわけではありません。

以下の動画は、Firefox の開発ツールを使用して、ページのカスケードや詳細度などを検査する方法を示しています。

継承を理解する

継承から始めましょう。次の例では <ul> 要素があり、内部にさらに 2 つ のレベルの順序なしリストがネストされています。外側の <ul> に境界線、パディング、文字色が指定されています。

color プロパティは継承されるプロパティです。よって、color プロパティの値は直接の子だけでなく、間接的な子にも適用されています。つまり直接の子である <li> 、それに最初のネストされたリスト内のものにも適用されています。そして 2 番目 にネストされたリストに special クラスを追加し、別の色が適用されています。これは子に継承します。

(前述のように)widthmarginpaddingborder といったプロパティは継承されるプロパティではありません。もし、このリストの例で境界線が子プロパティに掲載されていたら、すべてのリストとリスト項目に境界線が追加されることになります。

CSS のプロパティのページには、そのプロパティが継承されるかどうかが掲載されていますが、プロパティの値がどのようなスタイルを設定するのかを知っていれば、直観的に同じことを推測できることが多いでしょう。

継承の制御

CSS は、継承を制御するための 5 つ の特別なユニバーサルプロパティ値 (universal property values) を提供します。すべての CSS プロパティはこれらの値を受け入れます。

inherit

選択した要素に適用されるプロパティ値を、その親要素と同じものに設定します。これは「継承を有効にする」ことを意味します。

initial

選択した要素に適用されるプロパティ値を、そのプロパティの初期値に設定します。

revert

選択した要素に適用されるプロパティ値を、そのプロパティに適用されている既定値ではなく、ブラウザーの既定スタイル設定にリセットします。この値は、多くの場合 unset のように動作します。

revert-layer

選択した要素に適用されるプロパティ値を、前回のカスケードレイヤーで設定された値にリセットします。

unset

プロパティを自然な値にリセットします。つまり、プロパティが自然に継承される場合は inherit のように動作し、そうでない場合は initial のように動作します。

メモ: それぞれについて、またこれらがどのように動作するのかについては、オリジンの種類を参照してください。

リンクのリストを見ると、ユニバーサル値 (universal values) がどのように機能するかを調べることができます。以下のライブサンプルでは、CSS に変更を加えて何が起こるかを確認できます。HTML と CSS を理解するには、実際にコードを試すのが最善の方法です。

例えば次のようになります。

  1. 2 番目 のリストアイテムには、my-class-1 が適用されています。これは、内部にネストされた <a> 要素に色を継承 (inherit) します。ルールを削除すると、リンクの色はどのように変わるでしょうか?
  2. 3 つ目と 4 つ目のリンクがなぜそのような色になっているのか、お分かりでしょうか? 3 つ目のリンクは initial に設定されています。これはプロパティの初期値(この場合は黒)を使用し、ブラウザーのリンクの既定値である青は使用しない、という意味です。4 番目のリンクは unset に設定されており、これはリンクテキストが親要素の色である緑を使用することを意味しています。
  3. <a> 要素に新しい色を定義した場合、どのリンクの色が変わるでしょうか。例えば、a { color: red; } のようにした場合です。
  4. 次の節の「すべてのプロパティ値のリセット」を読んだ後、また戻ってきて color プロパティを all に変更してみてください。2 つ目のリンクが新しい行になり、箇条書きがあることに注目してください。どのようなプロパティが継承されていると思いますか?

すべてのプロパティ値のリセット

CSS の一括指定プロパティ all を使用して、これらの継承値の 1 つ を(ほぼ)すべてのプロパティに一度に適用できます。その値として、いずれかの継承値(inheritinitialrevertrevert-layerunset のいずれか)を指定することができます。新しく変更を開始する際、既知の開始点に戻ることができるように、変更されたスタイルを元に戻す便利な方法です。

以下の例では 2 つ のブロック引用 (blockquote) 要素があります。最初のスタイルは blockquote 要素自体に適用されます。2 つ目には allunset を設定するように blockquote に適用されるクラスがあります。

all の値を他の有効な値に設定してみて、違いを観察してみてください。

カスケードを理解する

これで、HTML の構造の奥深くにある段落が、本体に適用されている CSS と同じ色になるのは、継承のためだということが理解できたと思います。入門編で、文書内の任意の場所で何かに適用される CSS を変更する方法について理解しました。 CSS を要素に割り当てるか、クラスを作成するかです。これで、複数のスタイルブロックが同じ要素に異なる形で同じプロパティを適用する場合、どの CSS ルールが適用されるかをカスケードで定義する方法を見て取ることができます。

考慮すべき 3 つ の要因がありますが、ここでは重要度の高い順にリストしています。前にあるものは、後のものを無効にします。

  1. ソース順 (Source order)
  2. 詳細度 (Specificity)
  3. 重要度 (Importance)

これらを下から順に、ブラウザーがどうやって CSS を適用しているのかを見ていきましょう。

ソース順

ソース順がカスケードにとって重要であることは、既に見たとおりです。複数のルールを保有し、それらのルールがすべてまったく同じ重みを持っている場合、CSS の最後に来るルールが優先されます。これは、要素自体に近いルールが、最後のルールが勝利して要素のスタイルを取得するまで、前のルールを上書きすると考えることができます。

ソース順が問題になるのは、ルールの詳細度に対する重みが同じである場合だけなので、詳細度について考えてみましょう。

詳細度

あるルールがスタイルシートの後半に来ることは分かっていても、より前の、競合するルールが適用される状況がよくあります。これは、前のルールが より高い詳細度 を有しているため、つまり、より詳細であるため、ブラウザーが要素をスタイル設定すべきものとして選択した場合に起こります。

このレッスンで以前に見たように、クラスセレクターは要素セレクターよりも重みがあるため、クラスのスタイルブロックで定義されたプロパティは、要素のスタイルブロックで定義されたものを上書きすることになります。

ここで注意したいのは、セレクターとそれが選択するテキストや成分に適用されるルールに注目していますが、上書きされるのはルール全体ではなく、複数の場所で宣言されているプロパティだけだということです。

この動作は、CSS の繰り返しを避けるのに役立ちます。一般的な方法としては、基本的な要素には一般的なスタイルを定義し、異なる形の要素にはクラスを作成することです。例えば、下記のスタイルシートでは、レベル 2 の見出しに対して一般的なスタイルを定義し、その後、一部のプロパティと値のみを変更するクラスをいくつか作成しています。最初に定義された値はすべての見出しに適用され、その後、より詳細度の高い値がクラスを持つ見出しに適用されます。

ブラウザーが詳細度を計算する方法を見てみましょう。要素セレクターの詳細度は低く、クラスで上書きできることはすでにわかったはずです。基本的に、セレクターの重みはポイント単位の値で与えられ、これらを合計して特定セレクターの重みが与えられて、他の一致するものと相対して評価することができます。

セレクターが持つ詳細度の量は、3 つ の異なる値(またはコンポーネント)を使用して測定されます。これは、ID、クラス、要素の列を、それぞれ百の位、十の位、一の位として考えることができます。

  • ID: この列は、全体のセレクターの中に含まれるそれぞれの ID セレクターに対して 1 点ずつ評価します。
  • クラス: この列は、クラスセレクター、属性セレクター、擬似クラスが全体のセレクターの中に含まれている場合に 1 点ずつ評価します。
  • 要素: この列は、要素セレクターまたは擬似要素が全体のセレクターの中に含まれている場合に 1 点ずつ評価します。

メモ: 全称セレクター (*)、結合子 (+>~、' ')、詳細度調整セレクター:where()とその引数は、詳細度に影響しません。

否定 (:not())、関係セレクター (:has())、match-any (:is()) 擬似クラス、 CSS 入れ子自身は、詳細度に影響を与えませんが、それらの引数や入れ子ルールには影響を及ぼします。それぞれが詳細度重みアルゴリズムに寄与する詳細度の重みは、最も大きな重みを持っている引数または入れ子ルールのセレクターの詳細度です。

次の表でわかりやすいいくつかの例を示します。これらを試してみて、なぜ詳細度が与えられるのかをしっかり理解してください。セレクターについてはまだ詳しく説明していませんが、MDN の セレクターリファレンスで詳細を参照することができます。

セレクター ID クラス 要素 詳細度の合計
h1 0 0 1 0-0-1
h1 + p::first-letter 0 0 3 0-0-3
li > a[href*="en-US"] > .inline-warning 0 2 2 0-2-2
#identifier 1 0 0 1-0-0
button:not(#mainBtn, .cta) 1 0 1 1-0-1

先に進む前に、実例を見てみましょう。

何が起こっているのでしょうか?まず、この例の最初の 7 つ のルールにのみ関心があり、お気づきのように、各ルールの前に詳細度の値をコメントしてあります。

  • 最初の 2 つ のセレクターはリンクの背景色について競合しています。2 番目 のセレクターには ID セレクター があるのでそれが優先され、青になります。この詳細度は 2-0-1 であり、もう一方は 1-0-1 です。
  • セレクター 3 と 4 は、リンクの文字色のスタイル設定をめぐって競合しています。2 つ目のセレクターが勝って、テキストを白にします。これは、要素セレクターが 1 つ少ないものの、足りないセレクターをクラスセレクターに入れ替えたためで、これはいくつの要素セレクターよりも比重が大きいからです。勝者の詳細度は、1-1-3 対 1-0-4 です。
  • セレクター 5 ~ 7 は、マウスを当てたときのリンクの境界線のスタイル設定をめぐって競合しています。セレクター 6 は、0-2-3 対 0-2-4 の詳細度でセレクター 5 に明らかに負けています。セレクター 6 には、連鎖する要素のセレクターが 1 つ少ないからです。しかし、セレクター 7 はセレクター 5 と同じ数のサブセレクターを持ちますが、要素がクラスセレクターに置き換えられているため、セレクター 5 と 6 の両方に勝っています。つまり、0-3-3 の詳細度が 0-2-3、0-2-4 に対して勝利します。

メモ: セレクターの種類ごとにはそれぞれレベルの詳細度があり、より低い詳細度レベルのセレクターによって上書きすることはできません。例えば 100 万 のクラスセレクターを組み合わせても、1 つ の ID セレクターのルールを上書きすることはできません。

詳細度を評価する最良の方法は、詳細度の高いものから始めて、必要に応じて低いものへ移動しながら、個別に点数をつけることです。ある列のセレクターのスコアが同点である場合のみ、次の列を評価する必要があります。そうでない場合は、詳細度の低いセレクターは詳細度の高いセレクターを上書きすることができないため、無視することができます。

インラインスタイル

インラインスタイル、つまり style 属性内のスタイル宣言は、その詳細度に関わらず、すべての通常のスタイルよりも優先されます。このような宣言はセレクターがありませんが、その固有性は 1-0-0-0 と解釈され、セレクターにいくつの ID があっても、常に他のどの詳細度よりも高い重みを持ちます。

!important

インラインスタイルであっても、上記のすべての計算を覆すために使用できる特別なCSSがあります - !importantフラグです。しかし、使用する際にはとても注意が必要です。このフラグは、個々のプロパティと値のペアを最も詳細なルールとするために使用され、それによって通常のインラインスタイルを含むカスケードのルールが上書きされます。

メモ: !importantフラグの存在を知っておくと、他の人のコードでこのフラグに出会ったときに、それが何であるかを知ることができるので便利です。しかし、絶対に必要でない限り使用しないことを強くお勧めします。 !important フラグはカスケードの通常の動作方法を変更するので、特に大きなスタイルシートでは、CSS の問題をデバッグするのが実に作業しづらくなる可能性があります。

例を見てみましょう。2 つの段落があり、そのうちの 1 つ には ID がついています。

何が起きているのかを見てみましょう。理解しにくい場合は、いくつかのプロパティを削除しながら、どうなるか見てみてください。

  1. 3 番目のルールでは colorpadding が適用されていますが、background-color は適用されていないことようです。なぜでしょうか?ソースオーダーの後の方は、普通は前の方のルールをオーバーライドするため、その観点では 3 つ すべてが適用されるはずです。
  2. とはいえ、クラスセレクターは要素セレクターよりも詳細度が高いため前者のルールが優先されます。
  3. 両方の要素には better という class がありますが、2 番目 の要素には winning という id もあります。ID はクラスよりもより高い詳細度があるため(ページ上では ID を持つ要素は一意に 1 つ しか置けないのに対し、同じクラスを持つ多くの要素がありえるため、ID セレクターの方が非常に限定的になります)、2 番目の要素には赤い背景色と 1 ピクセルの黒い境界線が適用され、最初の要素についてはクラスで指定されたように灰色の背景色となり、境界線は消えます。
  4. 2 番目 の要素は赤い背景が適用されていますが、あるはずの境界線はありません。なぜでしょうか? 2 つ目のルールに !important フラグがあるためです。border: none の後に !important フラグを追加すると、ID セレクターの方がより高い詳細度であっても、この宣言が前のルールの border 値に勝利するということです。

メモ: important 宣言を上書きする唯一の方法は、同じ詳細度を持つ別の important 宣言をソースの順番で後に記載するか、より高い詳細度を持つものを記載するか、前のカスケードレイヤーに important 宣言を記載することです(まだ、カスケードレイヤーについて触れていません)。

!important フラグを使用しなければならない状況の 1 つ としては、コアにある CSS モジュールを編集できない CMS で作業している等で、他の方法では難しい場合にスタイルを実際にオーバーライドしたい場合等があります。でも本当に、回避できる場合は使用しないでください。

CSS の位置の効果

最後に、CSS 宣言の優先順位は、それがどのスタイルシートとカスケードレイヤーで指定されるかに依存することに注意することが重要です。

ユーザーがカスタムスタイルシートを設定して、開発者のスタイルを上書きすることは可能です。例えば、視覚的な障碍を持つユーザーが、アクセスするすべてのウェブページのフォントサイズを通常の 2 倍に設定して、読みやすくすることができます。

カスケードレイヤーで開発者のスタイルを宣言することも可能です。レイヤーで宣言されたスタイルをレイヤー以外のスタイルで上書きしたり、先に宣言されたレイヤーのスタイルを後のレイヤーで宣言されたスタイルで上書きしたりすることが可能です。例えば、開発者としてサードパーティのスタイルシートを編集することはできないかもしれませんが、外部スタイルシートをカスケードレイヤーにインポートすることで、サードパーティのセレクターの詳細度を気にせず、インポートしたスタイルをすべて簡単に上書きすることができます。

宣言の上書きの順

競合する宣言は、以下の順序で適用され、後に適用されたものが先に適用されたものを上書きします。

  1. ユーザーエージェントのスタイルシートにおける宣言(例えば、他にスタイルが設定されていない場合に使用される、ブラウザーの既定スタイル)。
  2. ユーザースタイルシート(ユーザーが設定したカスタムスタイル)内の通常の宣言。
  3. 作成者スタイルシートでの通常の宣言(これはウェブ開発者である私たちが設定したスタイルです)。
  4. 作成者スタイルシートにおける important 宣言。
  5. ユーザースタイルシートにおける important 宣言。
  6. ユーザーエージェントスタイルシートにおける important 宣言。

メモ: 優先順位は !important でフラグづけされたスタイルでは逆転します。ウェブ開発者のスタイルシートがユーザーのスタイルシートを上書きするのは理にかなっているので、デザインは意図したとおりに保たれます。しかし、上記のように、ユーザーがウェブ開発者のスタイルを上書きするのに有益な理由がある場合もあり、その場合はルールに !important を使用して実現することができます。

カスケードレイヤーの順序

カスケードレイヤーは高度なトピックで、すぐにこの機能を使用することはないでしょうが、レイヤーがどのようにカスケードされるかを理解することは重要です。

CSS をカスケードレイヤーで宣言する場合、優先順位はレイヤーを宣言した順番で決まります。どのレイヤーの外でも宣言された CSS スタイルは、それらのスタイルが宣言された順に、あたかも最後に宣言されたレイヤーであるかのように、無名のレイヤーに結合されます。競合する通常の(重要でない)スタイルでは、後から定義されたレイヤーが先に定義されたレイヤーより優先されます。しかし、!important が付けられたスタイルでは、順序が逆転し、より早いレイヤーの重要なスタイルが、後続のレイヤーまたは任意のレイヤーの外側で宣言された重要なスタイルより優先されます。インラインスタイルは、レイヤーに関係なく、すべての作成者スタイルよりも優先されます。

別々の形で複数のスタイルブロックを持っており、単一の要素のプロパティに競合する値を提供している場合、レイヤーが宣言されている順序で優先順位が決定されます。レイヤー間の詳細度は重要ではありませんが、単一のレイヤー内での詳細度は依然として重要です。

何が起こっているのかを理解するために、上の例からいくつかのことを説明してみましょう。firstLayersecondLayer の 2 つのレイヤーが順に宣言されました。たとえ secondLayer の詳細度が最も高いとしても、その宣言のプロパティは使用されません。なぜか?レイヤー化されていない通常のスタイルは、詳細度に関係なく、レイヤー化された通常のスタイルよりも優先され、重要なレイヤーのスタイルは、これまた詳細度に関係なく、後のレイヤーで宣言された重要なスタイルより優先されるからです。

この例の CSS の最初の行を @layer secondLayer, firstLayer; と変更すると、レイヤーの宣言順序が変わり、firstLayer の重要なスタイルはすべて secondLayer でそれぞれの値に変更されることになります。

スコープ近接性

もう一つ、すぐに使用することはないかもしれませんが、将来的に理解する必要があるかもしれない高度なトピックに @scope があります。これはアットルールで、ページ上の HTML の固有のサブセクションにのみ適用されるルールのブロックを作成することができます。例えば、 <img> 要素が feature クラスのある要素の中に入れ子になっている場合にのみ適用されるスタイルを指定します。

css
@scope (.feature) {
  img {
    border: 2px solid black;
    display: block;
  }
}

スコープ近接性は、スコープされた要素間の競合を解決するメカニズムです。スコープ近接性は、2 つのスコープに競合するスタイル設定がある場合、 DOM ツリー階層からスコープルートへの移動数が最も少ないスタイルが優先されるという状態です。詳細と例は @scope の競合の解決方法を参照してください。

メモ: スコープの近接性はソースの順序よりも優先されますが、重要度レイヤー詳細度のような他にも優先度の高い仕様があれば、そちらが優先されます。

スキルテスト

この記事の最後まで達しましたが、最も大事な情報を覚えていますか?次に移動する前に、この情報を覚えているか検証するテストがあります。スキルテスト: カスケードを見てください。

まとめ

この記事の内容をほぼ理解できたなら、よくやったと言えます。CSS の基本的な仕組みに慣れることには、着手できたのではないでしょうか。次は、カスケードレイヤーについて深く考えてみましょう。

カスケード、詳細度、継承を完全に理解していなくても心配する必要はありません。このコースでこれまで取り上げてきたなかで、間違いなく最も複雑なことであり、プロのウェブ開発者でさえもたまに扱いにくく感じるものです。このコースを続行していくなかで、この記事にときどき戻り、それについて考え続けることをお勧めします。

スタイルが期待どおりに適用されないという奇妙な問題に出くわした場合は、この記事を参照してください。詳細度の問題である可能性があります。