CSS Cascade Layers (@layer) 徹底解説:詳細度のリセット、レイヤー順、!important の挙動

約7分

CSS の @layer は、詳細度(specificity)の競争に振り回されないカスケードの再構成手段です。「ライブラリのスタイルが強すぎて上書きできない」「!important を 5 つ重ねて勝つ」というアンチパターンを構造的に解消します。本記事ではレイヤーの順序ルール、!important の意外な挙動、unlayered スタイルとの関係を整理します。

なぜレイヤーが必要だったか

カスケードでスタイルが勝つかどうかは、伝統的に以下の優先順で決まります:

  1. オリジン (ユーザー > 著者 > ブラウザのデフォルト)
  2. !important(通常宣言の上)
  3. 詳細度(specificity = id, class, type の重み)
  4. 記述順(後勝ち)

詳細度の比較は 同じ origin / 同じ important 性 の中で行われますが、これが「ライブラリの .btn-primary を上書きするために body .container .btn-primary と無理やり書く」のような詳細度インフレを引き起こします。

@layer はこの問題をカスケード優先順に新しい次元を追加することで解決します。

基本構文

名前付きレイヤー

@layer reset, base, components, utilities;

@layer reset {
	* {
		margin: 0;
	}
}

@layer base {
	body {
		font-family: system-ui;
	}
}

@layer components {
	.btn {
		padding: 0.5rem 1rem;
	}
}

@layer utilities {
	.mt-4 {
		margin-top: 1rem;
	}
}

@layer reset, base, components, utilities; の宣言で順序を確定。後ろのレイヤーが強い:utilities > components > base > reset。

後ろのレイヤーが勝つ

@layer base {
	.text {
		color: black;
	}
}

@layer utilities {
	.text {
		color: red;
	}
}

<p class="text"> の色は utilities レイヤーは base より後に宣言されたから。utilities 内のセレクタの詳細度は無関係。

@layer base {
	#main .text {
		color: black;
	} /* 詳細度 0,1,1,1 */
}

@layer utilities {
	.text {
		color: red;
	} /* 詳細度 0,0,1,0 */
}

これでもutilities が勝って red詳細度競争はレイヤー内でしか起きない

unlayered スタイルとの関係

@layer の中に書かないスタイル(unlayered)は、すべてのレイヤーより強い

@layer utilities {
	.text {
		color: red;
	}
}

.text {
	color: green; /* unlayered */
}

結果は green。「レイヤーは下に置きたいので、layered より unlayered が強い**」という直感的でない仕様。

優先順は:

[最強] unlayered > 後のレイヤー > 前のレイヤー [最弱]

これにより、レイヤーにライブラリのスタイルを入れて、自分のアプリは unlayered で書く運用が成立します:

/* tailwind や reset.css をレイヤー化 */
@import 'tailwind.css' layer(framework);
@import 'reset.css' layer(reset);

/* 自分のスタイルは unlayered で書く(自動的にライブラリより強い) */
.my-button {
	background: tomato;
}

@import url(...) layer(name) 構文で、外部 CSS をレイヤーに入れられる。

!important でレイヤー順が逆転する

これが最も意外な挙動です。!important 付きの宣言は、レイヤーの順序が反転します。

@layer base {
	.text {
		color: black !important;
	}
}

@layer utilities {
	.text {
		color: red !important;
	}
}

通常なら utilities が勝つはずが、!important の場合は base が勝って black

直感的な理由

通常の宣言で「後ろのレイヤーが強い」のは、より具体的・特殊化されたスタイルを後に書く慣習(reset → base → components → utilities)と整合させるため。

一方 !important は「絶対にこの値にしたい」を示します。ライブラリ側(前のレイヤー)が !important で要求した値は、後ろのレイヤーで上書きされたら困る。なので !important の優先順は逆転して、前のレイヤーが勝つ仕様になっています。

unlayered と !important

[最強] 前レイヤーの !important > 後レイヤーの !important > unlayered の !important
       > unlayered の通常 > 後レイヤーの通常 > 前レイヤーの通常 [最弱]

unlayered の !important最強ではない!important の世界では unlayered < layered の関係。

レイヤーのネスト

@layer components {
	@layer cards {
		.card {
			padding: 1rem;
		}
	}
	@layer buttons {
		.btn {
			padding: 0.5rem;
		}
	}
}

components.cardscomponents.buttons という階層が作れる。順序は components.cards < components.buttons(後勝ち)。

ネストはレイヤー名のドット記法でも参照できる:

@layer components.cards {
	.card-header {
		font-weight: bold;
	}
}

匿名レイヤー

@layer {
	.foo {
		color: red;
	}
}

名前なしのレイヤー。他から参照できないので、後から追加するスタイルとマージできない。基本的に名前付きを使う。

ブラウザサポートと運用上の注意

  • Chrome 99+(2022)、Firefox 97+、Safari 15.4+。現代では全ブラウザ対応
  • IE 11 はサポートなし(プロジェクトで切り捨て可能か確認)
  • レイヤー宣言だけは早期に:HTML の <head> で順序を確定させ、後から @layer name { ... } で内容を書く運用が安全:
<style>
	@layer reset, base, components, utilities;
</style>
<link rel="stylesheet" href="reset.css" />
<!-- @import で layer 化 -->
<link rel="stylesheet" href="tailwind.css" />

宣言順より「最初の @layer name 出現順」で順序が決まるので、バンドラの結合順に依存しないように上記の宣言を最初に書く。

デザインシステムでの推奨運用

/* リセット → 基底 → デザインシステム → アプリ → ユーティリティ */
@layer reset, base, design-system, app, utilities;

@layer reset {
	/* normalize.css 相当 */
}

@layer base {
	body {
		font-family: var(--font-base);
	}
}

@layer design-system {
	/* shadcn / Skeleton UI 等のコンポーネント */
}

@layer app {
	/* 自分のアプリ固有スタイル */
}

@layer utilities {
	/* tailwind utilities など */
}

レイヤー順を書き換える必要がほぼない安定構造。新しい外部ライブラリを追加するときは適切なレイヤーに @import url(lib.css) layer(design-system) で入れる。

アンチパターン

!important@layer の中で使う

@layer utilities {
	.mt-4 {
		margin-top: 1rem !important;
	}
}

「ユーティリティは強くしたい」気持ちは分かりますが、レイヤーで順序を制御している以上 !important は不要!important を使うとレイヤー順が逆転して挙動が読めなくなる。@layer を使うなら !important は禁じ手にする。

unlayered スタイルとレイヤーの混在

@layer を使い始めたら、自分の CSS はすべてレイヤーに入れるか、すべて unlayeredかに統一。混ぜると「unlayered だけ強くて意図せず勝つ」事故が起きる。

まとめ

@layer は CSS の詳細度問題を構造的に解消する機能で、ライブラリと自分のアプリのスタイル衝突を最小限の宣言で整理できます。注意点は !important でレイヤー順が逆転することと、unlayered が layered より強いこと。この 2 つを把握すれば、デザインシステム + ユーティリティクラス + 個別カスタマイズの 3 層が綺麗に共存します。

CSS の整形・構造確認は CSS フォーマッターツール で行えます。