メインコンテンツまでスキップ
  • Created:
  • Updated:
  • Author:
    Takeshi Takatsudo

カスケードレイヤー

問題

CSSの詳細度(specificity)の競合は、バグやフラストレーションの最も一般的な原因の一つです。開発者は、詳細度の競合を管理するために、過剰に具体的なセレクター、!important、またはBEMのような命名規則に頼ります。サードパーティCSS(デザインシステム、コンポーネントライブラリ、リセット)を統合する際、どのスタイルが優先されるかの制御はますます困難になります。AIエージェントは、詳細度の競合を引き起こすCSSを生成したり、修正として!importantを使用したりすることがよくあります。

解決方法

@layerアットルールは、カスケードに対する明示的な制御を提供します。レイヤーの優先度はセレクターの詳細度より前に評価されます — 後で宣言されたレイヤーのシンプルなセレクターは、先に宣言されたレイヤーの複雑なセレクターに常に勝ちます。これにより、設計上の詳細度の競合が解消されます。

完全なカスケード評価順序は、origin/importance > インラインスタイル > cascade layers > 詳細度 > ソース順序です。

コード例

レイヤー順序の宣言

レイヤーが最初に宣言される順序が優先度を決定します。宣言リストの最後のレイヤーが最も高い優先度を持ちます。

/* Declare layer order upfront — this is the recommended pattern */
@layer reset, base, components, utilities;

/* Now populate layers in any order */
@layer reset {
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}

@layer base {
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
}

a {
color: #2563eb;
}
}

@layer components {
.button {
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
}

.button-primary {
background: #2563eb;
color: white;
}
}

@layer utilities {
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
}

.text-center {
text-align: center;
}
}

サードパーティCSSのレイヤーへのインポート

/* Put third-party styles in a low-priority layer */
@import url("normalize.css") layer(reset);
@import url("some-library.css") layer(vendor);

@layer reset, vendor, base, components, utilities;

レイヤーの優先度が詳細度を上書きする

@layer base, components;

@layer base {
/* High specificity: (0, 2, 1) */
nav ul li.active a.nav-link {
color: black;
}
}

@layer components {
/* Low specificity: (0, 1, 0) — but this WINS because
'components' is declared after 'base' */
.nav-link {
color: blue;
}
}

ネストされたレイヤー

@layer components {
@layer card {
.card {
border: 1px solid #e5e7eb;
border-radius: 8px;
}
}

@layer button {
.button {
padding: 0.5rem 1rem;
}
}
}

/* Reference nested layers with dot notation */
@layer components.card {
.card {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
}

revert-layerの使用

revert-layerキーワードは、プロパティ値を前のレイヤーで設定された値にロールバックします。

@layer base, theme, overrides;

@layer base {
a {
color: blue;
text-decoration: underline;
}
}

@layer theme {
a {
color: #8b5cf6;
text-decoration: none;
}
}

@layer overrides {
/* Roll back to the base layer value */
.classic-link {
color: revert-layer;
text-decoration: revert-layer;
/* Result: color is #8b5cf6 from theme?
No — revert-layer goes to the PREVIOUS layer.
Actually: color reverts to theme's value first,
then theme could revert-layer to base's value. */
}
}

revert-layerの実用的なユースケース:

@layer defaults, theme;

@layer defaults {
button {
background: #e5e7eb;
color: #1f2937;
}
}

@layer theme {
button {
background: #2563eb;
color: white;
}

/* Opt specific buttons out of theming */
button.no-theme {
background: revert-layer; /* Falls back to #e5e7eb */
color: revert-layer; /* Falls back to #1f2937 */
}
}

レイヤーに属さないスタイルが最も高い優先度を持つ

どのレイヤーにも属さないスタイルは、常にレイヤー内のスタイルに勝ちます。

@layer base {
p {
color: gray;
}
}

/* Unlayered — this wins */
p {
color: black;
}

ブラウザサポート

  • Chrome 99+
  • Firefox 97+
  • Safari 15.4+
  • Edge 99+

グローバルサポートは96%を超えています。

AIがよくやるミス

  • レイヤーで優先度を管理する代わりに、スタイルのオーバーライドに!importantを使用する
  • レイヤー順序を事前に宣言せず、最初に出現した順序に基づく予測不可能な優先度になる
  • サードパーティ/ベンダーCSSをレイヤーの外に配置する(最も高いレイヤー外の優先度を与えてしまう)
  • レイヤーに属さないスタイルがすべてのレイヤー内スタイルに勝つことを知らない
  • revert-layerrevertを混同する — revertはユーザーエージェントスタイルシートにロールバックし、revert-layerは前のカスケードレイヤーにロールバックする
  • レイヤーの順序付けで優先度の問題を解決できるのに、過剰に具体的なセレクターを生成する

使い分け

  • 複数のスタイルソースを持つ大規模なコードベースでの詳細度の管理
  • 詳細度の競合なしにサードパーティCSSを統合する
  • 明確なCSSアーキテクチャの確立(reset、base、components、utilities、overrides)
  • !importantの使用を構造化されたレイヤー優先度に置き換える
  • コンシューマーがコンポーネントスタイルを予測可能にオーバーライドする必要があるデザインシステムの構築

ライブプレビュー

レイヤーの順序が勝者を決定する
レイヤーの優先度が詳細度に勝つ

参考リンク