一覧WB-08-2 · CSSの適用規則

CSSの継承とカスケード

同じ段落の色を変更し、computed styleで実際に使われる値を取得します。継承と直接指定から始め、詳細度・記述順・インライン指定・レイヤーへ順に条件を増やします。styleOfは対象文書のgetComputedStyleからプロパティ値を取り出す関数です。

説明

一つの要素に複数のCSS宣言が関わると、優先順位に従って使う値が決まります。親からの継承、セレクタの詳細度、記述順、!important、カスケードレイヤーはそれぞれ異なる段階の仕組みです。常に「後に書いた方が勝つ」とは限りません。

同じ段落の色を変更し、computed styleで実際に使われる値を取得します。継承と直接指定から始め、詳細度・記述順・インライン指定・レイヤーへ順に条件を増やします。styleOfは対象文書のgetComputedStyleからプロパティ値を取り出す関数です。

1親から継承する色

段落自身には色を指定せず、親の色を赤にします。

(globalThis.doc=createFormDocument独立した表示文書を作成('<!doctype html><html lang="ja"><head><style>body{font:16px/1.5 system-ui;margin:16px;background:#111;color:#eee}</style></head><body><div id="parent" style="color:rgb(220,80,80)"><p id="sample" class="note">参加案内</p></div></body></html>'),globalThis.sheet=doc.createElement("style"),doc.head.append(sheet),styleOf("#sample","color")段落の計算済みの色)

段落の色はrgb(220, 80, 80)です。colorは継承されるプロパティなので、段落への指定がなければ親の値を使います。marginやborderなど、すべてのプロパティが同じように継承されるわけではありません。

2直接指定は継承値より先に使う

段落に青を直接指定します。

(sheet.textContent="p{color:rgb(80,150,230)}子自身への直接指定",styleOf使われる値を確認("#sample","color"))

段落は青になります。親のstyle属性が強い指定だから子も必ず赤になる、という計算ではありません。子自身へ使う宣言を決める処理と、子に値がない場合の継承は別です。

3同じ条件なら記述順

同じ.noteセレクタを二つ書きます。

(sheet.textContent=".note{color:rgb(80,150,230)}先に書いた宣言 .note{color:rgb(80,200,140)}後に書いた宣言",styleOf("#sample","color"))

同じ起点・重要度・レイヤー・詳細度などの条件では、後の緑が採用されます。記述順は常に最初に比較する条件ではありません。

4idとclassの詳細度

idの赤を先に書き、その後へclassの青を置きます。

(sheet.textContent="#sample{color:rgb(220,80,80)}idによる指定 .note{color:rgb(80,150,230)}後にあるclass指定",styleOf("#sample","color"))

赤が採用されます。idの詳細度(1,0,0)はclassの(0,1,0)より高いためです。この組は桁ごとに比較するもので、classを十個書けばid一個と同点になる十進数の点数ではありません。

5:whereは詳細度を増やさない

idを:whereの中へ入れます。

(sheet.textContent=":where(#sample)idを選ぶが詳細度は0{color:rgb(220,80,80)} .noteclassの詳細度{color:rgb(80,150,230)}",styleOf("#sample","color"))

今度は青になります。:where自身と引数の詳細度は0なので、.noteのclass指定が上回ります。同じ要素を選ぶセレクタでも、詳細度は異なり得ます。

6:isは引数の詳細度を使う

:whereを:isへ変えます。

(sheet.textContent=":is(#sample)引数のidの詳細度を使う{color:rgb(220,80,80)} .note{color:rgb(80,150,230)}",styleOf("#sample","color"))

赤へ戻ります。:isは引数のうち最も高い詳細度を使うため、この指定はidの詳細度を持ちます。:isと:whereは対象の選び方が似ていても、優先順位の設計では同じではありません。

7style属性で指定

段落自身のstyle属性に緑を指定します。

(doc.getElementById("sample").style.color="rgb(80,200,140)"段落自身のインライン指定,styleOf("#sample","color"))

通常の作者スタイルの範囲では、インラインのstyle指定がスタイルシートの通常宣言より優先されます。親のstyleからの継承ではなく、段落自身への指定である点も確認します。

8!importantの段階

スタイルシートの宣言に!importantを付けます。

(sheet.textContent="p{color:rgb(230,180,60) !important重要宣言として扱う}",styleOf("#sample","color"))

黄色が採用されます。この!important宣言は通常のインライン指定より優先されます。!importantはセレクタの詳細度を少し増やす指定ではなく、カスケードの重要度に関わります。

9インラインの重要宣言

段落のstyleにもimportant付きの色を設定します。

(doc.getElementById("sample").style.setProperty("color","rgb(200,100,200)","important")重要度付きのインライン指定,styleOf("#sample","color"))

この作者スタイル同士の比較では、インラインの重要宣言が採用されます。利用者・UAの重要宣言やトランジションなどを含むカスケード全体を、インラインが何より強いという一言では表せません。

10レイヤー順は詳細度より先

インライン指定を除き、base・themeの二レイヤーで競合させます。

(doc.getElementById("sample").removeAttribute("style")インライン指定を除去,sheet.textContent="@layer base,themeレイヤー順を定義; @layer base{#sample{color:rgb(220,80,80)}} @layer theme後のレイヤー{p{color:rgb(80,150,230)}}",styleOf("#sample","color"))

通常宣言では後のthemeレイヤーが優先され、pの青になります。base内のidの方が詳細度は高くても、異なるレイヤー間ではその比較より先にレイヤー順が決まります。

11レイヤー外の通常宣言

同じスタイルにレイヤー外の通常宣言を追加します。

(sheet.textContent+=レイヤー外へ通常宣言を追加" p{color:rgb(80,200,140)}",styleOf("#sample","color"))

レイヤーに属さない通常宣言の緑が、レイヤー内の通常宣言より優先されます。レイヤーを導入するときは、既存のレイヤー外の指定が残っていないかも確認します。

12重要宣言ではレイヤー順が逆転

すべてを重要宣言にして、base・theme・レイヤー外を比較します。

(sheet.textContent="@layer base,theme同じレイヤー順; @layer base{p{color:rgb(220,80,80) !important}}最初のレイヤーの重要宣言 @layer theme{#sample{color:rgb(80,150,230) !important}} p{color:rgb(80,200,140) !important}",styleOf("#sample","color"))

最初のbaseレイヤーの赤が採用されます。重要宣言ではレイヤーの優先順が逆転し、レイヤー内の重要宣言はレイヤー外の重要宣言より優先されます。通常宣言の順序をそのまま当てはめることはできません。

まとめ

子への直接指定と親からの継承は別で、使う宣言がない場合に継承値が関わります。

重要度やレイヤーなどを決めた後、詳細度・記述順を比較します。

通常宣言と重要宣言ではレイヤーの優先順が異なります。computed styleで最終値を確認します。

操作履歴と解説を振り返れます。

ブラウザ / HTML・DOM

1
(globalThis.doc=createFormDocument('<!doctype html><html lang="ja"><head><style>body{font:16px/1.5 system-ui;margin:16px;background:#111;color:#eee}</style></head><body><div id="parent" style="color:rgb(220,80,80)"><p id="sample" class="note">参加案内</p></div></body></html>'),globalThis.sheet=doc.createElement("style"),doc.head.append(sheet),styleOf("#sample","color"))
"rgb(220, 80, 80)"
2
(sheet.textContent="p{color:rgb(80,150,230)}",styleOf("#sample","color"))
"rgb(80, 150, 230)"
3
(sheet.textContent=".note{color:rgb(80,150,230)} .note{color:rgb(80,200,140)}",styleOf("#sample","color"))
"rgb(80, 200, 140)"
4
(sheet.textContent="#sample{color:rgb(220,80,80)} .note{color:rgb(80,150,230)}",styleOf("#sample","color"))
"rgb(220, 80, 80)"
5
(sheet.textContent=":where(#sample){color:rgb(220,80,80)} .note{color:rgb(80,150,230)}",styleOf("#sample","color"))
"rgb(80, 150, 230)"
6
(sheet.textContent=":is(#sample){color:rgb(220,80,80)} .note{color:rgb(80,150,230)}",styleOf("#sample","color"))
"rgb(220, 80, 80)"
7
(doc.getElementById("sample").style.color="rgb(80,200,140)",styleOf("#sample","color"))
"rgb(80, 200, 140)"
8
(sheet.textContent="p{color:rgb(230,180,60) !important}",styleOf("#sample","color"))
"rgb(230, 180, 60)"
9
(doc.getElementById("sample").style.setProperty("color","rgb(200,100,200)","important"),styleOf("#sample","color"))
"rgb(200, 100, 200)"
10
(doc.getElementById("sample").removeAttribute("style"),sheet.textContent="@layer base,theme; @layer base{#sample{color:rgb(220,80,80)}} @layer theme{p{color:rgb(80,150,230)}}",styleOf("#sample","color"))
"rgb(80, 150, 230)"
11
(sheet.textContent+=" p{color:rgb(80,200,140)}",styleOf("#sample","color"))
"rgb(80, 200, 140)"
12
(sheet.textContent="@layer base,theme; @layer base{p{color:rgb(220,80,80) !important}} @layer theme{#sample{color:rgb(80,150,230) !important}} p{color:rgb(80,200,140) !important}",styleOf("#sample","color"))
"rgb(220, 80, 80)"

演習完了