Token
責任範囲
「その値は、どこで一元管理するか」に答える層。design token の集約定義を担う。
配置基準
Token Test(この値はデザイントークン〔色の値、フォント名、余白、z-index 等〕か? または計算ヘルパーか?)で判定する。色・spacing・typography・motion の単一の定義源として扱う。
例
--color-main, --space-md。
構造
各層は @layer の宣言順に並ぶ。宣言順がそのまま優先順位になり、後に宣言した層ほど強い。
層が違えば、答える問いも違う。ある値をどこで一元管理するかを決めるのは Token 層の責任であり、その値を使ってページの骨格を組むのは Layout 層の責任だ。責任が重ならないから、どこに書くべきかを機械的に判定できる。
「その値は、どこで一元管理するか」に答える層。design token の集約定義を担う。
Token Test(この値はデザイントークン〔色の値、フォント名、余白、z-index 等〕か? または計算ヘルパーか?)で判定する。色・spacing・typography・motion の単一の定義源として扱う。
--color-main, --space-md。
「ブラウザ間の差を、どこまで無くすか」に答える層。リセットまたはノーマライズで、ブラウザ間の差異を吸収する。
Reset Test(このスタイルはブラウザデフォルトの初期化か?)で判定する。
margin: unset 等の、ブラウザ既定値の解除。
「要素の初期スタイルは、何を基準にするか」に答える層。body のフォントや見出し・リンク色など、要素そのものへの基本スタイルを担う。
Foundation Test(このスタイルは、全ページ共通の基本スタイルか?)で判定する。
:where(body) のフォントと行間、見出し・リンクの基本スタイル。
「ページの骨格は、どう構成するか」に答える層。header・footer・main 等の構造を組む。
Layout Test(このスタイルは、配置・寸法・空間の確保か? 中身の見た目は変わらないか?)で判定する。
.l-header, .l-footer, .l-section。
「この部品は、別のサイトへそのまま持ち出せるか」に答える層。再利用可能な小単位の UI を担う。
Portability Test(そのパーツを別のサイトにそのまま持っていけるか?)で判定する。
.c-button, .c-card, .c-badge。
「このパーツは、置く場所で決まるか」に答える層。プロジェクト固有の組み合わせを担う。
Responsibility Test(このスタイルは、パーツ自身の視覚的責任か?)で、Portability Test だけでは判断できない場合を補う。
.p-home-hero, .p-home-features。
「動きは、どこに集約するか」に答える層。装飾的なアニメーションを分離して集約する。
Animation Test(その動きを無効化しても、インタラクションの意味が伝わるか?)で判定する。装飾的な動き(fade-in 等)はこの層に分離し、hover の色変化のような機能的トランジションは、その Block が属する層に残す。prefers-reduced-motion への対応はここで一元管理する。
@keyframes fade-in, prefers-reduced-motion 対応。
「単一の上書きは、どこで最終保証するか」に答える層。単一目的の上書きを担う。
Utility Test(このスタイルは特定の Block に帰属せず、単一プロパティ〔または密接に関連する最小プロパティセット〕で完結するか?)で判定する。各プロパティに !important を付与し、@layer の外にある unlayered CSS に対しても最終上書きを構造的に保証する(spec §5.8 MUST)。
.u-visually-hidden, .u-hidden-sp。
「どの層に書くべきか」を、センスではなく問いで決める。
そのパーツを別のサイトにそのまま持っていけるか? Yes なら Component。
このスタイルは、パーツ自身の視覚的責任か? Portability Test だけで判断が曖昧なときに補う。パーツ自身の責任なら Component、置く場所によって変わるなら Project。
このスタイルは、配置・寸法・空間の確保か? 中身の見た目は変わらないか? Yes なら Layout。
このスタイル、どの層に書くべきか。判断テストを順に辿ると、答えは一本の道筋として決まる。迷ったときは、各層の判断テストを上から順に当てていけばいい。
ここでは 2 つの角度から @layer の効き方を見る。ひとつは層の秩序が破られる一線と、それでも最後の例外を通す仕組み。もうひとつは、Component と Project の境界をどう判定するか。
理想は、すべてのスタイルが層に入っていること。けれど現実のサイトには、層から漏れたスタイルが紛れ込む。
@layer component {
.c-notice-card__note {
color: var(--color-text-light);
}
}
/* あとから誰かが足した一行、層に入れ忘れた借り物の CSS、
ウィジェットが注入するスタイル…… */
.c-notice-card__note {
color: red;
}
/* 層に入っていない素のスタイルは、層に入った
すべての通常スタイルに勝ってしまう */
!important が最終上書きを保証する@layer utility {
.u-hidden-sp {
@media (width < 48rem) {
display: none !important;
}
}
}
!important を付けた宣言は、層の内外を問わず、通常のスタイルすべてより一段上で勝負する。だから Utility に !important を付けておけば、層から漏れたスタイルがどこかに紛れていても、最後の例外は確実に通る(spec §5.8 MUST)。「勝つためにセレクタを強くする」という動機そのものが要らなくなる。
判断の核は Portability Test(そのパーツを別のサイトにそのまま持っていけるか?)。Yes なら Component、No なら Project。曖昧な場合のみ Responsibility Test(このスタイルは、パーツ自身の視覚的責任か?)で補う。
.c-button)— 別のサイトに持っていける/* 公開 API:
--button-color (default: var(--color-surface))
--button-bg-color (default: var(--color-accent))
--button-border-color (default: var(--color-accent))
--button-hover-bg-color (default: oklch(from var(--color-accent) calc(l * 1.08) c h)) */
@layer component {
.c-button {
--_color: var(--button-color, var(--color-surface));
--_bg-color: var(--button-bg-color, var(--color-accent));
--_border-color: var(--button-border-color, var(--color-accent));
--_hover-bg-color: var(--button-hover-bg-color, oklch(from var(--color-accent) calc(l * 1.08) c h));
display: inline-flex;
gap: var(--space-sm);
align-items: center;
justify-content: center;
padding: var(--space-sm) var(--space-lg);
color: var(--_color);
background-color: var(--_bg-color);
border: var(--border-width) solid var(--_border-color);
border-radius: var(--radius-lg);
@media (any-hover: hover) {
&:hover {
background-color: var(--_hover-bg-color);
}
}
/* Modifier: 静的なバリエーション */
&.-ghost {
--_color: var(--button-color, var(--color-main));
--_bg-color: var(--button-bg-color, transparent);
--_border-color: var(--button-border-color, var(--color-main));
}
}
}
ボタンの背景色・余白・角丸・枠線は「ボタン自身の視覚的責任」(Responsibility Test = Yes)。どのサイトにもそのまま持っていける(Portability Test = Yes)→ Component。
.p-home-hero)— このサイト固有の組み合わせ@layer project {
.p-home-hero {
display: grid;
place-items: center center;
min-block-size: 60dvb;
/* メインカラーの透明版を放射状に。Theme 変更に自動追従(相対カラー構文)*/
background: radial-gradient(ellipse at 50% 0%, oklch(from var(--color-main) l c h / 6%) 0%, transparent 70%);
}
/* Hero グラデーション上のダークモード可読性調整(c-button 公開 API を上書き)*/
.p-home-hero__sub-cta {
--button-color: light-dark(var(--color-main), var(--color-text));
--button-border-color: light-dark(var(--color-main), var(--color-text));
}
}
ヒーローのグリッド配置・最小高さ・背景グラデーションは「このサイト固有のデザイン要件」で、別のサイトにそのまま持っていけない(Portability Test = No)→ Project。
境界判定の要点: .p-home-hero__sub-cta は Project 層から Component の公開 API(--button-color / --button-border-color)を上書きしている。Component(.c-button)の中身は書き換えず、公開 API 経由で文脈に合わせる。これが「Component は配置先に依存しない自己完結したパーツ、Project はそれを文脈に合わせて組み合わせる」という責任分離の実コードだ。
Component が Project のクラスや Custom Property を参照してはならない(spec §3 MUST NOT)。上書きは常に上位層(Project)から下位層(Component)の公開 API に対して行う。