既存のCSSにカスケードレイヤーを導入するには、まず既存のルールをどこに配置するか決めます。通常のレイヤー外ルールは、名前付きレイヤーのすべてより優先されます。そのため、新しいレイヤー内のオーバーライドが機能するのは、競合する既存ルールを優先度の低いレイヤーに移した後です。
このガイドは、Macで作成済みのWebページを編集し、デスクトップブラウザーで確認する人を対象としています。具体的な作業は、既存のスタイルシートを新しいコンポーネントスタイルより下位に配置しつつ、分かりやすい移行手順を保つことです。例ではローカルファイル、架空のラベル、シンプルなCSS構成を使用します。
新しいレイヤー内スタイルが既存のルールに負けるのはなぜですか?
作成者スタイルシート内で競合する通常の宣言では、レイヤー外のCSSが、明示的なレイヤー内のCSSより優先されます。セレクターの詳細度を上げたり、レイヤー内で後ろに記述したりしても、この優先順位の境界は越えられません。CSS仕様では、レイヤー外のルールが、より詳細度の高いレイヤー内のルールを上書きする動作が示されています。CSS Cascading and Inheritance Level 5。
クラス tile を持つ要素を対象にした、次のCSS断片を考えてみましょう。
.tile {
background-color: #b8bdc5;
}
@layer components {
.tile {
background-color: #246a73;
}
}
背景は灰色のままです。レイヤーに overrides という名前を付けても、ブロックをファイルの後ろに移動しても、セレクターの詳細度を上げても、通常のレイヤー外ルールの優先順位は変わりません。レイヤー名に組み込みの意味はなく、宣言された順序によってレイヤー間の優先関係が決まります。MDN: Cascade layers。
次の判断表は、同じ作成者オリジンとコンテキストにあるスタイルシートの宣言同士が競合し、インラインスタイル、アニメーション、トランジションがプロパティに影響していない場合を想定しています。各行は優先度の低いものから高いものへ読んでください。
| 宣言の種類 | 優先度が低い → 高い |
|---|---|
| 通常 | 最初に宣言されたレイヤー → 後から宣言されたレイヤー → レイヤー外ルール |
!important |
レイヤー外ルール → 後から宣言されたレイヤー → 最初に宣言されたレイヤー |
レイヤーの優先順位は、セレクターの詳細度より先に解決されます。優先レイヤー内では詳細度が引き続き重要で、同点の宣言はソース順で決まることがあります。重要宣言では優先順位が逆転するため、移行時には通常宣言と重要宣言を分けて検討する必要があります。MDN: Cascade layers。
どの移行範囲を選べばよいですか?
まず、新しいCSSで制御したい要素とプロパティを1つずつ選びます。現在どの宣言が適用されているかを確認し、その宣言があるスタイルシートを特定して、そのファイルを変更できるか、またはページでの読み込み方法を変更できるか判断します。これにより、役立つ最小限の移行範囲が定まります。
| 状況 | 実際の次の手順 | 影響 |
|---|---|---|
| 既存のCSSの優先順位を維持する必要がある | 既存のCSSはレイヤー外に残し、優先度の低いレイヤー内に既定値を追加する | より優先度の高いルールと競合しない箇所では、新しいレイヤーが値を提供する |
| 既存のCSSを編集できる | 適用対象となる既存ルールを @layer legacy の中に入れる |
後続のレイヤーが、通常の既存宣言を上書きできる |
| スタイルシートのエントリーポイントを変更できる | 既存ファイルを layer(legacy) でインポートする |
セレクターを書き換えずに、インポートしたスタイルシートをそのレイヤーに含められる |
| 競合する既存CSSや読み込み方法を変更できない | 必要なオーバーライドは一時的にレイヤー外に置く | 詳細度やその他のカスケード要素を引き続き解決する必要がある |
これらの選択肢は、CSSで定義されている3つのレイヤー機能に沿っています。ステートメントでレイヤー名を宣言し、ブロックでルールをレイヤーに割り当て、レイヤー付きインポートでインポートしたスタイルシートをレイヤーに割り当てます。ステートメントだけでは、既存のルールは移動しません。CSS Cascading and Inheritance Level 5: Declaring Cascade Layers。
小さなスタイルシートなら、通常のルールをブロックで囲むのが簡単な場合があります。既存の外部ファイルなら、レイヤー付きインポートで明確な境界を作れます。既存の @import ルールを @layer ブロックの中に入れないでください。インポートは配置に制限があるトップレベルルールです。MDN: @import。
以下の推奨移行例では、既存ファイルを名前付きレイヤーにインポートします。これにより他のスタイルシートとの優先順位が変わるため、この変更をサンプルページ以外にも適用する前に、読み込まれている他のCSSを確認してください。
Macで完全なローカル例を作るにはどうすればよいですか?
次の3つのプレーンテキストファイルを含むフォルダーを作成します。コードエディターを使い、.html と .css の拡張子を維持してください。これらのファイルにパッケージ、スクリプト、フォント、ダウンロードしたアセットは必要ありません。ファイル名はローカルのサンプルリソースです。
次の完全なドキュメントを sample.html として保存します。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Layer sample</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main id="sample">
<h1>Shape sample</h1>
<p>Tile A</p>
<div class="tile" aria-label="Sample shape"></div>
</main>
</body>
</html>
このドキュメントがリンクするのは styles.css だけです。HTMLでは rel="stylesheet" と href を使って、外部スタイルシートをページに関連付けます。現行のHTML Standardでは、link 要素に layer 属性を定義していません。リソースのレイヤーへの割り当てはCSSで行います。HTML Standard: The link element。
次の完全なスタイルシートを legacy.css として保存します。セレクターを変更せずに残す既存ルールを表します。
body {
margin: 2rem;
font-family: system-ui, sans-serif;
color: #26313a;
background-color: #f4f5f7;
}
#sample .tile {
width: 6rem;
height: 6rem;
background-color: #b8bdc5;
border-radius: 0;
}
次の完全なエントリースタイルシートを styles.css として保存します。
@layer legacy, components;
@import url("legacy.css") layer(legacy);
@layer components {
.tile {
background-color: #246a73;
border-radius: 50%;
}
}
現在のデスクトップ版SafariまたはFirefoxで sample.html を開きます。期待される結果は、ラベル Tile A の下に青緑色の円が表示されることです。既存のレイヤーが寸法を指定し、後続のcomponentsレイヤーが背景色と丸みのある形を指定します。宣言はレイヤーをまたいで競合するため、単一クラスのセレクターが既存のIDセレクターより優先されます。MDN: @layer。
エントリーファイルでは、最初にレイヤー順序を定め、インポートをブロックより前に置き、その後にコンポーネントルールを追加します。これは、文書化されているインポート構文と配置規則に沿っています。レイヤーステートメントはインポートより前に置けますが、レイヤーブロックは置けません。MDN: @import。
既存ページでは何を変更すればよいですか?
この構成を採用するときは、ページが直接参照しているスタイルシートを新しいエントリースタイルシートに置き換えます。ページが legacy.css を直接読み込み、さらにレイヤー内にもインポートしている場合、直接読み込まれたCSSがレイヤー外の宣言を提供し、移行を無効にすることがあります。
失敗を予測するために、別の直接スタイルシートリンクを legacy.css に追加してみてください。その通常宣言によって、灰色の四角形に戻ることがあります。直接読み込みを削除すれば、既存ルールは意図したレイヤー内に残ります。この予測は、通常の優先順位ではレイヤー外ルールが明示的なレイヤーの後に位置付けられるという仕様に基づいています。CSS Cascading and Inheritance Module Level 5: Layer Ordering。
この例では3つのファイルを同じ場所に置いてください。この方法を応用する際は、メディア条件など既存リソースの条件を維持し、カスケードの競合を調べる前に、参照されるすべてのスタイルシートが読み込まれていることを確認します。
macOS版Firefoxで移行を確認するにはどうすればよいですか?
Command–Option–I でFirefoxの開発者ツールボックスを開き、Inspector を選択します。ドキュメントツリーでサンプルの図形を選択し、Rules ビューを確認します。Mozillaのドキュメントには、Macのツールボックス用ショートカットと、InspectorのCSS確認機能が記載されています。All keyboard shortcuts、Examine and edit CSS。
サンプルの各プロパティを1つずつ確認します。
background-colorを探します。コンポーネントの宣言が、既存の灰色の値より優先されているはずです。border-radiusを探します。コンポーネントの値によって円形になるはずです。widthとheightを確認します。これらの値は引き続き既存ファイルから取得されるはずです。- コンポーネントの背景宣言を一時的に無効にします。図形が丸みを保ったまま、既存の灰色が表示されるはずです。
Firefoxは上書きされた宣言に取り消し線を表示し、宣言を一時的に無効にするチェックボックスを備えています。また、ルールのソースファイルへのリンクも表示します。予期しない結果をすべてセレクターの問題と決めつけず、これらの操作で競合する宣言を特定してください。Mozilla: Examine and edit CSS。
実際のページでは、変更した状態ごとにこの確認を繰り返します。ウィンドウ幅を狭めた場合、関連するメディア条件、コンポーネントのバリエーションなどを確認してください。恒久的な編集はソースファイルに保存してから再読み込みします。一時的な検査時の変更は原因の特定に役立ちます。
移行中に予期せぬ変化が起きることはありますか?
重要宣言では意図した順序が逆転する
@layer legacy, components の場合、legacy 内の重要宣言は、components 内の競合する重要宣言より優先されます。そのため、重要な既存ルールを早い段階のレイヤーに移すと、上書きが難しくなることがあります。同じ作成者オリジンとコンテキストでは、レイヤー内の重要なスタイルシート宣言は、競合するレイヤー外の重要なスタイルシート宣言よりもすべて優先されます。MDN: @layer。
ファイルを移動する前に、!important を検索し、関連する競合をそれぞれ確認してください。両方の宣言を管理できる場合は、レイヤー導入後も重要指定が必要か判断します。新しいコンポーネントルールすべてに重要指定を追加しないでください。使う優先順位の仕組みが変わります。
インラインスタイルにも個別の注意が必要です。通常のインラインスタイルは通常の作成者スタイルシートルールより優先され、重要なインラインスタイルは他の重要な作成者宣言より優先されます。スタイルシートをレイヤーに移しても、要素のstyle属性は移動しません。MDN: Introduction to the CSS cascade。
後からのステートメントでは既存レイヤーの順序を変更できない
レイヤーの順序は、最初に宣言された時点で決まります。名前付きレイヤーを再び開いてルールを追加しても、そのレイヤーの位置は変わりません。インポートしたスタイルシートによるレイヤー作成も含め、ページでレイヤーが初めて作成される前に、意図した順序を指定してください。CSS Cascading and Inheritance Module Level 5。
段階的に移行する場合は、最初の順序を簡潔かつ明示的に保ちます。まとまりのあるルール群を移動して適用結果を確認し、それから次に進みます。競合するコピーをレイヤー外に残したまま1つのルールだけを移動しても、取り除こうとしていた優先順位の境界がそのまま残ることがよくあります。
Macで気を付けるべきブラウザー対応状況は何ですか?
MDNによると、@layer は2022年3月以降、各ブラウザーで広く利用できます。WebKitはSafari 15.4でカスケードレイヤーに対応したと説明しています。これらの日付は機能が利用可能になった時期を示すもので、すべてのMacに対応ブラウザーがインストールされていることを意味しません。ページが実際に対応する必要のあるバージョンを確認してください。MDN: @layer、WebKit: New WebKit Features in Safari 15.4。
レイヤーブロックを理解しないブラウザーは、その中にあるスタイルを通常のレイヤー外ルールとして適用しません。未対応の構文はCSSの解析規則に従って破棄されます。そのため、必須のCSSすべてをレイヤーで囲んでも、古いブラウザー向けの代替にはなりません。MDN: CSS error handling。
そうしたブラウザーへの対応が引き続き必要なら、対応対象の環境向けに、意図的にレイヤー外としたスタイルシートを残し、セレクターとソース順で競合を解決します。対応ブラウザーでレイヤー版とレイヤー外の重複版を同時に読み込むのは避けてください。設計したカスケードが変わります。
まずは、新しいコンポーネントが意図したプロパティを制御できる最小限の移行から始めます。レイヤー順序を定め、競合する既存ルールをレイヤーに割り当て、重複するレイヤー外読み込みを削除し、適用結果を確認します。この順序なら、さらに多くのCSSを移行する前に、優先順位の変更を1つずつ確認できます。
出典
- CSS Cascading and Inheritance Level 5 — W3C。
- CSS Cascading and Inheritance Module Level 5 — CSS Working Group。
- HTML Standard — WHATWG。
@layerCSS at-rule — MDN Web Docs。@importCSS at-rule — MDN Web Docs。- Cascade layers — MDN Web Docs。
- Introduction to the CSS cascade — MDN Web Docs。
- CSS error handling — MDN Web Docs。
- All keyboard shortcuts — Mozilla。
- Examine and edit CSS — Mozilla。
- New WebKit Features in Safari 15.4 — WebKit。