CSSの新機能カスケードレイヤーが主要ブラウザにサポートされます、最初に理解しておきたい基礎知識を解説
Post on:2022年2月15日
CSSの新機能「カスケードレイヤー」がいよいよ主要ブラウザすべてにサポートされます。カスケードレイヤー@layer
を使用すると、CSSの詳細度とスタイルの順番をカスケード内で明示的にレイヤー化(階層化)でき、これまでのCSSの実装方法が大きく変わるものです。
Chromeのデベロッパーによるカスケードレイヤーの基礎知識を紹介します。
Cascade layers are coming to your browser
by Una Kravets
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様のライセンスに準じて翻訳しています。
はじめに
カスケードレイヤー(@layer
CSSルール)は、Chromium 99、Firefox 97、Safari 15.4 Betaに搭載されるCSSの新機能です。
参考: CSS Cascading and Inheritance Level 5
【訳者注】先週リリースされたFirefox 97ではデフォルトでサポートされ、ChromeとSafariは次のバージョンでサポートされる予定です。
参考: Firefox, Chrome, Safari
カスケードレイヤーを使用すると、CSSファイルをより明示的に制御でき、スタイル固有の競合を防ぐことができます。これは大規模なコードベース、デザインシステムやアプリでサードパーティのスタイルを管理する場合に特に役立ちます。
CSSを明確な方法で階層化することで、スタイルの予期せぬオーバーライドを防ぎ、より良いCSSのアーキテクチャを促進します。
CSSの詳細度とカスケード
CSSの詳細度とは、CSSがどのスタイルをどの要素に適用するかを決定する方法です。使用できるさまざまなセレクタによってスタイルルールの詳細度が決まり、詳細度が高いほど優先(オーバーライド)されます。
たとえば、要素はclass
セレクタよりも詳細度が低く、さらにid
セレクタよりも低くなります。詳細度は、CSSを学ぶ上で欠かせません。BEMのようなCSSの命名規則があるのは、意図せずに詳細度が上書きされるのを防ぐためです。
すべてに単一のclass
名を付けると、すべてが同じ詳細度の平面上に配置されます。しかし、特にサードパーティのコードやデザインシステムと連携する場合、このように整理されたスタイルを維持することは常に可能とは限りません。
BEMの命名規則の例(すべてが同じ詳細度): keepinguptodate.com
カスケードレイヤーはこの問題を解決することを目的としています。CSSのカスケードに新しいレイヤーを導入するものです。レイヤー化されたスタイルでは、レイヤーの優先順位は常にセレクタの詳細度よりも高くなります。
たとえば、.post a.link
は.card a
よりも高い詳細度を持っています。カード内、ポスト内でリンクのスタイルを設定しようとすると、より詳細度の高いセレクタが適用されることがわかります。
@layer
を使用すると、それぞれのスタイルの詳細度をより明確にすることができ、すべてのCSSが同一平面上にある場合、数値的には詳細度が低くても、カード内リンクのスタイルがポスト内リンクのスタイルをオーバーライドできます。これは、カスケードの優先順位によるものです。レイヤー化されたスタイルは、新しいカスケード「プレーン」を作成します。
数値的には詳細度が低くても、オーバーライドできる
@layerの動作
上記は、@layer
を使用したカスケードレイヤーの威力を示したものです。class
がないリンク、.link
があるリンク、.pink
があるリンクなど、リンクがいくつかあります。CSSには3つのレイヤー(base
, typography
, utilities
)があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@layer base { a { font-weight: 800; color: red; /* 無視されます */ } .link { color: blue; /* 無視されます */ } } @layer typography { a { color: green; /* すべてのaに適用されます */ } } @layer utilities { .pink { color: hotpink; /* すべての.pinkに適用されます */ } } |
すべてのリンクはグリーンかピンクになります。CSSの仕組みを解説すると、.link
はa
よりも高いセレクタレベルの詳細度が高いですが、base
レイヤーよりも優先順位の高いtypography
レイヤーのa
にカラーのスタイルがあります。グリーンのルールがブルーのルールの後のレイヤーにある場合、a { color: green }
は.link { color: blue }
をオーバーライドします。
レイヤーの優先順位は要素の詳細度に勝ります。
レイヤーの優先順位の管理
上記のようにページの記述順でレイヤーの優先順位を管理することもできますし、ファイルの先頭で優先順位を定義することもできます。レイヤーの優先順位は、各レイヤー名がコード内で最初に表示されることで確定されます。
つまり、ファイルの先頭に下記を記述すると、リンクはすべてレッドで表示され、.link
のリンクはブルーで表示されます。
1 |
@layer utilities, typography, base; |
この1行のCSSを追加するだけで、レイヤーの優先順位は逆(utilities
が最初で、base
が最後に)になります。base
レイヤーのスタイルは、typography
レイヤーのスタイルルールよりも常に高い詳細度も持つことになります。リンクのスタイルはグリーンではなく、レッドやブルーになります。
ITCSS(逆三角形CSS)は、Harry Robertsが考案したCSSの手法で、オーバーライドや複雑さを最小限に抑えた理想的なスタイル構成ができるようにするためのものです。レイヤー化されたスタイルを構造化するための理想的な方法で、最小から最大へと、レイヤー化されたスタイルを記述する際に従うべき素晴らしい規則です。
インポートファイルの整理
@layer
を使用する別の利点は、インポートファイルです。スタイルをインポートするときに、下記のようにlayer()
関数で直接できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* Base */ @import '../styles/base/normalize.css' layer(base); /* normalize or rest file */ @import '../styles/base/base.css' layer(base); /* body and base styles */ @import '../styles/base/theme.css' layer(theme); /* theme variables */ @import '../styles/base/typography.css' layer(theme); /* theme typography */ @import '../styles/base/utilities.css' layer(utilities); /* base utilities */ /* Layouts */ @import '../styles/components/post.css' layer(layouts); /* post layout */ /* Components */ @import '../styles/components/cards.css' layer(components); /* imports card */ @import '../styles/components/footer.css' layer(components); /* footer component */ |
上記のCSSには、3つのレイヤー(base
layouts
components
)があります。normalize、theme、typographyのファイルはbase
に、postファイルはlayouts
に、cardsとfooterはcomponents
に配置されています。これらのCSSファイルをインポートすると、layer()
関数でレイヤーがインスタンス化されます。別の方法としては、ファイルの先頭でインポートする前に定義することもできます。
1 2 3 4 5 |
@layer base, theme, layouts, components, utilities; |
これで、レイヤー名の最初のインスタンスですでに確立されているので、スタイルを@import
する順序はレイヤーの記述順とは関係ありません。これで心配事が一つ減りました。インポートしたファイルを特定のレイヤーに定義することはできますが、その順番はすでに確立されています。
このようにCSSファイル内のスタイルを読み込むとチェーン化してしまうので、HTML内のstyle
タグにすべてを含める方がパフォーマンス的に優れています。link
経由で含めるスタイルシートは最もパフォーマンスが高いのですが、現在のところレイヤーとしてロードする機能はありません。これは現在、WHATWGの未解決課題となっています。
レイヤーとカスケード
ここで、レイヤーがどのように使用されるのか、カスケードとの関連性を見てみましょう。
優先順位はこのようになっています。
- User Agent normal (優先順位が最も低い)
- Local User @layer
- Local User normal
- Author @layers
- Author normal
- Author !important
- Author @layer !important
- Local User !important
- User Agent !important(優先順位が最も高い)
@layer !important
のスタイルが反転していることにお気づきでしょうか。レイヤーでない(通常の)スタイルよりも詳細度が低いのではなく、より高い優先順位になります。これは、!important
がカスケードでどのように機能するかによるものです。スタイルシートの通常のカスケードを中断し、通常のレイヤーレベルの詳細度(優先度)を逆転させます。
入れ子のレイヤー
レイヤーは、他のレイヤーの中に入れ子にすることもできます。下記は、Miriam SuzanneのCascade Layers explainerから得たCSSです。
1 2 3 4 5 6 7 8 9 |
@layer default { p { max-width: 70ch; } } @layer framework { @layer default { p { margin-block: 0.75em; } } } |
このCSSでは、framework.default
にアクセスするために、.
を使用してframework
内に入れ子になっているdefault
レイヤーを示すことができます。省略して下記のように記述することもできます。
1 2 3 |
@layer.default { p { margin-block: 0.75em } } |
レイヤーとレイヤーの順番は次のとおりです。
default
framework.default
framework
非レイヤー化- 非レイヤー化
Shadow DOM内にカプセル化されたレイヤーは、Shadow DOMの境界を越えることはありません。したがって、Shadow DOM内の同じ名前のレイヤーはLight DOM内のレイヤーの順番に影響を与えず、その逆も同様です。
カスケードレイヤーの注意点
カスケードレイヤーは正しく使えば素晴らしいものですが、混乱や予期せぬ結果を生む可能性もあります。カスケードレイヤーを使用する際には、以下の点に注意してください。
Rule 1: スコープに@layer
を使用しない
カスケードレイヤーはスコープを解決するものではありません。たとえば、@layer
を含むCSSファイル(card.css
のように)があり、すべてのリンクにスタイルを定義したい場合、次のようなCSSは記述しないでください。
1 2 3 |
a { … } |
上記のように定義すると、ファイル内のすべてのa
タグにこのスタイルがオーバーライドで適用されます。スタイルのスコープを適切に設定することが重要であることに変わりはありません。
1 2 3 |
.card a { … } |
Rule 2: カスケードレイヤーは、レイヤー化されていないCSSの背後に配置される
ここで重要なのは、レイヤー化されたCSSファイルは、レイヤー化されていないCSSを上書きしないことです。これは、既存のコードベースと連携して、より理にかなった方法でレイヤーを導入しやすくするための意図的な決定です。たとえば、reset.css
を使用することはカスケードレイヤーの良い出発点で、使用例として適しています。
Rule 3: !important
はカスケードの詳細度を反転させる
レイヤー化されたスタイルはレイヤー化されていないスタイルよりも詳細度が低いですが、!important
を使用すると逆になります。レイヤーでは!important
ルールを持つ宣言は、レイヤー化されていないスタイルよりも詳細度が高くなります。
!important
スタイルは、詳細度を反転させます。前述の図は、参考のためにこれを示しています: author @layersはauthor normalよりも優先順位が低く、author !importantよりも優先順位が低く、author @layer !importantよりも低くなっています。
複数のレイヤーがある場合、!important
がある最初のレイヤーが!important
の優先順位を取得し、最も詳細度の高いスタイルとなります。
Rule 4: インジェクションポイントを把握する
レイヤーの順番は、各レイヤー名が最初にコードに現れることで確立されるので、layer()
のインポートと設定の後や別の@layer
文の後に@layer
宣言を記述しても無視されます。カスケードレイヤーの場合、ページ上で最後にあるスタイルルールが適用されるCSSとは異なり、最初のインスタンスで順番が確立されます。
これは、リスト、レイヤーブロック、インポートのいずれでも可能です。インポートリストの後にlayer()
で@layer
を記述しても何も起こりません。しかしファイルの先頭に置くと、レイヤーの順番が設定され、アーキテクチャ内のレイヤーを明確に確認できるようになります。
Rule 5#: 詳細度に注意する
カスケードレイヤーでは詳細度の低いセレクタが詳細度の高いレイヤーにある場合、詳細度の低いセレクタ(a
など)が詳細度の高いセレクタ(.link
など)より優先されます。
前述の例でみると、layer(components)
内のa
は、layer(utilities)
内の.pink
をオーバーライドします(@layer utilities
がcomponents
に指定されている場合)。これはAPIの意図的なものですが、予想外に混乱し、イライラする可能性があります。
そのため、ユーティリティのクラスを作成する場合は、常にオーバーライドするコンポーネントよりも上位のレイヤーとして記述してください。「色を変えるために.pink
を追加したのに、適用されない」ということがあるかもしれません。
カスケードレイヤーの参考リソース
カスケードレイヤーについては、下記のリソースも参照ください。
sponsors