t28.dev

CSS の C (Cascading) を見つめ直す

2025/11/7
Tech

カスケードレイヤー (@layer) は Widely available になってから 1年以上経っている。 また、いくつかの UIコン ポーネントCSS ラ イブラリでも使われるようになっている。 そろそろキャッチアップしようと MDN を読んでいたが、カスケード自体はCSSの基本設計原則の1つであることを思い出した。

カスケードは、異なるソースから来るプロパティ値を組み合わせる方法を定義するアルゴリズムです。(…) これは カスケーディングスタイルシート という名前で強調されているように、 CSS の中心を占めるものです。

ref: CSS カスケード入門

新しい機能 (カスケードレイヤー) によってウェブ開発者は CSS でカスケードが使えるようになった…ではなくカスケードをより制御できるようになったが正確な理解である。 「じゃあ CSS のカスケードってなに?」と言われると言葉が詰まるので、CSS の C (Cascading) を見つめ直してから、カスケードレイヤーのキャッチアップをすることにした。

2025/11/15 updated: “仕様と照らし合わせながら CSS カスケードレイヤーのふるまいをメモする”も書いた

CSS カスケードアルゴリズム

カスケードは

異なるソースから来るプロパティ値を組み合わせる方法を定義するアルゴリズム

ref: CSS カスケード入門

で、CSS カスケードアルゴリズムの役割は、

CSS プロパティの正しい値を決定するために CSS 宣言を選択すること

ref: CSS カスケード入門 - オリジンの種類

である。 例えば👇️のような競合するプロパティ値があるとき、前のルールを上書きすることが CSS のカスケードとして定義されている (playground)。

/* <h1>これは青い見出し</h1> */
h1 {
  color: red;
}
h1 {
  color: blue; /* 👈️ ソース上最後が勝つ */
}

カスケードによる並び替え

CSS のカスケードは以下の流れで最終的な値 (カスケード値)を選ぶ。

  1. ある要素に対して適用されるすべての宣言を集める
  2. 宣言を優先順位に従って並び替える
  3. 単一のカスケード値を出力する

The cascade takes an unordered list of declared values for a given property on a given element, sorts them by their declaration’s precedence as determined below, and outputs a single cascaded value.

ref: CSS Cascading and Inheritance Level 5 - 6. Cascading

カスケードの優先順位は以下のように決まっている (仕様とMDNの説明で差分がある?)。

順序 (低い順)仕様MDN の説明
1Filtering(ここだけ § 5 Filtering関連性
2Origin and Importanceオリジンと重要度
3Context(ない?)
4Element-Attached Styles(ない?)
5Layers(ない?)
6Specificity詳細度
7(ない?)スコープ近接性
8Order of Appearance出現順

さらに、それぞれの項目内にも優先順位がある。

Origin and Importance 内の優先順位は

  • スタイルルールがどの層(出所)から来たかを示すオリジン
  • 宣言に付けられた !important の有無

で決まる。 オリジンには3つのコアオリジン2つの追加オリジンがあり、 これらと !important の組み合わせで次のような優先順位になる:

順序(低い順)オリジン重要度
1ユーザーエージェント(ブラウザー)通常
2ユーザー通常
3作成者(開発者)通常
4CSS @keyframes アニメーション
5作成者(開発者)!important
6ユーザー!important
7ユーザーエージェント(ブラウザー)!important
8CSS トランジション

ref: MDN - CSS カスケード入門 - カスケード順

Web アプリの開発で普通に CSS を実装すると、そのスタイルは作成者オリジン1由来になる。 ユーザーのスタイルより作成者のスタイルが優先されるのは少し不思議な気もしたが、!important を使えば逆に上書きできるようにすることでバランスを保つことが仕様になっている。 「上書きしたいからとりま !important」なコードを見ることもあるが、単純な上書きではなく、作成者-ユーザー-ユーザーエージェント間の優先度が逆転していることを知らないとハマりそうな予感。

Layers の優先順位は

  • @layer を使った explicit layer
  • @layer を使わない implicit final layer

に分かれる。@layerで囲わない宣言は自動的に implicit final layer に入る。 implicit final layer は他の explicit layer より後に評価されるため、高い優先度で他の宣言を上書きする。

Declarations within each origin and context can be explicitly assigned to a cascade layer. For the purpose of this step, any declaration not assigned to an explicit layer is added to an implicit final layer.

ref: https://www.w3.org/TR/css-cascade-5/#cascade-layering

並べ替えのイメージ

CSS カスケードは前述した優先度で並べ替えをして、そこで差がつかないときは次の基準で並べ替えをする。

The cascade sorts declarations according to the following criteria, in descending order of priority:

ref: https://www.w3.org/TR/css-cascade-5/#cascade-sort

並べ替えを図示すると、👇️のような感じになる。

h1 / color
└─ [Origin & Importance (Normal)]
    ├─ User-Agent origin                    // ブラウザ既定
    │    └─ h1 { color: black; }
    ├─ User origin                          // ユーザーによるスタイルCSS
    │    └─ h1 { color: green; }
    └─ Author origin                        // 作成者のスタイル
         ├─ [Layers]                        // Author origin の中で Layer の並べ替えをする
         │    ├─ @layer base
         │    │    └─ h1 { color: gray; }
         │    └─ (implicit final layer)    // レイヤーの中では、レイヤー指定なしが一番強い
         │         └─ h1 { color: blue; }
         └─ [Element-Attached Styles]
              └─ <h1 style="color: orange"> 👈️ 勝ち!

Footnotes

  1. オリジンの名前は結構表記ゆれしている。 仕様では authore originauthor declarationsauthor style sheet の表記がある。 MDN も同じ感じでばらついている。