コンテンツへスキップ

CSS の迷いをなくす、
層アーキテクチャ

v1.0 では 8 つの層で設計判断を体系化します。

layer-order.css
@layer tokens, theme, foundation, layout, component, project, animation, utility;
Tokens

プリミティブトークン

意味を持たない「素材」を定義する層。色コード、フォント名、数値スケールなど。「この青色の oklch 値はいくつか?」に答えます。

tokens/color.css
@layer tokens {
  :root {
    --slate-600: oklch(44% 0.06 240deg);
    --vermilion-500: oklch(55% 0.24 25deg);
    --neutral-900: oklch(17% 0.008 240deg);
  }
}

判断基準: 値に意味を持たせない。--slate-600 は「スレートの 600 番」であり、「メインカラー」ではない。

Theme

セマンティックテーマ

Tokens に意味のある名前を与える層。「プライマリカラーとしてどの色を使うか?」に答えます。ダークモードは Theme だけで切り替わります。

theme/color.css
@layer theme {
  :root {
    --color-main: var(--slate-600);
    --color-accent: var(--vermilion-500);
    --color-bg: var(--neutral-50);
    --color-text: var(--neutral-900);
  }
}

判断基準: Tokens の値に「役割」を持たせたいか? → Yes なら Theme。

Foundation

ベーススタイル

リセット CSS と要素セレクタのデフォルトスタイル。クラスを使わず HTML 要素そのものに適用します。:where() で詳細度をゼロに保ちます。

foundation/base.css
@layer foundation {
  :where(body) {
    font-family: var(--font-family-ja);
    color: var(--color-text);
    background-color: var(--color-bg);
  }
}

判断基準: クラスなしの HTML 要素に適用すべきスタイルか? → Yes なら Foundation。

Layout

ページの骨格

「配置と空間」だけを担当する層。中身の見た目(色、フォントサイズ等)には一切関与しません。

layout/l-grid.css
@layer layout {
  .l-grid {
    --_columns: 3;
    --_gap: 1.5rem;
    display: grid;
    grid-template-columns: repeat(var(--_columns), 1fr);
    gap: var(--_gap);
  }
}

判断基準: 色やフォントに触れているか? → 触れているなら Layout ではない。

Component

再利用パーツ

別のサイトにそのまま持っていけるか? これが Component の判断基準です。ボタン、カード、バッジ——このページで使われているパーツの多くが Component です。

component/c-button-cta.css
@layer component {
  .c-button-cta {
    background-color: var(--color-main);

    &.-large { font-size: 1.125rem; }
    &.-accent { background-color: var(--color-accent); }
  }
}

判断基準: 別のサイトにそのまま持っていけるか? → Yes なら Component。

Project

サイト固有パーツ

このサイトのデザインに依存しているか? ヘッダー、フッター、ヒーロー——これらは mFLOCSS サイト固有のデザインなので Project です。Component を組み合わせて、サイト固有の文脈を作ります。

判断基準: このサイトのデザインに依存しているか? → Yes なら Project。

Animation

動きの管理

アニメーションを分離し、prefers-reduced-motion 対応を一元管理する層。アクセシビリティを「仕組み」で担保します。

animation/a-fade-in-slide-up.css
@layer animation {
  .a-fade-in-slide-up {
    opacity: 0;
    translate: 0 20px;
    transition: opacity 0.6s, translate 0.6s;

    &.is-active { opacity: 1; translate: 0 0; }
  }
  @media (prefers-reduced-motion: reduce) {
    .a-fade-in-slide-up { opacity: 1; translate: 0 0; }
  }
}

判断基準: reduced-motion で無効化すべき動きか? → Yes なら Animation 層に集約。

Utility

局所的な調整

最後の手段。 他の層で解決できない局所的な微調整のみ。!important で最高優先度を保証します。

utility/u-hidden.css
@layer utility {
  .u-visually-hidden {
    position: absolute !important;
    inline-size: 1px !important;
    block-size: 1px !important;
    clip-path: inset(50%) !important;
  }
}

判断基準: Utility でないと解決できないか? → Component や Project で対応できるなら、そちらに書く。

「どの層に書くか」判断フロー

迷ったら、この順番で考えてみてください。

Q1

クラスを付けて使うスタイル?

NoFoundation(HTML 要素のデフォルトスタイル)

Yes → 次の質問へ

Q2

ページの骨格(配置・余白)に関するもの?

YesLayout(色やフォントに触れていないことを確認)

No → 次の質問へ

Q3

別のサイトにそのまま持っていける?

YesComponent

NoProject(このサイト固有のパーツ)

Q4

reduced-motion で無効化すべき動き?

YesAnimation(アクセシビリティ対応を一元管理)

Q5

他の層で解決できない局所的な微調整?

YesUtility(最後の手段)

No → Component か Project を再検討

なぜ、この順番なのか

@layer では、後に宣言された層ほど優先度が高くなります。mFLOCSS の 8 層は「抽象度が高いもの → 具体的なもの」の順に並んでいます。

宣言順 = 優先順位(低 → 高)
@layer tokens,      /* 1. 素材(最も抽象的) */
       theme,       /* 2. 意味付け */
       foundation,  /* 3. デフォルト */
       layout,      /* 4. 配置 */
       component,   /* 5. 再利用パーツ */
       project,     /* 6. サイト固有パーツ */
       animation,   /* 7. 動きの制御 */
       utility;     /* 8. 最終調整(最も具体的) */

Tokens が最低優先度なのは、他のすべての層から参照される「素材」だから。Utility が最高優先度なのは、どの層のスタイルも確実に上書きする「最終調整」だから。この順序は設計思想そのものです。

各層の「なぜ」を、さらに深く

各層の設計根拠の詳細と実案件の判断 Q&A は、書籍で深く解説しています。