Kumiki の data-as コンポーネントを SCSS で書くときの三層再利用ルール。卒業(data-as→FLOCSS)に乗る、grep 可能なクラス名規律を保つための約束。
再利用は 3 層で構成する。各層は機能ではなく思想で役割が決まる。
| 層 | 実体 | 役割 | 厚み |
|---|---|---|---|
| 第1層 | @mixin | コンポーネントの外側で決まる条件を @content で内側に掛ける機構 | 極薄 |
| 第2層 | %placeholder | data-as 語彙 1 件に対応する、自己完結したコンポーネント定義 | 厚い(ここに実質を全部置く) |
| 第3層 | [data-as="…"] | 第2層を claim する公開語彙 | 極薄 |
第1層と第3層は外との境界であり、責務を盛ると複雑性が外へ漏れる。実質はすべて、変更が内部に閉じる第2層に置く。
その条件は、コンポーネントの「外側」で決まるか?
@content を取れることは第1層の必要条件にすぎない。十分条件は「外部条件への応答」であること。
& の規則(三層共通・最重要)& には性質の異なる 2 種類があり、層ごとに扱いを変える。
&(&__element, &--modifier) … 完全修飾子を省略形に畳んで隠す。.bl_card__title なら grep 一発で当たるが、&__title は文脈補完なしには辿れない。名前が実体を正直に指すという Kumiki の原則(grep 可能性)を損なうため、全層で禁止。&(&:hover 等) … 連結ではなく、@content で渡されたブロックを「親の相互作用状態」に掛け直す折り返し点。@content と一体で第1層の仕事を構成する。修飾子を隠さない(親セレクタ名はソースに残る)。第1層に限り許可。& 許可。ただし以下に厳密に限定する。:hover / :active / :focus / :focus-visible / :focus-within。@content で親に掛け直すコールバック」として機能する。CSS 文法上、相互作用状態は直前のセレクタにしか付けられないため & が言語仕様上不可避 ——境界ゆえの例外として正当化される。上記 5 つ以外には第1層でも & を使わない(擬似要素 ::before/::after、:checked・:disabled 等のフォーム状態、:nth-* 等の構造擬似クラス、BEM 連結 ——すべて例外に入らない)。& 全面禁止。 完全修飾子を書く。%kumiki-card__title / %kumiki-card--active、擬似要素も %kumiki-card__title::before とフル修飾。&__ / &-- 連結は不可。(コスト:擬似クラス/擬似要素のたびに親セレクタ名を反復する。これは grep 可能性と引き換えの必然的コストとして受け入れる。)& 不可。 @extend 一行のみで出番がない。@mixin(外部条件ラッパ)コンポーネントの外側の状態に応答する、@content ラッパのみ。
sp / tablet / pc、print、prefers-reduced-motion、prefers-color-scheme、orientation ほかメディア特性@container (min-width: …) ラッパhover(@media (hover: hover) + &:hover)、focus 系 ——§2 の User Action Pseudo-Classes に対応truncate, visually-hidden, clearfix など)→ 外部条件に応答しない。第2層へ直書き。grid($cols) など)→ 引数は外部環境ではなく内部の構造都合。第2層へ。@content を取るだけで外部条件が介在しないラッパ)→ 第1層ではない。@content を取る(または外部環境クエリを掛ける)形だけ。& は §2 の 5 擬似クラスに限り使用可。連結・擬似要素・その他擬似クラスには使わない。readable 内で sp を呼ぶ等の合成は可)。第3層から直接呼ばない。%placeholder(名前付きコンポーネント)data-as 語彙 1 件に対応する、コンポーネントの実体すべて。
max-width, font-size, 余白、色…)を自分で持つ。@include して内側に畳み込む。%(%kumiki-card-featured のような独立した名前)として分ける。引数で連続変化させず、語彙として離散列挙する。&(§2:全面禁止。完全修飾子で書く)。@include 行だけで構成され、固有宣言を 1 つも持たない %(=第1層の単なるプロキシ)。これは設計エラー。その第1層 @mixin を第2層へ昇格すべきだったサイン。@media の中で定義しない)。% の内部に第1層 @include として畳み込む(@include sp { … } / @include hover { … })。& は第1層 mixin の内部にあり、第2層のソースには現れない。% として書く(%kumiki-card__title)。% → % の @extend で第2層内部に再利用を導入してよい。その複雑性も第2層に閉じ込め、第1層・第3層には漏らさない。[data-as="…"](公開語彙)対応する第2層を claim する @extend 一行のみ。
[data-as="prose"] { @extend %kumiki-prose; }
@include、@media、& ——一切書かない。@extend 以外が出たら第2層へ押し戻す。@extend は必ずルート位置に書く。@media の中に書かない。(メディアクエリ越えの制約を構造的に踏まない。第2層が内部に環境応答を畳み込んでいるため、ルートからの claim だけで全条件に波及する。)@extend を @media の中に書かない。 コンポーネントは内部 MQ ごとルートで定義し、ルートからのみ claim する。& の三層規則(§2)。 第1層は User Action Pseudo-Classes(:hover/:active/:focus/:focus-visible/:focus-within)に限り許可、第2層・第3層は禁止。連結 &__/&-- は全層禁止。@extend だけ。 第1層を直接 @include しない。card-sp のような語彙を作らず、% 内部に @include sp で畳み込む。語彙化するのは意味として独立したバリアントだけ。% が固有宣言ゼロ = プロキシ化の警告灯。 レビュー/リンタの検出ポイント。// ── 第1層:外部条件ラッパのみ。──
// 環境ラッパ(& なし)
@mixin sp { @media (max-width: 600px) { @content; } }
@mixin tablet { @media (max-width: 1024px) { @content; } }
@mixin container($w) { @container (min-width: #{$w}) { @content; } }
// 相互作用ラッパ(& は User Action Pseudo-Classes に限り許可)
@mixin hover { @media (hover: hover) { &:hover { @content; } } }
@mixin focus { &:focus-visible { @content; } }
// grid($cols) / truncate / 単なる囲い はここに無い
// ── 第2層:実質を全部持つ。固有宣言は直書き。& は使わず完全修飾。──
%kumiki-prose {
max-width: 65ch;
font-size: 1.6rem;
line-height: 1.8;
overflow: hidden; // truncate 相当を切り出さず直書き
text-overflow: ellipsis;
white-space: nowrap;
@include sp { font-size: 1.4rem; } // viewport 応答(第1層経由)
@include hover { opacity: 0.7; } // 相互作用応答。& は hover mixin の内部にあり、ここには出ない
}
// 子要素は完全修飾の別 % として書く(&__title は使わない)
%kumiki-prose__lead {
font-size: 1.8rem;
font-weight: bold;
}
// ── 第3層:extend 一行のみ。──
[data-as="prose"] { @extend %kumiki-prose; }
[data-as="prose-lead"] { @extend %kumiki-prose__lead; }
prefers-color-scheme で決まる(=環境)なら第1層ラッパ。[data-theme="dark"] のようなアプリ注入状態で切るなら、それは環境ではなく語彙体系の話なので第3層側で扱う。出どころで層が変わる。hover 系:第1層 mixin が「@media (hover: hover) 環境クエリ」と「&:hover への掛け直し」を一体で内包する。& は §2 の例外として第1層内部にのみ存在し、第2層には漏れない。::before/::after):相互作用状態ではなく「自分の見た目の一部」。第1層の & 例外には入らない。第2層で完全修飾(%kumiki-card__title::before)として書く。:checked/:disabled 等):ユーザー操作の結果だが、コンポーネント/データが持つ持続的状態であり、transient な相互作用動作ではない。第1層の & 例外には入れない。@container:viewport より純度が高い外部条件(自分が置かれた親に応答する)。Kumiki の「DOM 不変・文脈応答」思想の中核に据えてよい。第1層は外部条件(環境+相互作用)を
@contentで掛けるラッパだけ。&は User Action Pseudo-Classes に限り許可、固定塊・引数は不可。第2層はdata-as語彙に対応する%に実質を全部持ち、応答を内側へ畳み込み、固有宣言は重複を恐れず直書きし、&は使わず完全修飾で書く。第3層は@extend一行だけ(ルート位置、宣言・@include・@media・&不可)。連結&__/&--は全層禁止。両境界(第1・第3層)は薄く保ち、太らせるのは第2層に限る。