surface-separation について
surface-separationは、カードやパネルなどの見分け方を検討するデザイナーや開発者向けのUIデザインスキルです。ボーダー、エレベーション、塗りの使い分けに加え、不透明度やカラー トークン、入れ子のサーフェスに関する指針を示します。
主な機能
- 01ボーダー、エレベーション、塗りの選択
- 02入れ子サーフェスの区別方法
- 03UI状態に応じた不透明度の使用
- 04透明色よりカラートークンを優先
- 05サーフェス分離のチェックリスト
活用シーン
- 01カードやパネルの表現方法を選ぶ
- 02コンポーネントの不透明度と色を見直す
- 03入れ子UIの過剰な視覚的区分を減らす
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dembrandt/ dembrandt-skills --skill surface-separationインストール後、Claude Code で/surface-separationを入力して読み込まれたか確認します。
詳細
サーフェスの分離
カード、パネル、ツールバー、行などのサーフェスは、その背後にある要素と明確に区別できる必要があります。これを実現する方法はちょうど3つあります。ほとんどの場合、選択を誤ることより、どれかを選ばずに複数をすべて強く適用してしまうことが問題です。
サーフェスごとに戦略を1つ選ぶ
| 戦略 | 仕組み | どのように見えるか | 最適な用途 |
|---|---|---|---|
| 境界線 | 境界に1pxの線を引く | 明確で、描かれたような、記録的な印象 | 情報量の多いUI、テーブル、フォーム、境界自体に意味があるもの |
| エレベーション | 高さを示すシャドウ | 浮いている、一時的、上にある印象 | モーダル、ドロップダウン、ポップオーバー、ドラッグ中の状態 |
| 塗り | コントラストのある背景色 | グループ化され、埋め込まれ、ページの一部である印象 | セクション、サイドバー、交互に色を変えた行、インラインのコールアウト |
それぞれが答える問いは異なります。境界線は「サーフェスがどこで終わるか」を示します。エレベーションは「ページからどれだけ浮いているか」を示します。塗りは「何に属しているか」を示します。ユーザーが実際に抱いている問いに合わせて選びましょう。
ルール: 同じサーフェスで3つすべてを組み合わせてはいけません。見える境界線、ドロップシャドウ、ページとは異なる背景色を備えたカードは、境界を3回示しています。すべてが強調されているため、何も強調されません。UIを「ごちゃごちゃしている」あるいは「重たい」と感じる理由を指摘できないとき、その原因として最もよくあるのがこれです。
正当に組み合わせられるケース
境界線とエレベーションを一緒に使ってよいケースは、特定の1つです。明るいページ上にある明るいサーフェスで、控えめなシャドウだけでは境界がぼやけてしまう場合です。この場合、境界線が境界を定義し、シャドウが奥行きを表します。それぞれ異なる役割を担っています。シャドウのスケールと、組み合わせる境界線の色調については、[[elevation-and-depth]]を参照してください。
使うシグナルを2つにとどめるポイントは、サーフェスの塗りをページの色調と同じ、または近い色に保つことです。背景色も明確に異なる色合いにすると、境界を3つ目の方法で示すことになります。
入れ子構造
サーフェスの中にサーフェスを置く場合は、同じ方法を強めるのではなく、戦略を変えましょう。境界線付きカードの中に境界線付きの行を配置すると、階層のない線のグリッドが生まれます。内側の行には塗りの色を変えて使うか、内側の境界線をなくして余白に頼りましょう。近接だけでグループ化するのに十分な場面については、[[gestalt-ui-organisation]]を参照してください。
本当の制約となるのは、入れ子の深さです。視覚的に区別する階層が2段を超えると、どの戦略も明確に見えなくなります。代わりに構造を平坦化しましょう。
不透明度は状態を示すものであり、色合いではありません
不透明度が伝えるのは、要素が一時的に完全には利用できない状態にあることです。無効化、読み込み中、保留中、ドラッグ中などが該当します。UIにおける不透明度の役割はこれだけです。
.btn:disabled { opacity: 0.4; }
.card.is-loading { opacity: 0.7; }
.row.is-dragging { opacity: 0.5; }
この用途に限定すれば、意味が明確に伝わります。それ以外の理由で不透明度を使うと、シグナルとしての意味が失われます。
透明度で色合いを作らない
色を薄くするためにrgba(brand, 0.1)やopacity: 0.6を使うのは、避けるべきパターンです。代わりに、色合いを実際のトークンとして定義しましょう。
/* Wrong — the rendered colour depends on whatever is behind it */
.badge { background: rgba(37, 99, 235, 0.12); }
/* Right — a resolved token with a known value */
.badge { background: var(--color-brand-50); }
透明度を使うと、次の3つの問題が発生します。
- 色を特定できません。 背後にある要素に応じて色が決まるため、親要素、テーマ、スクロール位置によって変化します。同じコンポーネントの2つのインスタンスが、異なる色で表示されることになります。
- コントラストを検証できません。 WCAG比は、具体的な2つの色の間で算出されます。半透明のレイヤーは合成されるまで具体的な色が定まらないため、自動チェックではテキストの読みやすさを確認できません。ある状況では合格しても、別の状況では不合格になる可能性があります。
- デザインシステムで値を使えません。 アルファ乗数としてしか存在しない色合いはトークンではありません。参照、エクスポート、バージョン管理、テーマごとの差し替えができません。
ブランドカラーからアルゴリズムで色合いを導出し、トークンとして保存しましょう。1色または2色の基準色から全色域を導出する方法については、[[algorithmic-color-palette]]を参照してください。
半透明の上に重ねる半透明
半透明の要素を別の半透明の要素の上に置くと、問題はさらに悪化します。各レイヤーは直前のレイヤーに重なって作用するため、最終的に描画される色は、その下にあるすべてのレイヤーの連鎖に左右されます。コンポーネントを作成するときに、その連鎖を把握することはできません。
background-color にすでにアルファチャンネルが含まれている要素には、opacity を絶対に設定しないでください。 同じコンポーネントに両方が使われている場合、どちらか一方の役割が誤っています。背景を不透明なトークンにするか、opacity を状態に応じたものにして、背景を半透明にしないでください。
意図的に背景を予測できないようにするガラス効果やすりガラス効果は例外です。ただし、代償があります。その上に載せるテキストには不透明な下地を用意するか、最悪の背景に対してもコントラストが確保される保証が必要です。どちらもなしに、ガラス面に本文を載せないでください。
無効状態は例外であり、それが他の状態に適用されない理由です
無効状態の要素は WCAG のコントラスト最低基準の適用対象外です。だからこそ、低い不透明度が適切なのは無効状態だけであり、他の状態では適切ではありません。意図的に読みにくくすることがメッセージとして意図されているのは、この状態だけです。それでも、不透明度だけでは無効状態を伝えるには不十分です。cursor: not-allowed と非インタラクティブな状態を組み合わせてください。すべての状態の一覧については [[button-states]] を参照してください。
チェックリスト
- 各サーフェスでは、ページのトーンに合わせた塗りつぶしと、境界線またはエレベーションの組み合わせを使うか、単一の分離手法を使っていますか?
- 境界線、シャドウ、そして異なる塗りつぶしを同時に備えたサーフェスはありませんか?
- 入れ子になった要素の分離では同じ手法を繰り返すのではなく手法を切り替え、2 レベルまでにとどめていますか?
- コードベース内のすべての
opacity値は、無効、読み込み中、ドラッグ中などの状態に関連付けられていますか? - すべての色合いはアルファ乗数ではなく、実際のトークンですか?
- すでに半透明の背景の上に
opacityを設定しているコンポーネントはありませんか? - ガラス面上のテキストには、不透明な下地があるか、最悪の条件でもコントラストを確保できる保証がありますか?