レイヤーを使い始めると判定が一段増える
一言でいうと
カスケードの並べ替えの順序では、レイヤーが詳細度よりも先に決まります。そのため、レイヤーを使い始めたシートでは、詳細度の低いルールが高いルールに勝つことが、正常に起こります。
なぜ必要なのか
CSSが崩れていく道筋は、ほとんどいつも1つです。他人のルールに勝つためにセレクターを1つ長く書き、次の人はもっと長く書き、結局!importantが出てきます。すると、そのプロパティは永遠に!importantでしか扱えなくなります。
問題の根は、「勝つべき順序」と「詳細度」が、もともと別のものだという点にあります。リセットのルールは常に最も弱く、ユーティリティクラスは常に最も強くなければなりません。ところがCSSには、それを直接言う方法がなく、これまでは、セレクターの長さという間接的な手段で真似してきました。
@layerは、その順序を名前で直接書く構文です。CSS Cascade 5が定義し、MDNの@layerのドキュメントが構文を整理しています。
どう動くのか: 並べ替えの順序
仕様の6.1節が定める基準は、この順序で見ます。前で決まれば、後ろは見ません。
1. 출처와 중요도 (사용자 important > 작성자 important > … )
2. 캡슐화 맥락 (섀도 트리)
3. 요소에 직접 붙은 스타일 (style 속성)
4. 레이어
5. 특정성
6. 나온 순서
!importantは1で決まるため、依然として最も強いです。その次がstyle属性で、その次がレイヤー、そのあとでようやく詳細度です。
2行で終わります
レイヤーの段階のルールは、2行です。
- レイヤーに入れていない宣言は、暗黙のうちに最後のレイヤーに入ります
- 通常の宣言は、あとのレイヤーが勝ち、importantの宣言は、前のレイヤーが勝ちます
この2つを合わせると、最初は逆に見える結果になります。
@layer reset, components;
@layer components {
#app .card .title { color: navy; } /* 특정성 1,2,0 */
}
.title { color: crimson; } /* 특정성 0,1,0, 레이어 밖 */
勝つのは、.titleのcrimsonです。レイヤーの外は最後のレイヤーであり、通常の宣言ではあとのレイヤーが勝つからです。詳細度は、見られる番が来ません。
逆に、!importantを付けると、順序がひっくり返ります。resetレイヤーの!importantがcomponentsレイヤーの!importantに勝ち、レイヤー外の!importantは、すべてのレイヤーの!importantに負けます。仕様は、これを「importantが通常の宣言の優先順位を逆転させるのと同じ論理」だと説明しています。
順序は最初に出てきた場所で決まります
レイヤーの順序は、名前が最初に出てきた順です。そのため、ファイルが複数に分かれ、バンドラーが順序を入れ替えると、レイヤーの順序も変わります。
そのため、実務では、いちばん上にブロックのない宣言型のルールを1行置きます。
@layer reset, base, components, utilities;
この1行が、ファイル全体の契約になります。それ以降は、どのファイルが先に読み込まれても、順序は固定です。
:where()と対になります
:where()は、詳細度が常に0です。:where(.card .title)は0,0,0なので、タグセレクター1つにも負けます。
レイヤーが「どの集団が勝つか」を決めるなら、:where()は、同じ集団の中で上書きしやすい基本値を作ります。ライブラリが基本スタイルを与えるときに、この2つを一緒に使えば、使う側はセレクターを長く書く必要がなくなります。
:is()と:not()は違います。かっこの中で最も高いものに従います。:is(.card, #app)は1,0,0です。
現場での姿
レイヤーを導入するときに実際にぶつかるものを、3つ書き留めておきます。
1つ目は、既存のコードを一度にレイヤーに入れられないことです。そのとき、残ったコードはすべて「レイヤーの外」になり、すべてのレイヤーよりも強くなります。これは事故ではなく、むしろ安全な移行の道筋です。新しく作ったレイヤーが、既存の画面を突然上書きすることがありません。
2つ目は、@importにレイヤーを付けられることです。@import url(reset.css) layer(reset);で他人のCSSをまるごと1つのレイヤーに押し込むと、そのライブラリ全体が自分のコードよりも弱くなります。ライブラリに勝つために使っていた!importantが、丸ごとなくなります。
3つ目は、メディアクエリの中でレイヤーを最初に宣言しないことです。仕様は、条件が真のときだけ、そのレイヤーが順序に入ると述べています。画面幅によってレイヤーの順序が変わるシートは、再現できないバグを生みます。いちばん上の宣言型のルール1行が、これも防いでくれます。
次のラボですること
4つのレイヤーを宣言してルールを埋めたあと、どの宣言が勝つかを計算ツールで自分で確認します。レイヤーの外がなぜ最も強いのか、!importantを付けるとなぜひっくり返るのかを、先に予測ファイルに書いてから、計算ツールで答え合わせをします。