この記事では、次の 2 つについて書きます。
- 前半: SCSS ファイルを分割して管理するときの 読み込み順(
global→base→layouts→components。そのあとutility) - 後半:
u-の役割(c-との違い、どこまでを補助クラスに寄せるか)
あくまで 自分のプロジェクトではこう切っている という整理です。チームや案件で最適な形は変わります。
プレフィックスの c- と p- の切り分け は、SCSS の c- と p- をどう分けるか で触れています。
読み込み順(自分の切り方)
自分は、次の順で @use しています。
@use './global' as *;
@use "./base/_reset";
@use "./base/_root";
@use "./base/_base";
@use "./layouts/**";
@use "./components/**";
@use "./utility/**";
projects 用のディレクトリは置かず、ページやセクション単位のまとまりも components 配下のファイル で扱っています(フォルダを分けないだけで、BEM の p- などの命名は案件のルールに合わせてよい、というイメージです)。
global
変数、mixin、function など、あちこちから参照される前提の定義 をまとめています。
最初に読んでおくと、base 以降のパーシャルから共通のトークンやヘルパーをそのまま使えます。
base
reset、:root 周り、タイポやリンクのデフォルトなど、ドキュメント全体の土台 に近いスタイルです。
レイアウトや部品特有の見た目はここには書かず、広く効かせたい最低限に寄せています。
layouts
ヘッダー・フッター・メインラッパーなど、ページの骨格 に当たるスタイルです。
base で整えた土台の上に、大きな枠の配置や余白のルールを載せるイメージです。
components
ボタンやカードなどの 小さな UI から、ページやセクション単位のブロックまで、ここに集約しています。
別ディレクトリに projects を切らずとも、「小さな部品を先に定義し、大きなブロック側で組み合わせや調整を書く」という流れは、ファイルの並びや import の順で意識しています。
utility(読み込みは最後)
u-pc / u-sp / u-screen-reader のような u-* を最後に読み込む ようにしています。
c-* やページ側のブロックより 後から効かせたい補助 だからです。
u-* は「部品」ではなく「補助」
u-* をどこまで許すかは、c-* との境界で迷いやすいです。
自分は、u-* を 部品そのものではなく、後から効かせる補助 に寄せると整理しやすい、と考えています。
向いているのは、たとえば次のようなものです。
u-pcu-spu-screen-reader
見た目のバリエーションというより、表示や可視性を制御するスイッチ に近い役割です。utility を最後に読んでおくと、「最後に効いてほしい補助」として扱いやすくなります。
u-pc は強くてよい(自分の前提)
u-pc や u-sp は、コンポーネントの見た目を作るためのクラスではなく、表示制御を確定するためのクラス です。
そのため、c-* より後から読み込まれ、強く効く設計で問題ない、という前提にしています。
たとえば display を部品側で書いていても、最後に u-pc で表示・非表示を切り替えられる方が、マークアップ側の都合で扱いやすいです。
一方で、この考え方を すべての u-* に広げると、c- との境界が崩れやすいです。「後勝ちの補助」として強くしたいのか、見た目を持った部品なのか は分けて考える必要があります。
marker 系は u- より c- が自然(例)
マーカー風の下線装飾は、u-marker より c-marker の方がしっくりきます。
単なる補助ではなく、見た目を持った小さな部品 だからです。
親やセクション側からカスタムプロパティで調整したくなることも多いです。
.c-marker {
--marker-color: color-mix(in srgb, var(--color-theme), transparent 70%);
--marker-width: 0.5rem;
}
.p-hero {
--marker-color: color-mix(in srgb, var(--color-accent), transparent 40%);
}
このように、ブロック側から --marker-color や --marker-width を上書きするなら、それは「最後に強く効く補助」ではなく、コンテキストから調整される部品 です。
これを u-* に置くと、読み込み順の都合で「後勝ちの強い補助」として振る舞いやすくなり、部品としての調整余地を狭めがちです。
自分の判断基準はだいたい次のとおりです。
- 見た目を持つ小さな部品なら
c-* - 親からカスタムプロパティで調整するならなおさら
c-* - 後から効かせたい補助なら
u-*
読み込み順で役割を分ける(再掲)
自分のプロジェクトでは、層を次のように捉えています。
global: 共有の変数・mixin などbase: リセットとドキュメント全体の土台layouts: ページの骨格components: 部品とページ/セクション単位のまとまり(projectsディレクトリは切らない)utility: 最後に効かせたいu-*
順番が前後すると、どこが土台でどこが上書きか が読みづらくなります。細かい実装ルールというより、各層の責務を保つための順番 として割り切っています。
まとめ
- SCSS は
global→base→layouts→components→utilityの順で読み込んでいる projectsディレクトリは使わず、ページ単位もcomponents側 にまとめているu-*は 補助・表示制御 に寄せ、c-*は 見た目を持つ部品(親から調整されるものも含む)として分ける
c- と u- の分類と、上の読み込み順を揃えておくと、追加するときの置き場所と上書きの意図が追いやすくなります。