コンテンツにスキップ

@layer 導入時の注意点

CSSカスケードレイヤー(@layer)の導入は、CSSプロパティの衝突を仕様レベルで防ぎやすくなる反面、使用にはいくつかの注意が必要になります。

ここでは、@layer 導入時の課題についてまとめます。

@layer の仕様そのものについては、 mdn @layer をご確認ください。

レイヤーの順序は「最初の宣言」で確定する

@layer の優先順位は、文書全体で各レイヤー名が最初に現れた順で確定します。
後から同じ名前を宣言しても、順序は変わりません(並べ替えは起きません)。

このため、複数のCSSファイルを読み込む構成では、レイヤー順序を宣言したファイルを必ず最初に読み込む必要があります。StrataCSSの構成では、 global.css がこの宣言を担います。

HTML|正しい読み込み順
<!-- 1・global.css が全レイヤーの順序を宣言する -->
<link rel="stylesheet" href="/assets/css/global.css">

<!-- 2・ページ個別CSSは、宣言済みの pages レイヤーに合流する -->
<link rel="stylesheet" href="/assets/css/pages/home.css">

もし読み込み順が逆になると、 home.css@layer pages {}最初の宣言となり、結果、レイヤー順は pages → base → layout → components → … となり、意図しない上書き順になるため注意が必要です。

  • レイヤー順序は 「最初に名前が現れた順」で確定し、後から変更できない
  • 順序を宣言する global.css は、常に他のCSSより先に読み込む
  • ページ個別CSSを非同期読み込みなどで前後させない

global.css 側で全レイヤーを事前宣言しておくのはこのためです。
Pagesスコープのように「別ファイルで後から書くレイヤー」も、先に宣言に含めておくことで文書全体での位置が固定されます。

レイヤー宣言だけを切り出したパーシャルファイルを用意し、各CSSの先頭に置くことで、万が一非同期読み込みなどで順序が前後しても宣言順を固定する、という対策もあります。

@layer の指定漏れによるスタイル崩れ

@layer を利用する場合は、意図する場合を除き、全てのスタイルをレイヤーに所属させることを推奨します。

@layer <layer-name> { … } の指定が漏れると、そのコードは @layer の管理外となり、カスケードの優先順位が @layer 指定のコードよりも高くなるためです。

意図して設計した場合はともかく、そうではない場合は、自作したスタイルには必ず @layer <layer-name> {} が記述されているか(有効になっているか)を意識します。

@layer 内で無効になるもの

以下の3つは、@layer ブロックの中に書くと無効になります。

@charset / @import / @namespace

特に、 @import は注意が必要です。
例えば、以下の記述は意図通りに動きません。

CSS
@layer base {
  @import "reset.css"; /* ← 無視される */
}

正しくは、トップレベルで layer() 付きインポートを使います。

CSS
@import "reset.css" layer(base);

これは、あくまでブラウザに配信されるネイティブなCSSの話です。
PostCSS の postcss-import や、バンドラーなどが @import をビルド時に解決し、取り込んだファイルの中身をインライン展開する場合は問題ありません。

@layer 内に問題なく書けるもの

前述の3つ以外は、 @layer の中に書いても通常どおり機能します(無効になりません)。

  • 条件付きグループ: @media / @supports / @container
  • カスケード・スコープ: @layer(入れ子)/ @scope
  • 印刷時の指定: @page
  • フォント: @font-face / @font-feature-values / @font-palette-values
  • 名前付き定義: @keyframes / @counter-style / @property / @function
  • その他: @starting-style / @position-try / @color-profile / @view-transition

頻出する @font-face@keyframes を含む “名前解決系” の アットルール全般は、どのレイヤーからでも名前で参照できます

実質グローバルになるアットルール

どのレイヤーに書いても、名前が文書全体の名前空間に登録され、どこからでも名前で参照できるアットルールは次のものです。

アットルール 定義する「名前」
@keyframes アニメーション名
@font-face フォントファミリ名
@font-feature-values 名前付き機能値
@font-palette-values パレット名
@counter-style カウンター名
@property カスタムプロパティ名
@function 関数名
@position-try 退避位置名
@color-profile プロファイル名

同名衝突時はレイヤー順で勝敗が決まり、下位レイヤーの定義が上書きされます。

上記の一覧は仕様として記述したものであり、現在ブラウザで利用できないものも含まれています。 実装の際には「Can I use」などで実装状況をご確認ください。

revert-layer による打ち消し

上位レイヤーから下位レイヤーの宣言を打ち消したい場合は、値のキーワード revert-layer が利用できます。
対抗する値を書かずに、「そのプロパティは下位レイヤーの宣言が無かったもの」として振る舞わせることができます。

CSS|revert-layer の使用例
@layer components {
    .btn { border-radius: 8px; }
}
@layer pages {
    .btn { border-radius: revert-layer; /* componentsの宣言を打ち消す */ }
}

仕様のポイントは次の通りです。

  • revert-layer は「下位レイヤーの値」に巻き戻すキーワード。下位レイヤーにも宣言が無い場合は revert と同様に振る舞う
  • revert はオリジン(作成者スタイル全体)単位、revert-layer はレイヤー単位の巻き戻し
  • 非レイヤーのCSSで使用した場合は、レイヤー付きのスタイルへ巻き戻る

Library層に取り込んだ外部製CSSの一部プロパティを打ち消す、といった使い方ができます。(外部製CSSの取り込み 参照)

revert-layer は、現在全ての主要ブラウザで利用できます(最後発のSafariが16.4/2023年3月〜)。

!important 利用の注意点

@layer の管理下で !important を使用した場合、 !important の記述がないスタイルよりも、 !important の記述があるスタイルが勝つのは今まで通りです。

しかし、レイヤーをまたいだ !important 同士の場合、「後に書いたスタイルが勝つ」状態にはならず、先に宣言した @layer に含まれる !important のスタイルが勝ちます。
つまり、従来の感覚と変わります。(MDN参考図解)

!important を利用される場合はご注意ください。

@layer でも変えられないこと

以下の内容は、@layer を利用しても変えることができません。

スタイルの出所と !important の優先順位

「誰が書いたか(ブラウザ / ユーザー / 作成者)」と、!important の有無による大枠の順位。
これはレイヤーより上位で決まるため、レイヤーの並べ方では変えられません。

同じレイヤーの中での勝敗

1つのレイヤー内は、従来どおり「詳細度 → 後に書いた方が勝つ」で決まります。
@layer でレイヤー内の詳細度競争まで消えるわけではありません。

次のステップ

ここでは、@layer 利用時の全体的な注意点についてまとめました。
次は、スライダーなどの外部で作られたCSSの取り込み方法について検討します。

外部製CSSの取り込み