カスケード レイヤがブラウザに登場

カスケード レイヤ(@layer CSS ルール)は、Chromium 99、Firefox 97、Safari 15.4(ベータ版)に導入される予定です。CSS ファイルをより明示的に制御して、スタイル固有の競合を防止できます。これは、大規模なコードベースやデザイン システム、アプリケーションでサードパーティのスタイルを管理する場合に特に便利です。

CSS を明確にレイヤ化することで、予期せぬスタイルのオーバーライドを防ぎ、CSS アーキテクチャを改善できます。

CSS の特異性とカスケード

CSS 限定性とは、CSS がどのスタイルをどの要素に適用するかを決定する仕組みです。使用できるセレクタによって、スタイルルールの特異性が決まります。たとえば、要素はクラスや属性ほど限定的ではなく、クラスや属性よりも限定的ではありません。これは CSS 学習の構成要素です。

多くの場合、BEM などの CSS の命名規則を使用することによって、具体性を意図せずオーバーライドしてしまうことを防ぎます。すべてに同じクラス名を付けると、すべてが同じ特異性プレーンに配置されます。しかし、特にサードパーティのコードやデザイン システムを使用する場合、そのような整理されたスタイルを維持することは必ずしも不可能です。

クラスが記載されたカードの BEM ビジュアル
keepinguptodate.com からの BEM 名の例を示す図。

カスケード レイヤはこの問題を解決することを目的としています。CSS カスケードに新しいレイヤが導入されています。レイヤ化スタイルでは、レイヤの優先順位は常にセレクタの特異性に勝ります。

たとえば、セレクタ .post a.link.card a よりも特異性が高くなります。リンクのスタイルを設定しようとしている場合、カード内の投稿内で、より具体的なセレクタが適用されます。

@layer を使用すると、それぞれのスタイル固有性をより明確に示すことができます。また、すべての CSS が同じ平面上にある場合に、限定性が数値的に低くなる場合でも、カードリンクのスタイルが投稿リンクのスタイルをオーバーライドするようにできます。これはカスケードの優先順位によるものです。レイヤスタイルは、新しいカスケード「平面」を作成します。

UI の分割に関するプロジェクト デモのイラスト

@layer の実例

インポート時のリンクの色を示すデモ
Codepen のデモをご覧ください。

この例では、@layer を使用してカスケード レイヤの機能を紹介します。複数のリンクが表示されています。追加クラス名が適用されていないリンク、.link クラスを含むリンク、.pink クラスを含むリンクがあります。CSS は、basetypographyutilities の 3 つのレイヤを次のように追加します。

@layer base {
  a {
    font-weight: 800;
    color: red; /* ignored */
  }

  .link {
    color: blue; /* ignored */
  }
}

@layer typography {
  a {
    color: green; /* styles *all* links */
  }
}

@layer utilities {
  .pink {
    color: hotpink;  /* styles *all* .pink's */
  }
}

最終的には、すべてのリンクが緑色またはピンク色になります。これは、.linka よりもセレクタ レベルの特異性が高い一方、@layer の優先度が高い a には色スタイルがあるためです。緑のルールが青のルールの後のレイヤにある場合、a { color: green }.link { color: blue } をオーバーライドします。

レイヤの優先順位は要素の特異性に勝る。

レイヤの編成

上図のようにレイヤをページ上で直接整理することも、ファイルの上部で整理することもできます。

レイヤの順序は、各レイヤ名がコードで初めて登場した時点で設定されます。

つまり、ファイルの先頭に次のコードを追加すると、リンクがすべて赤色になり、クラス .link のリンクが青色になります。

@layer utilities, typography, base;

これは、レイヤの順序が逆になり、ユーティリティが最初、ベースが最後になるためです。そのため、base レイヤのスタイルルールは常に、タイポグラフィ レイヤのスタイルルールよりも限定性が高くなります。今後は緑色のリンクではなく、赤または青のリンクになります。

Codepen プロジェクトのスクリーンショット
Codepen のデモをご覧ください。

インポートの編成

@layer のもう 1 つの使用方法は、インポート ファイルを使用することです。これは、スタイルをインポートするときに、次の例のように layer() 関数を使用して直接行うことができます。

/* 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 */

上のコード スニペットには、baselayoutscomponents の 3 つのレイヤがあります。base の正規化ファイル、テーマファイル、タイポグラフィ ファイル。layouts には post ファイル、components には cardsfooter を使用します。ファイルのインポート時に、layer 関数を使用してレイヤがインスタンス化されます。もう 1 つの方法は、ファイルの先頭でレイヤを整理し、インポートの前にレイヤを宣言することです。

@layer base,
       theme,
       layouts,
       components,
       utilities;

スタイルを @import する順序は、レイヤ名の最初のインスタンスですでに確立されているため、レイヤの順序は重要ではありません。心配する必要が 1 つ減ります。インポートしたファイルを特定のレイヤに設定できますが、順序はすでに設定されています。

Codepen プロジェクトのスクリーンショット
Codepen のプロジェクトを確認する。

レイヤとカスケード

一歩戻って、より広いカスケードに関連してレイヤが使用される場所を見てみましょう。

カスケードのイラスト

優先順位は次のとおりです。

  • ユーザー エージェントは通常(最も優先順位が低い)
  • ローカル ユーザー @layer
  • ローカル ユーザー(通常)
  • @layers を作成
  • 作成者(通常)
  • 作成者: 重要
  • 作成者 @layer !重要
  • ローカル ユーザー !重要
  • ユーザー エージェント !important**(最も優先順位が高い)

ここで、@layer !important のスタイルが反転していることがわかります。レイヤ化されていない(通常の)スタイルよりも限定的ではなく、優先順位が高くなります。これは、カスケードで !important が機能する仕組みによるものです。スタイルシートの通常のカスケードは解除され、通常のレイヤレベルの特異性(優先順位)が逆になります。

ネストされたレイヤ

レイヤを他のレイヤ内にネストすることもできます。次の例は、Miram Suzanne の Cascade Layers の説明によるものです。

@layer default {
  p { max-width: 70ch; }
}

@layer framework {
  @layer default {
    p { margin-block: 0.75em; }
  }

  p { margin-bottom: 1em; }
}

上記のコード スニペットでは、framework 内にネストされている default レイヤの記号として . を使用して、framework.default にアクセスできます。これをより簡潔な形式で記述することもできます。

@layer framework.default {
  p { margin-block: 0.75em }
}

生成されるレイヤとレイヤの順序は次のとおりです。

  • デフォルト
  • framework.default
  • framework レイヤなし
  • レイヤーなし

注意点

カスケード レイヤは正しく使用すれば素晴らしい効果を発揮しますが、混乱や想定外の結果を招く可能性もあります。カスケード レイヤを操作するときは、次の点に注意してください。

ルール 1: スコープ設定に @layer を使用しない

カスケード レイヤではスコープ設定を解決できません。@layer を含む CSS ファイルがあり、card.css と話しかけ、カード内のすべてのリンクにスタイルを設定する場合は、次のようにスタイルを記述しないでください。

a {
  …
}

そうすると、ファイル内のすべての a タグでこのオーバーライドが行われます。その場合も、スタイルを適切にスコープ設定することが重要です。

.card a {
  …
}

ルール 2: カスケード レイヤは非レイヤ化 CSS の背後に並べられる

レイヤ化 CSS ファイルは、レイヤ化されていない CSS より優先されないことに注意してください。これは、既存のコードベースで作業するために、より理にかなった方法でレイヤを簡単に導入できるようにするための意図的な決定です。カスケード レイヤの出発点およびユースケースとしては、たとえば reset.css ファイルを使用するのがよいでしょう。

ルール 3: !important はカスケード特異性を反転する

一般的に、レイヤ化スタイルはレイヤ化されていないスタイルほど限定的ではありませんが、!important を使用すると逆になります。レイヤ内では、!important ルールによる宣言は、レイヤ化されていないスタイルよりも限定的です。

その場合、!important スタイルはその特異性を反転します。上の図に参考として示します。作成者の @layers は作成者の通常よりも優先度が低く、作成者の !important よりも優先度が低く、作成者の @layer !important よりも優先度が低くなります。

レイヤが複数ある場合は、!important の最初のレイヤが !important の優先順位となり、最も狭い範囲のスタイルになります。

ルール 4: インジェクション ポイントを理解する

レイヤの順序は、各レイヤ名が初めてコードに登場した時点で設定されるため、layer() をインポートして設定した後や、別の @layer ステートメントの後に @layer 宣言を配置しても、無視してかまいません。ページの一番下にあるスタイルルールがカスケード レイヤに適用される CSS とは異なり、順序は最初に確立されます。

これは、リスト、レイヤ ブロック、インポートのいずれかです。layer() でインポート リストの後に @layer を追加しても、何も起こりません。ファイルの先頭に配置すると、レイヤの順序が設定され、アーキテクチャ内のレイヤを明確に確認できるようになります。

ルール 5: 独自性に注意する

カスケード レイヤでは、具体的でないセレクタ(a など)が、より具体的なレイヤにある具体的でないセレクタ(.link など)をオーバーライドします。次の点にご注意ください。

@layer utilities, components が指定されている場合、layer(components)alayer(utilities).pink をオーバーライドします。これは API の意図的な部分ですが、想定していないと混乱や不満が生じる可能性があります。

そのため、ユーティリティ クラスを記述する場合は、オーバーライドするコンポーネントよりも高階レイヤとして必ず含めてください。「色を変更するためにこの .pink クラスを追加したので、適用されない」と思われるかもしれません。

カスケード レイヤの詳細

カスケード レイヤについて詳しくは、以下のリソースもご覧ください。