product-ui-taste について
product-ui-tasteは、ダッシュボード、データテーブル、フォーム、管理コンソールなどの高密度なプロダクト画面向けデザインスキルです。データやユーザー、操作の影響を見極めてから構造やデザインシステムを選び、実データでの利用を考慮します。
主な機能
- 01画面の種類とデータ量を判定
- 02密度・複雑さ・操作の影響を設定
- 03既存のデザインシステムを優先
- 04テーブルや複数ステップ画面の構造を選択
- 05空・読み込み・権限状態を考慮
活用シーン
- 01絞り込みや選択ができるデータテーブルの設計
- 02管理コンソールの構築
- 03影響の大きい操作を含む設定画面の計画
- 04複数ステップのウィザード作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ huytieu/ cog-second-brain --skill product-ui-tasteインストール後、Claude Code で/product-ui-tasteを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/block-skeletons.mdcanonical-sources.mdhouse-ui-rules.mdinstall-commands.mdsemantic-zoom.md詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
product-ui-taste:高密度プロダクト画面向けアンチスロップスキル
ダッシュボード、データテーブル、フォーム、ウィザード、設定、一覧/詳細、管理コンソール、アプリのシェルが対象です。ランディングページ、ポートフォリオ、マーケティング向けではありません(それは
taste-skillの対象です)。グラフも対象外です(それはdatavizの対象です)。ネイティブモバイルも対象外です。 すべてのルールは状況に応じた機械的なものです。自動的に適用されるのではなく、構築する画面に応じて適用されます。画面を読み取り、フレームの予算を決め、適合するものだけを取り入れてください。 連携の契約:taste-skillは「ダッシュボード/高密度なプロダクト UI/データテーブル/複数ステップのフォーム」を「適切なツール」に明示的に引き継ぎます。このスキルがそのツールです。依頼の半分がマーケティング、半分がプロダクト(ライブダッシュボードを埋め込んだランディングページなど)の場合、ヒーロー/マーケティングセクションにはtaste-skillを、プロダクト画面にはこのスキルを使ってください。同じコンポーネントに両方を適用してはいけません。
0. プロダクトの読み取り(何よりも先に画面を読み取る)
マーケティング UI は第一印象が重要です。プロダクト UI は、実際のデータを使って業務を行う人が100回目に使うときが重要です。スロップの失敗パターンも異なります。「テンプレート化された見た目」ではなく、実データに触れた途端に破綻するプロトタイプです。ページ全体が横スクロールするテーブル、ラベル自体が省略表示されるボタン、3つの異なる状況で使い回される単一の灰色の「データなし」ボックス、ユーザーが1つのフィールドを誤入力しただけで内容が消えるフォームなどです。
0.A まずこれらの手掛かりを読み取る
- 画面の種類 — 最も重要な読み取り項目です。次のいずれかです。インデックス/テーブル(走査、絞り込み、選択するレコード一覧)、詳細/レコード(1つのエンティティとそのフィールド、関連データ)、ダッシュボード/概要(KPI + ウィジェット + 1つか2つのテーブル)、フォーム/設定(入力と設定)、複数ステップのフロー/ウィザード(順番に進めるタスク)、コンソール/オブザーバビリティ(ログ、メトリクス、デプロイ)、フィード/メッセージング(項目のストリーム)。実際の画面の多くは、2つか3つの組み合わせです(インデックス + 詳細ドロワー、ダッシュボード + 詳細表示用テーブルなど)。
- データの形状と量 — p95での行数、行の幅、セルが均一か不揃いか、データがライブ/ストリーミングか静的か、フィールドが空/null/非常に長くなる可能性があるか。
- 必要な密度 — トレーダーのコックピットと一般消費者向けの設定ページは、どちらも「プロダクト UI」ですが、必要とする密度は正反対です。エキスパート向けの日常利用ツール(コンパクト、キーボード優先)か、たまに使う一般ユーザー向け(ゆとりがあり、寛容)かを読み取ってください。
- 操作の影響度 — 主なアクションは情報の閲覧、取り消し可能(名前変更、移動)、それとも破壊的/取り消し不可能(削除、デプロイ、カードへの課金)ですか。この判断によって、確認、フォーカスの初期位置、自動保存の適用可否が決まります。
- ユーザーと許可されている操作 — ロール、読み取り専用の閲覧者、プラン/ライセンスの階層などです。プロダクト UI には、マーケティング UI にはない状態があります。権限拒否、読み取り専用、プランによるロック(セクション 6.G)です。正常系を構築した後ではなく、今の段階で確認してください。
- 既存システム — すでに Carbon/Fluent/Polaris/Atlaskit アプリや、独自のトークンセットがありますか?プロダクト UI を新規にゼロから作ることはほとんどありません。独自のスタイルを取り込む前に、ホストシステムに合わせてください。
0.B 生成前に「プロダクトの読み取り」を1行で出力する
コードを書く前に、次の形式で述べてください。「次のように読み取っています:<画面の種類>。対象ユーザーは<ユーザー>、密度は<密度>、<データ量>、操作の影響度は<レベル>、<デザインシステム | 独自トークン | なし>をベースに構築。」
例:
- 「次のように読み取っています:社内オペレーション向けのインデックス + 詳細ドロワー。コンパクトな密度、約2,000行(仮想化)、操作の影響度は取り消し可能、独自システム(テーブル + サイドパネルのインスペクター)をベースに構築。」
- 「次のように読み取っています:エンドユーザー向けの設定/フォーム画面。ゆとりのある密度、データ量は少なめ、操作の影響度は混在(請求は破壊的)、独自システムのフォームレイアウトをベースに構築。」
0.C 最も重要なアンチデフォルト:カードではなく行を使う
AI プロトタイプのような見た目のプロダクト画面を最も手早く作る方法は、すべてのレコードをバッジ付きのカードで囲むことです。ユーザーがスキャン、フィルタリング、並べ替え、選択する密度の高いデータには、端から端まで広がり、区切り線があり、行の高さが 32~40px の行(列形式ならテーブル、1 行ならリスト/アイテム)を使います。カードはウィジェットのコンテナ(KPI タイル、チャートパネル、設定グループ、ギャラリーのエントリー)であり、リスト項目を囲むラッパーではありません。[Material3 lists; Carbon] レコードのリストを見てカードのグリッドを最初に思いついたら、立ち止まってこの行を読み直してください。
0.D 要件が曖昧なら、質問を 1 つしてから進める
尋ねる価値のある質問は、ほとんどの場合、データ量または結果の重大性(アーキテクチャが変わるため)に関するものです。「最大でおよそ何行ありますか?」または「この操作は取り消せますか?」と尋ねます。それ以外は、プロダクトの読み取りから推測し、前提を伝えます。質問攻めにはしないでください。
0.E アンチデフォルトの徹底
プロダクト UI におけるモデルのデフォルトは、カードだらけの画面、ページからはみ出す固定幅の<table>、唯一の検証手段としてのdisabledな送信ボタン、汎用的な空状態 1 つ、z-index: 9999、そして読み込みのたびに表示される全ページのスピナーです。それぞれに、以下で説明する適切な置き換え方法があります。いずれかを使いそうになったら、該当するセクションを開く合図です。
1. 3 つのダイヤル(コア設定)
プロダクトの読み取り後、3 つのダイヤルを設定します。表現豊かなマーケティング向けに調整する嗜好スキル(VARIANCE/MOTION/DENSITY)とは異なり、プロダクトのダイヤルは情報作業向けに調整します。プロダクト UI ではモーションは意図的にほぼなくし、装飾ではなくフィードバックとして使います。
DENSITY: 2- 1 = ゆったり/コンシューマー向け、2 = 標準、3 = コンパクト/コックピット。行の高さ、パディングのスケール、フォントサイズ、密度切り替えを用意するかどうかを決定します。DATA_COMPLEXITY: 2- 1 = シンプルなリスト(プレーンなテーブル/リスト)、2 = 並べ替え・フィルタリング・選択が可能なテーブル、3 = エンタープライズグリッド(固定列、仮想化、列管理、ライブデータ)。CONSEQUENCE: 2- 1 = 情報表示/読み取り、2 = 取り消し可能な変更、3 = 破壊的/不可逆/金銭に関わる操作。確認のデフォルト、自動保存の可否、エラーのトーン、「未保存の変更」の目立たせ方を決定します。
ベースライン:2 / 2 / 2(詳細ビューを備えた標準的な SaaS プロダクトのテーブル)。プロダクトの読み取りに基づいて上書きします。
1.A ダイヤルの推定
| シグナル | DENSITY | DATA_COMPLEXITY | CONSEQUENCE |
|---|---|---|---|
| 「社内管理/運用ツール/エキスパート向けの日常ツール」 | 3 | 2-3 | 2 |
| 「エンタープライズのデータコンソール/グリッド/分析」 | 3 | 3 | 2 |
| 「コンシューマー向けの設定/アカウント/オンボーディング」 | 1 | 1 | 1-2 |
| 「SaaS プロダクトのダッシュボード(デフォルト)」 | 2 | 2 | 2 |
| 「請求/削除/デプロイ/権限の画面」 | 一致 | 一致 | 3 |
| 「オブザーバビリティ/ログ/ライブメトリクス」 | 3 | 3 | 1 |
1.B ユースケースのプリセット
| プリセット | DENSITY | DATA_COMPLEXITY | CONSEQUENCE | フレーム |
|---|---|---|---|---|
| 社内管理ツール | 3 | 2 | 2 | AppShell + SideNav、選択時にインスペクターを表示 |
| SaaS プロダクトのダッシュボード | 2 | 2 | 2 | AppShell + SideNav または TopNav + TabList |
| エンタープライズのデータコンソール | 3 | 3 | 2 | AppShell + SideNav、全高のグリッド |
| コンシューマー向け設定/アカウント | 1 | 1 | 1-2 | AppShell + 設定テンプレート(ナビゲーション + フォームパネル) |
| 複数ステップのウィザード | 1 | 1 | 2-3 | 中央配置の列、ステッパー、注意をそらすクロームなし |
1.C ダイヤルが出力をどう左右するか
推論時には、これらの正確な名前をグローバル変数として使います。DENSITYは行の高さと密度切り替えの判断(セクション 10.A)を制御します。DATA_COMPLEXITYはテーブルの詳細解説(セクション 7)をどこまで掘り下げるかを制御します。CONSEQUENCEはセクション 6.G、8.E、8.F、9.A を制御します。別名を作らないでください。
2. プロダクトのアーキタイプ → デザインシステムの対応表
プロダクト UI で、CSS をほぼすべて手作業で作ることはありません。まずホストアプリから基盤を選び、次にアーキタイプを選びます。1 つのアプリにつき 1 つのシステムにします。Carbon と Fluent を混在させたり、Polaris のツリーに shadcn を追加したりしないでください。
2.A まず既存のデザインシステムを調べる(推測ではなく、実際の仕様を確認する)
アプリにすでにデザインシステムがある場合は、そのシステムを優先します。最初にすべきことは、何が実際に提供されているかを確認することであり、決めつけることではありません。このスキルが扱う、情報量の多い画面での問題は、成熟したシステムの多くですでに解決されているため、生の CSS に頼ることはほとんどありません。コンポーネント名やプロパティを推測すると、見た目は正しくてもコンパイルできないコードをエージェントが生成することがよくあります。
UI を書く前に、次の項目をホストシステムの実際の API(CLI、ドキュメントサイト、または .d.ts ファイル)で確認してください。
| 必要なもの | 確認する内容 | 確認方法 |
|---|---|---|
| 全ページのシェル | ナビゲーションスロットを備えたアプリシェルのプリミティブ | システムのドキュメントにある「layout」または「app shell」のページ |
| 複数ペインのツール/インスペクター | 2 つの浮動 div ではなく、レイアウトとサイドパネルの組み合わせ | ドキュメントの「layout」ページ。パネルのサイズを変更できるか確認 |
| 列形式のデータ | システムの Table と、その並べ替え/選択/ページネーションのプラグイン | ブログの使用例ではなく、コンポーネントリファレンス |
| 固定表示/フリーズ列 | 専用のフックまたはプラグイン(多くのシステムで提供されています) | 独自実装する前に、ドキュメントで「sticky」または「frozen」を検索 |
| サイズ変更可能な列 | 列幅変更用のフック | 同上 |
| ページネーションフッター | Table のページネーションとページサイズコントロール | コンポーネントリファレンス |
| 検索+フィルターツールバー | 単純な検索とは別のツールバー/フィルターコンポーネント | コンポーネントリファレンス |
| 1 行のレコード | List と Item の組み合わせ | コンポーネントリファレンス |
| 空の領域 | EmptyState コンポーネント | コンポーネントリファレンス |
| ステータス/メタデータ | Badge とは異なるステータスインジケーター | トークン/ステータスのドキュメント |
**ワークフロー(必ず実施):**システムがすでに提供している、最も近いページまたはブロックテンプレートを見つけ、そのレイアウトの骨組みを調べてから、使用するすべてのコンポーネントの実際のプロパティを確認します。**プロパティを決して捏造しないでください。**間隔をシステムの尺度に合わせるため、生の <div> よりもシステム独自のレイアウトコンポーネントを優先します。すべての値にデザイントークン(var(--color-*)、スペーシングトークン)を使い、生の hex 値や px 値は使いません。Badge は件数や列挙された状態に限って使用し、ステータスにはステータスインジケーターを使います。
既存のデザインシステムとの橋渡し(矛盾を解消する):セクション 5~10 の CSS の仕組み(スクロールの制御、position:sticky ヘッダー、フリーズ列のオフセット、z-index の階層、min-width:0)は、成熟したシステムのフックが内部で満たす契約です。そのようなシステムでは、まずフックまたはプラグインを使ってください。適切な固定列フックなら、オフセット、不透明な背景、影の区切りを正しく設定できます。生の CSS の契約に頼るのは、(a)表示が透けるなどの問題があり、システムが正しく実装しているかを確認する場合、または(b)デザインシステムをまったく使っていない場合に限ります。フックが存在するのに、システムの Table 内でインラインスタイルを使って固定列を独自実装してはいけません。
2.B 外部システム(ホストに合わせるか、アーキタイプで選択)
| アーキタイプ/概要 | 選択するもの | 理由 |
|---|---|---|
| IBM風のエンタープライズ分析、密度の高いグリッド | @carbon/react + @carbon/styles | データ密度、2xグリッド、空状態/読み込みパターンが最も成熟しています [carbon] |
| Microsoft/エンタープライズ SaaS、DataGrid | @fluentui/react-components (v9) | 公式の Fluent 2、Overflow プリミティブ、トークン [fluent] |
| Atlassian/Jira風の製品 | @atlaskit/* + @atlaskit/tokens | 意味に基づいて選ばれたセマンティックな間隔トークン [atlaskit] |
| マーチャント/管理画面、リソース一覧 | Polaris(@shopify/polaris または Web コンポーネント) | IndexTable、4状態の考え方、エラーコンテンツ [polaris] |
| GitHub風の開発ツールのクローム | @primer/react / @primer/css | PageLayout のランドマーク、Blankslate、Truncate [primer] |
| 適応型/標準レイアウト、コンシューマー向け | @material/web + Material 3 トークン | ウィンドウサイズクラス、リスト詳細/補助ペイン [material3] |
| コードを自分で管理する軽量な社内ツール | Radix Primitives + shadcn/ui + TanStack Table | ヘッドレスなアクセシビリティ対応プリミティブ、スタイルは自分で管理 [radix, tanstack] |
| 高速でデータ密度の高いエンタープライズ React | Ant Design または Mantine + Mantine React Table | 機能が揃った Table/Form [ant, mantine] |
| キーボード操作を重視した軽量なツール(Linear/Vercel風) | Radix/shadcn + カスタムトークン | 密度 + コマンドパレット + 抑制されたデザイン [linear, vercel] |
**誠実さのルール:**アプリがこれらのいずれかに該当する場合は、公式パッケージとその Table/Form/Dialog を使用してください。Carbon のデータテーブルを生の CSS で再現しないでください。システムのトークンをインポートして、その90%を上書きしないでください。
3. フレームを最初に考えるアプリシェルのアーキテクチャ
**コンテンツを1行でも書く前に、フレームのピクセル数を割り当ててください。**実際のアプリケーションはトップダウンで構築します。シェルを選び、領域に名前を付け、それぞれに明示的な px の割り当てを行い、コンテナーの方針とレスポンシブ時の動作を決めてから内容を入れます。コンテンツ先行のレイアウト(セクションを書き、それぞれを Card で囲む)では、余白のあるスクロール列になり、プロトタイプのように見えます。[ほとんどのシステムのレイアウトドキュメント]
3.A シェルを選ぶ
- AppShell(ヘッダーやサイドナビゲーション)- 全ページに共通する標準的な製品フレームです。
- Layout + LayoutContent + LayoutPanel - エクスプローラー、コンソール、インスペクター付きマスターディテールなどのマルチペインツールです。
- プレーンなコンテンツ列 - 単一のフォーム、設定セクション、ウィザードです。
3.B 領域ごとの px 割り当て(覚えておくこと)
| 領域 | 割り当て |
|---|---|
| サイドナビゲーション | 240-280px |
| アイコンレール(折りたたまれたナビゲーション) | 64-72px |
| 詳細/インスペクターパネル | 340-420px(サイズ変更可能な場合は320-480) |
| フィルター/ファセットレール | 220-260px |
| 行の高さ | 32px(コンパクト)/40px(標準)/48px(ゆったり)[高密度なシステムでは32-40を割り当てます。Carbon には相対的な5段階があり、固定 px はありません] |
| ヘッダー/トップナビゲーション | 48-64px(一般的なアプリバーの慣例) |
3.C ランドマーク領域(混同しないこと)
各領域に実際のランドマーク名を付け、その違いを把握してください [primer PageLayout]:
- Header - 全幅、上部、アプリのクロームです。
- Content - メイン領域で、残りのスペースを埋めるように伸縮します。
- Pane - Content の横にある副次的な領域で、コンテンツの高さに限られます。インスペクターや補足情報に使用します。
- Sidebar - 全高のナビゲーションで、Pane とは異なります。コンテンツの高さに限られるペインを「サイドバー」として構築したり、全高のナビゲーションを「ペイン」として構築したりしないでください。
- Footer - 全幅、下部(ステータスバー、ページネーションのコンテキスト)です。
3.D 標準的なレイアウト(見分けて骨組みを作る)[material3]
- リスト詳細 - 左側にリスト、右側に選択したレコードを表示します。約1024px未満では、詳細をオーバーレイ表示するか、別のルートにします。
- 補助ペイン - メインコンテンツが約2/3、補助ペインが約1/3です。
- フィード - ひと続きのスクロールストリーム(行/吹き出しで構成し、ストリーム内にカードは置きません)。
- インスペクター付きマスターディテール(ツールの基盤)- 行を選択すると固定幅の LayoutPanel インスペクターが開き、画面遷移はしません。
resizableを追加します。約1024px未満では Content を圧縮せず、インスペクターをオーバーレイ表示します。
3.E マーケティング UI にはない、アプリシェルのクローム
- ワークスペース/組織スイッチャー - ほぼすべての SaaS シェルに必要です。サイドナビの上部またはヘッダー左側に配置し、現在のテナント名/アバターを表示します。クリックすると、モーダルではなくドロップダウンが開き、ワークスペースを一覧表示します。ワークスペースが 8 つ以上ある場合は検索フィールドを設け、「最近使用したワークスペース」グループと、下部の「作成/参加」アクションも表示します。これはコンテキスト(データのスコープ)を切り替えるものであり、ナビゲーション先ではありません。要素は 1 つにまとめ、テナントの選択肢をナビゲーションリストに散在させないでください。ユーザーごとに最後にアクティブだったワークスペースを保持します。
- グローバルコマンドパレット(Cmd/Ctrl-K)- DATA_COMPLEXITY >= 2 のキーボード優先ツールでは、最速のナビゲーション手段です。あると便利な機能ではなく、シェルの第一級の機能として扱ってください。
- バックグラウンドジョブ/非同期ステータストレイ - アプリが長時間かかる処理(インポート、エクスポート、デプロイ)を並行して開始する場合、ジョブごとのモーダルよりも、常時表示される通知トレイ(件数バッジ付きのヘッダーアイコンをクリックするとジョブのドロップダウンが開く)が適しています。各ジョブの行には、進捗を示すプログレスバー、キャンセル操作、完了時の成功/エラー表示と結果へのリンクを表示します。Carbon の原則:数分を超える待ち時間では、ユーザーがその場を離れられるようにし、完了時に通知します。1 つのバックグラウンドジョブで UI 全体をブロックしてはいけません。[carbon loading]
3.F リージョンごとのコンテナー方針(カードと行の使い分けをアーキタイプごとに決定)
| アーキタイプ | コンテナー方針 |
|---|---|
| トラッカー/業務ツール(課題、チケット、CRM) | 行のみ。端から端までグループ化したリストにし、カードは使用しません。 |
| コンソール/オブザーバビリティ | ダッシュボードウィジェットにはカードグリッド、それ以外にはテーブルを使用します。 |
| メッセージング/フィード | 行と吹き出しを使用します。ストリーム内にカードは使用しません。 |
| メディア/ギャラリー | カードグリッド(ClickableCard)と、詳細画面の高密度なメタデータ行を使用します。 |
| 設定/フォーム | FormLayout のセクションを使用します。危険な操作/請求関連の操作をまとめる場合に限り、Card を使用します。 |
禁止事項:リストの各項目をカードにする(カードだらけになる)、ページ構造として全幅カードを積み重ねる、カードを入れ子にする、装飾として Badge を使う。
4. レイアウトの厳格なルール(違反すると、壊れた成果物がリリースされます)
- スクロールの所有権(プロダクト UI レイアウトで最もよくある不具合)。 各スクロール軸(
overflow:auto)を所有するラッパーは、必ず1つだけにしてください。ページの<body>は、横スクロールしてはいけません。幅の広いテーブルは、それ自体のコンテナー内でスクロールさせ、アプリ全体を横に押し広げてはいけません。スティッキーヘッダーでは、そのスクロールラッパーのスコープ内でposition:stickyを使い、ビューポートに対するposition:fixedは決して使わないでください。[carbon, ant table-scroll, tanstack] 入れ子になった2つの要素が同じ軸をスクロールするなら、それは不具合です。 - すべてのスティッキー要素には、高さが制限された祖先要素が必要です。 スクロール境界を確立する祖先要素がないと、
position:stickyは何もせず、気づきにくい状態になります。テーブルヘッダーをスティッキーにするには、テーブルのスクロールコンテナーに実際の最大高さを設定する必要があります。[css mechanics] - テキストを含むフレックスの子要素には
min-width:0を指定してください。 フレックス/グリッドの子要素は、デフォルトでmin-width:autoになっており、コンテンツのサイズより小さく縮みません。そのため、text-overflow:ellipsisは何もせず、代わりに子要素がレイアウトを横に押し広げます。フレックス行内のテキストを切り詰める場合は、テキストを含む子要素にmin-width:0が必要です。「省略記号が機能しない」最もよくある原因です。[overflow-truncation] - 各領域のレスポンシブ対応方針を、事前に明示してください。 内容を埋める前に、各領域がどのブレークポイントで折りたたまれるのか、オーバーレイになるのか、非表示になるのかを記述してください。「Tailwind が対応してくれる」という思い込みは禁物です。ナビゲーションはアイコンレールまたはオフキャンバスの MobileNav に折りたたみます。インスペクターは約 1024px 未満でオーバーレイにします。幅の広いテーブルは、列をリフローするのではなく、横スクロールを維持します。
- ブレークポイントにはデバイス名ではなく、サイズクラスを使ってください。 Compact/medium/expanded(Material 3)、または代替グリッドとして Carbon の正確な 2x グリッドのブレークポイント(320/672/1056/1312/1584、8px のミニ単位、16px のパディング、32px のガター)を使います。[material3, carbon 2x-grid]
- 再利用可能なコンポーネントにはコンテナークエリを使ってください。 幅の広い Content 領域にも狭い Pane にも表示されるカードやパネルは、ビューポートではなく、自身の幅に応じて変化する必要があります。複数の領域幅で表示されるコンポーネントには、ビューポートのメディアクエリではなく、
container-typeと@containerを使ってください。[joshwcomeau, responsive-layout] - サイドバーの折りたたみは、オフキャンバスまたはアイコンレールにしてください。縮小表示は避けてください。 260px のナビゲーションを 180px に縮めると、使いづらくなります。ツールチップ付きの 64~72px のアイコンレールに折りたたむか、再表示用の常時表示ストリップを残してオフキャンバスにスライドさせます。リサイズや折りたたみの後も、どのペインが表示されていたかを保持し、ユーザーがコンテキストを失わないようにしてください。
- 密度は CSS のズームではなく、レイアウトのモードです。 DENSITY ダイヤルが求める密度プリセットを少なくとも1つ以上、ブラウザーのズームによる代用ではなく、実際の切り替え機能として提供してください(行の高さとパディングのトークンを切り替えます)。[i18n-a11y-density, carbon 5 row heights]
- テキストコンテナーに固定幅を指定してはいけません。 バッジ、ボタン、テーブルヘッダー、チップ、タグは、テキストの折り返しや拡張ができるオートレイアウト(flex/grid、コンテンツに合わせたサイズ)にしてください。固定幅のテキストコンテナーは、実際のデータや翻訳されたテキストで内容が切り取られます(セクション 10.B)。
- 比較する数値には等幅数字を使ってください。 ユーザーが縦方向に比較する数値の列やリストには、数字の桁を揃えるため
font-variant-numeric: tabular-numsを使います。メトリクス列にプロポーショナル数字を使うのは、設計上の不備が見て取れるサインです。
5. オーバーフローとテキストの切り詰めに関する一覧(詳細)
プロダクト UI には、実際の文字列が届きます。90文字のファイルパス、ドイツ語のボタンラベル、1語の顧客名や40語の顧客名などです。テキストの切り詰めは、必要な箇所にだけ使う手段であり、デフォルトではありません。
5.A 切り詰めてはいけない項目(必須)
主要なコンテンツや識別情報は、決して切り詰めないでください。タイトル、エンティティ名、一意の ID、エラーや検証のメッセージ、ページヘッダー、ボタンラベルが該当します。切り詰めるのは、副次的な補足コンテンツ(説明、二次メタデータ、パンくずリストの中間部分)だけです。[carbon overflow, primer truncate] 識別用の文字列が長すぎる場合は、折り返すか、表示スペースを増やすか、両端を残す中央部分の切り詰め(パス/ID 向け)を使ってください。2つのレコードを区別する部分を隠してしまう、末尾の省略記号は使わないでください。
5.B 省略されたすべての文字列には、完全なテキストを表示する手段が必要です
ホバー時にのみ表示されるtitleツールチップは、アクセシブルな代替手段にはなりません。キーボードのみを使うユーザー、タッチ操作のユーザー、音声認識を使うユーザーには対応できないためです。フォーカス時にもホバー時にも全文を表示するか、インラインで展開できるようにするか、詳細ビューに表示してください。[carbon, primer]
5.C 長いボタンラベル(必須。製品 UI の仕上がりを示す重要な指標です)
ボタンラベルは絶対に省略しないでください。 タップターゲットのアクセシビリティを確保するために最小幅を設けたうえで、ボタンの幅は内容に合わせてください(「内容に合わせる」)。ラベルが長い場合や翻訳後に長くなった場合は、切り詰める前に最大 2 行で折り返し、ラベルの文言も見直してください。「保存しまし…」と表示されるボタンは、実装として不適切です。アイコンのみのボタンにはaria-labelとツールチップが必要です。[overflow-truncation, i18n]
5.D 1 行の省略表示のレシピ
overflow:hidden; text-overflow:ellipsis; white-space:nowrap;に加えて、テキストとスクロール/行コンテナの間にあるすべての Flex 祖先要素にmin-width:0を設定してください(セクション 4)。min-width:0の連鎖がなければ、CSS は機能しません。
5.E 複数行のクランプ表示のレシピ(-webkit-line-clamp)
次の 4 つのプロパティはすべて同時に設定しないと機能しません。display:-webkit-box; -webkit-line-clamp:N; -webkit-box-orient:vertical; overflow:hidden; クランプ対象の要素自体にパディングを設定しないでください。 クランプの計算ではパディングが無視され、テキストがはみ出します。パディングが必要な場合は、クランプ対象をパディング付きの親要素で囲むか、高さを固定する方法を代替策として使ってください(line-height * N)。[css-tricks line-clampin]
5.F コンテンツの例外ケース:件数、大きな数値、データ欠損(必須)
実際のデータは 0、1、1,000,000 になる可能性があります。デモの「3」だけでなく、この 3 つすべてに対応するデザインにしてください。
- ゼロ/1/複数(複数形):件数を表すすべての文字列で、0、1、N に正しい文法を使ってください。「0 items」「1 item」「24 items」のように表示し、「1 items」や「24 item」は使わないでください。文字列の連結は使わず、ICU 対応の複数形フォーマッターを使ってください。言語によっては複数形が最大 6 種類あります。通常、0 の場合は空の状態(セクション 6.C)を表示し、通常のレイアウトに「0 items」とだけ表示することはありません。
- 非常に大きい数値/精度が必要な数値:情報量の多い画面では、読み取りやすさのために数値を短縮表示します(1.2k、3.4M、1.1B)。ただし、ホバー時やフォーカス時、詳細ビューでは正確な値を確認できるようにしてください。請求書の合計額や口座残高など、ユーザーが正確な値をもとに操作する必要がある数値は、絶対に短縮しないでください。 ロケールに応じた桁区切り(1,234.56 と 1.234,56)、比較する列での等幅数字(セクション 4)、短縮表示に切り替える際の一貫したしきい値を設定してください。
- データ欠損/null/該当なし(テーブルのセルに限らない一般的なルール):null フィールドには、一貫性のある控えめなプレースホルダー(「未設定」、「-」、「不明」)を表示してください。空文字列やゼロとは視覚的に区別できるようにします。null が「まだ設定されていない」(入力を促す)、「該当しない」(簡潔に説明する)、「権限により非表示」(セクション 6.G)のどれを意味するかをフィールドごとに決めてください。この 3 つにはそれぞれ異なるプレースホルダーを使い、描画の不具合に見える空欄を使わないでください。
- 長くて区切りのないトークン(URL、ハッシュ、ファイルパス、空白のない ID):両端を残す中央省略か、
overflow-wrap:anywhereを使ってください。識別に重要な末尾が隠れる末尾省略は使わず、トークンが列幅を超えないようにしてください。
5.G 列ヘッダーと UI クロームのはみ出し
- 列ヘッダー:2 行に折り返した後、ツールチップにヘッダー全文を表示して省略してください。[carbon]
- パンくずリスト:先頭と末尾を残し、中央部分を省略記号/オーバーフローメニューにまとめてください。[carbon overflow]
- ツールバー/アクションバー/タブ:Priority-plus オーバーフロー(Fluent Overflow)を使ってください。表示優先順位と
minimumVisibleの下限を定義し、残りを「その他」メニューに移動します。DOM 順序は逆にし、最初の項目が優先されるようにしてください。[fluent] - 長い自由形式のコンテンツブロック:展開可能な文章には「続きを読む/折りたたむ」、リストには「さらに読み込む」を使ってください(セクション 7.D)。
6. 状態マトリックス(詳細解説)
データを表示するあらゆる画面には、通常の状態以外にも対応が必要です。ページ、コンポーネント/領域、フィールドの3 つの粒度すべてで、状態の全体像を設計してください。雑な実装でよくある失敗は、通常の状態以外すべてに同じ灰色のボックスを使い回すことです。
6.A 4つの基本状態を常にまとめて設計する
データを読み込む領域では、理想状態をリリースする前に、理想状態/読み込み中/空/エラーを設計してください。[polaris four-states, nngroup]
6.B 読み込み時間のしきい値(必須)
- 1秒未満:何も表示しません。一瞬表示されて消えるスケルトンより、しばらく何も表示しないほうが適切です。
- 1~10秒、ページ全体または領域の読み込み:実際のレイアウト構造を反映したスケルトン(表示後に現れる実際の行やフィールド)を使います。枠だけのグレーのボックスや、構造化されたコンテンツに対するスピナーは使いません。[nngroup skeleton, carbon loading]
- 10秒超、または所要時間が不明な処理(アップロード、ダウンロード、変換、デプロイ):進捗状況がわかるプログレスバーを使います。不確定なスピナーやスケルトンは使いません。数分かかる処理では、ユーザーが画面を離れても完了時に通知されるようにします。[carbon]
- 表示済みデータのバックグラウンド更新:古いコンテンツを表示したままにして、控えめなインラインインジケーターを表示します。更新時にキャッシュ済みのコンテンツを消して、スピナーやスケルトンを表示することは決してしません。
6.C 空の状態は異なる3つの状態として扱う(必須)
以下の状態で、同じ空状態コンポーネントやメッセージを使い回してはいけません。
- まだ一度も使われていない(初回利用) - オンボーディング向けのトーンで、これが何かを説明し、最初のレコードを作成するための主要な CTA を提示します。[primer empty-states, pencilandpaper]
- フィルターまたは検索の結果がゼロ - 「X の検索結果はありません」と表示し、フィルターを解除する操作を提示します。データは存在し、フィルターが原因です。
- ゼロであることが望ましい状態 - 「受信トレイが空です」「アラートはありません」などと表示します。肯定的なトーンにし、罪悪感を与える CTA は使いません。 [carbon empty-states, atlaskit empty-state] 小さなタイルやサイドパネルでは、中央揃えのテキストのみの空状態を、「レイアウト構造を反映する」というルールの例外とします。
6.D エラー状態を空の状態として扱わない
読み込みの失敗は必ず ErrorState とし、EmptyState にしてはいけません。エラー文言は具体的で、平易な言葉を使い、次に取るべき行動がわかる内容にし、サポート用のエラーコードまたは ID を含めます。トーンとレイアウトを一貫させるため、すべてのエラーを共有の ErrorState コンポーネント経由で表示します。「エラーが発生しました」だけの表示はリリースしてはいけません。[polaris error-messages, nngroup]
6.E フィールドとインラインの状態
フィールドにはそれぞれ独自のマイクロ状態があります。非同期検証中(Section 8.D)、インラインエラー、成功確認、保存中/保存済み(自動保存、Section 8.E)です。フィールドレベルの失敗を、何も起きなかったかのように扱ってはいけません。
6.F 部分的な読み込み、古いデータ、オフラインの状態(必ず検討)
「読み込み済み」と「失敗」の間には、実際のネットワークで生じる状態があります。
- 部分的な読み込み/部分的な失敗: 複合ビューの読み込み時にウィジェットの1つが失敗した場合、失敗したそのウィジェットだけをその場でエラー状態(独自のインライン ErrorState と再試行)にします。1つのパネルの失敗でページ全体を空白にしてはいけません。壊れたタイルが1つあるだけでダッシュボード内のすべてのタイルが表示されなくなるのは不具合です。
- 古いデータ/キャッシュ済みデータ/更新中: 最後に正常に取得したデータを表示し、控えめな「更新中」インジケーターと「最終更新日時」の表示を付けます。バックグラウンド更新時にスケルトンを表示するため、再びコンテンツを消してはいけません(Section 6.B)。更新に失敗した場合は古いデータを表示したままにし、「更新できませんでした。 時点のデータを表示しています」という、操作を妨げない通知を表示します。
- オフライン/切断中: 接続の切断を検知し、モーダルではないバナーを継続して表示します。変更操作を無効にするか、キューに登録して、再接続時に反映します。保存できないことに気づかないまま、ユーザーがフォームに入力できる状態にしてはいけません。
- 楽観的更新の契約: サーバーの確認前に変更を表示する場合は、必ずロールバック手段を用意します。書き込みに失敗したら、以前の値に戻し、明確なエラーを表示します。ロールバックのない楽観的更新は、データ整合性上の重大な警告です。
6.G 権限とプランの状態(プロダクト UI のみ。必ず考慮してください)
マーケティング UI にこれらの概念はありませんが、プロダクト UI では重要です。
- 権限拒否/ロール制限:無効化するか非表示にするかを意図的に決めてください。ユーザーが決して利用できないアクションは非表示にし、ロールを変更すれば利用できるアクションは無効化して(ホバー/フォーカス時に理由を表示して)ください。説明のない、機能しないコントロールを表示してはいけません。
- 読み取り専用モード:入力欄を静的な値として表示し、明確に「読み取り専用」とわかる表示を添えて、同じレイアウトを提示してください。壊れているように見える、グレーアウトした入力欄にしてはいけません。
- プラン/ライセンスによるロック:ロックされた機能を示す表示(「アップグレードして利用可能にする」)は、独自のコンポーネントを持つ別個の状態です。表示して説明し、アップグレードへの導線を設けてください。無効化されたボタンにしてはいけません。[ホストアプリに既存の先例があれば、それに合わせてください。]
7. データテーブルの詳細(プロダクト UI で最も難しい領域)
DATA_COMPLEXITY に応じて詳細度を調整してください。シンプルなリストなら 7.A~7.B で十分ですが、エンタープライズ向けグリッドではすべてが必要です。デザインシステム内では、そのシステムの Table とプラグイン/フックを使用してください。以下の仕組みは、それらのフックが満たすべき契約であり、デザインシステムを使わない場合の指針でもあります。
7.A スクロールの管理と固定ヘッダー(必須)
高さに上限を設けた 1 つのラッパーで両方向のスクロール(overflow:auto)を管理します。ページを横スクロールさせてはいけません(セクション 4)。固定ヘッダーは position:sticky; top:0 で実装し、そのラッパーの範囲内に限定してください。[carbon、ant table-scroll]
7.B 固定/ピン留め列(必須の実装方法)
position:sticky +明示的な left:0(または right:0)のオフセット+z-index >= 10(スクロールする本体より上)+ホバー状態と選択行の状態も覆う、不透明な背景色+内側の box-shadow による区切り線。CSS の border は使わないでください(固定スクロール中に境界線が消えたり、二重に描画されたりします)。不透明な背景色がないと、スクロールするコンテンツが固定列から透けて見えます。[ant、mantine-react-table column-pinning、tanstack]
7.C 仮想化(DATA_COMPLEXITY 3)
約 100~200 行を超える場合は仮想化してください。必須の DOM 構造は2 つのラッパーです。外側はビューポートの高さに制約されたスクロール可能なラッパー、内側は仮想コンテンツ全体の高さを持つスペーサーです。<table> 要素自体を仮想コンテンツ全体の高さに設定してはいけません。行の高さが歪み、固定ヘッダーが(短すぎる親要素から抜け出せないため)崩れる原因になります。[tanstack virtual #640]仮想レンダリング中に、行ごとのスタイルオブジェクトや行ごとのメニューインスタンスを追加しないでください(パフォーマンスを著しく損ないます)。メモ化しない場合は columnResizeMode:"onEnd" を使用してください。[ant、mantine]
7.D ページネーション、無限スクロール、追加読み込みの使い分け(判断基準)
- インデックスが約 50 件を超えたらページネーションを使用してください(Polaris の具体的な基準)。また、「特定の項目を見つける/離れた項目を比較する/先頭の数件を確認する」タスクでは、必ずページネーションを使用してください。[polaris、smashingmagazine]
- 無限スクロール:探索型で、項目が同質かつ際限なく続くフィードに限って使用してください。**検索結果や比較タスクには決して使用しないでください。**使用する場合は、戻るボタンでスクロール位置が復元されるように
history.pushState()を実装してください(実装の 90%超がこれを誤っています)。モバイルでは、チャンクをスクロール中に遅延読み込みするのではなく、大きめのフラットなバッチ(15~30 件)を一度に読み込んでください(スクロール中の取得は信頼性に欠けます)。[nngroup infinite-scrolling、baymard] - 追加読み込みボタン:有限だが長いリストに適した安全な中間策です。読み込みはユーザーが操作します。
7.E 選択、一括操作、選択状態の保持
- 行選択では、Shift キーを押しながらのクリックによる範囲選択と、すべて選択をサポートしてください。文言では、「N 件すべてを選択(全ページ)」と「このページのすべてを選択」を区別してください。[polaris]
- 選択状態の保持(エンタープライズでよくある重大な正確性の問題):ページネーション、並べ替え、フィルターの変更後も、選択した行の ID を保持してください。ページが変わったときに選択状態を黙って解除してはいけません。画面遷移後も維持される「N 件を選択中」の件数を表示してください。
- 一括操作の実行中は、競合する変更を防ぐため、行ごとのアクションを無効化してください。
- ツールバーに表示する主要アクションは最大 5 件とし、それを超えるものはメニューにまとめてください。[polaris]
7.F フィルタリング、ファセット、検索(それぞれ別の機能)
- 検索(フリーテキスト)とフィルター(構造化された条件)は別々のコントロールです。ひとつのボックスに暗黙的にまとめないでください。各フィルターコントロールは、データ型(期間、列挙型の複数選択、数値範囲)に合わせてください。[data-table-mechanics; ほとんどのシステムにはツールバー用のフィルターコンポーネントがあります]
- 適用済みフィルターのチップ:アクティブなフィルターを、テーブルの上に削除可能なチップの行として表示し、「すべてクリア」も設けてください。さらに、ファセットが多い場合はファセット用サイドバーも設けます。これは現在の Jira/Linear/Notion の標準的なパターンです。チップの行がないと、ユーザーはデータを絞り込んでいる条件を確認したり、取り消したりできません。
7.G セルの内容
セルには、長さがそろわない実データが入ります。識別情報を含むセルは省き、それ以外のセルはセクション 5 に従って省略表示してください(フォーカスで全文にアクセスできるようにします)。数値列は右揃えにし、tabular-nums を使用してください(セクション 4)。空のセルには、一貫した「未設定」のプレースホルダーを表示してください(em-dash は使用禁止です。「-」または「未設定」を使います)。レンダリングの不具合に見える空欄は使わないでください。
8. フォーム、バリデーション、マルチステップフロー(詳細)
8.A ラベルの配置
デフォルトではラベルを上揃えにします(最も素早く確認でき、ローカライズやモバイルに最適です)。右揃えのラベルは、縦方向のスペースが限られる高密度なエンタープライズフォームに限ります。左揃えにするのは、慎重さが必要なタスクで、意図的にユーザーのペースを落としたい場合に限ります。ローカライズされた製品でラベルを右揃えにしてはいけません(文言の拡張で列の配置が崩れます)。[lukew web-form, nngroup web-form-design]
8.B バリデーションのタイミング
未完成の値に対して、キー入力のたびに検証するのではなく、フォーカスが外れたときと送信時に検証してください(最初の文字を入力した時点で「メールアドレスが無効です」と表示しないでください)。例外は、パスワード強度のような、リアルタイムで肯定的な補助情報を表示する場合です。フィールドの横にインラインエラーを表示し、さらに送信時にはフォーム上部にエラーの概要を表示してください。どちらか一方だけにしてはいけません。[nngroup errors-forms, polaris inline-error]
8.C 送信ボタンを唯一の通知手段として無効化しない(必須)
5 つ以上のフィールドがあるフォームでは、無効化された送信ボタンだけでフィードバックしてはいけません。ユーザーは何が不足しているのかわからないためです。送信できるようにし、その後、エラーの合計数を表示して最初のエラーにフォーカスしてください。送信ボタンを無効化してよいのは、簡単な(フィールド数が 3 未満の)フォームに限ります。送信に失敗しても、ユーザーの入力は常に保持してください。フォームをクリアしてはいけません。[forms-flows, nngroup]
8.D 非同期/リモートのフィールド検証
デバウンスを適用し、さらにAbortSignal で古いリクエストをキャンセルしてください。キャンセルされていない遅いレスポンスが、新しく正しい結果を上書きする可能性があります(ユーザー名の利用可能性確認で発生する競合)。[forms-flows]
8.E 自動保存(影響度で判断)
- 自動保存は、フォーム全体をキー入力のたびに保存するのではなく、論理的なフィールドグループごとに行ってください。
- パスワード、公開範囲/権限、機密情報、金銭的影響のあるフィールドは自動保存しないでください。明示的な手動確認が必要です(影響度 3)。
- フォーカスが外れたときと、最後のキー入力から約 3 秒後に保存を実行してください。「保存中...」の後に「<相対時間>に保存済み」と表示します。
- 自動保存に失敗した場合は、再試行できる、自動で消えないトーストを表示してください。失敗を通知せずに済ませてはいけません。
8.F マルチステップフロー/ウィザード
- ステップインジケーターは実際のステップを示し、完了したステップはクリックして戻れるようにしてください。実際のステップ名を使い、「ステージ 1/ステージ 2」のような実態のないラベルは使わないでください。[taste-skill tell parity]
- フィールド数を減らすことでウィザードの途中離脱を防いでください。ステップを増やす方法では解決しないでください。フローの途中でアップセルを挿入してはいけません。[baymard checkout-linear]
- フローは直線的に保ってください。分岐のあるウィザードには、全体を見渡せる図を表示する必要があります。
8.G 破壊的/取り消せない操作の確認(影響度 3、必須)
削除/デプロイ/課金の確認ダイアログでは、キーボードフォーカスの初期位置を破壊的な操作ではなく、安全な操作(キャンセル)にしてください。role="alertdialog"、aria-modal="true"、および警告文を指す aria-describedby を使用してください。影響の大きい削除では、破壊的なボタンを有効にするためにリソース名の入力を必須にしてください。閉じたときは、ダイアログを開いた要素(または次に論理的な要素)にフォーカスを戻してください。[w3 alertdialog, radix dialog]
9. ナビゲーション、オーバーレイ、レイヤリング(詳細)
9.A ダイアログ/モーダル
開いている間はフォーカストラップを有効にし、閉じたときはフォーカスを元に戻し、Escape キーで閉じます。破壊的な操作ではない場合に限り、オーバーレイのクリックで閉じます。Dialog.Title と Dialog.Description は、視覚的に非表示の場合でも決して省略しないでください(スクリーンリーダーが読み上げます)。[radix dialog, w3 dialog-modal]
9.B トースト/通知
デフォルトは約5000msとし、ホバー時とフォーカス時は一時停止します。スワイプで閉じるしきい値は約50pxとし、手動でも閉じられるようにします。スクリーンリーダーでの優先度を考慮し、フォアグラウンドの通知(ユーザー自身の操作が成功したこと)とバックグラウンドの通知(何かが変更されたこと)を区別します。破壊的な操作の確認をトースト内に含めてはいけません。[radix toast]
9.C メニュー、ドロップダウン、タブ、コンボボックス
キーボード操作の仕様を完全に実装します。矢印キーでの移動、Home/End、入力文字による検索、ロービング tabindex(Section 10.D)に対応します。Tab キーは複合ウィジェット間の移動に使い、矢印キーはウィジェット内の移動に使います。キーボードでフォーカス可能な状態を維持するメニュー項目/タブ項目には、ネイティブの disabled(タブ順から除外されます)ではなく aria-disabled を使います。[radix, w3 grid]
9.D z-index/スタッキングコンテキストのシステム(必須。場当たり的な z-index を置き換えます)
- 階層ごとに大きな数値の間隔を設け、トークンとして一度だけ定義します。例:base 0、sticky 100、dropdown 1000、sticky-header 1050、overlay/drawer 1100、modal 1200、popover 1300、toast 1400、tooltip 1500。z-index を単独で場当たり的に調整してはいけません。変更する場合は、スケール全体との整合性を取ります。場当たり的な
9999/99999は警告サインです。 - レイヤーの問題をデバッグする前に、スタッキングコンテキストを生成する要因をすべて列挙します。
position:fixed/stickyと z-index の組み合わせ、opacity < 1、コンテキストプロパティを指定するtransform/filter/will-change、contain、container-type、isolation:isolate、トップレイヤー要素が該当します。[mdn stacking-context] - ネストされたスタッキングコンテキストは一体として扱われます。深くネストされた要素の z-index をいくら大きくしても、浅い階層にある祖先の兄弟要素を上回ることはできません。メニューがヘッダーの背後に隠れる場合は、数値を大きくするのではなく、祖先のコンテキストを修正します。
- オーバーレイのレイヤー順は、種類ごとの固定値ではなく、開いた順序で決まります。最後に開いたオーバーレイが最前面になります。利用できる場合は、レイヤーマネージャー/プラットフォームのトップレイヤー(
<dialog>、Popover API)を使用します。
10. 密度、I18N、アクセシビリティ(詳細)
10.A 第一級のモードとしての密度
密度プリセットを3種類以上(comfortable/standard/compact)用意し、縮小表示ではなく、行の高さとパディングのトークンを切り替える実際の設定として提供します。[i18n-a11y-density] Carbon には5種類の行の高さがあります。DENSITY ダイヤルの要件に合うセットを選んでください。
10.B 国際化におけるテキスト拡張の余裕(必須)
英語は UI 言語の中でも特に短いため、翻訳に備えてコンテナーに余裕を持たせて設計します。
- ドイツ語:通常は**+30~40%**、最悪の場合は約70%まで。
- フランス語/ロシア語/スペイン語:+20~25%。
- フィンランド語:最大+100%(2倍)。
- CJK:拡張ではなく、短くなります。 固定幅のテキストコンテナーをハードコードしてはいけません(Section 4)。テキストが切り詰められるのではなく折り返されるよう、flex/grid を使用します。疑似ローカライズ(文字列の前後にアクセント付きの余白を追加する方法)は、拡張による問題の約80%を検出できる、手軽なスモークテストです。[crowdin, i18n-a11y-density]
10.C RTL
方向を示すアイコン(戻る/進むの矢印、シェブロン、進行方向)のみを反転します。時計、再生、チェックマーク、ロゴなどの普遍的なアイコンは決して反転しません。RTL テキスト内でも、数字は LTR の字形で表示します。主要なアクションボタンは左側に移動します(RTL の読み進め方では自然な終点です)。プログレスバーは右から左に向かって塗りつぶします。[crowdin]
10.D ARIA グリッドのキーボード操作規約(フォーカス可能なセルを持つカスタムグリッド/テーブルでは必須)
- ロービング tabindex:
tabindex="0"のセルは1つだけにし、ほかのすべてのセルは-1にします。グリッドコンテナー自体はタブストップにしません。 - 矢印キーでセル間を移動します。端では移動を止め(行や列の端で折り返しません)。
- Home/End で現在の行内を移動し、Ctrl+Home / Ctrl+End でグリッドの最初/最後のセルに移動します。
- Page Up/Down はスクロール可能なグリッドでのみ使用し(約5行分移動します)。 [w3 grid data-grids] Carbon、Fluent、Polaris、Ant などのデザインシステムの Table を使用する場合、この動作は処理されるため、独自に実装しないでください。グリッドを一から構築する場合に限り、この規約に従ってください。
10.E あらゆる場面で守るべきアクセシビリティの最低基準
- タッチターゲットは44~48px以上(タッチ操作)、24px以上(高密度のデスクトップ表示)にします。[material3 accessibility]
:focus-visibleを使用し、マウスクリック時に表示されるだけの:focusは使いません。置き換えずにフォーカスのアウトラインを削除することも禁止です。- アイコンのみのコントロールにはすべて
aria-labelを設定します。 - 数値、日付、通貨はロケールに応じて書式設定します。
01/02/2025のような曖昧な表記は使用しません。
11. オンボーディングと段階的な情報開示
情報量の多い製品は初回利用時にユーザーを圧倒します。強力な機能は段階的に見せ、複雑さは必要に応じて加えてください。
段階的な情報開示(基本の仕組み):
- 高度なコントロール(一括編集、列管理、保存済みビュー、API/Webhook の設定)は、1回の操作でアクセスできる場所(「その他」、展開パネル、設定ポップオーバーなど)に置き、一度にすべてを画面に表示しないでください。Linear の原則は、シンプルに使い始められ、使い込むほど強力になることです。[linear]
- 目安:1つの領域にコントロールが7つを超えたら、情報開示を検討します。 主な操作3~5個をインラインでまとめ、残りはオーバーフローに格納します。高度な設定はラベル付きの展開パネルに配置し、ツールチップのない正体不明の歯車アイコンに隠してはいけません。
- 情報開示は画面ごとに行い、全体に一律適用しないでください。ユーザーがつい2クリック前に使ったコントロールを隠してはいけません。
初回利用時(必須):
- 初回利用時には、空のグリッドやスピナーではなく、「未使用」の空状態(セクション6.C.1)を表示します。この画面の説明を1行で示し、10個のメニューではなく、最初に行う1つの操作(「最初のプロジェクトを作成」など)を案内します。
- サンプル/デモデータを用意する場合は、それと明確に分かるラベルを付け、ワンクリックで削除できるようにしてください。実データと区別できない、削除できない「Acme Inc」の行を残してはいけません。
コーチマーク、ツアー、チェックリスト(スキップ可能でない場合は禁止):
- コーチマーク、製品ツアー、オンボーディングチェックリストは、手順1から必ずスキップでき、いつでも完全に閉じられるようにしなければなりません。閉じたことはユーザーごとにセッションをまたいで保持し、一度閉じたツアーを再表示してはいけません。スキップできないツアーや、閉じる手段のないチェックリストは Telltale です。
- 同時に実行できるツアーは1つまでとし、モーダルの上にコーチマークを重ねてはいけません。
- ツアーでは実際のUIにある本物のコントロールを示してください。スクリーンショットを偽装してハイライト表示してはいけません。終了時には行き止まりではなく、ユーザーが使える状態に戻してください。
- 対象を分けてください。再訪ユーザー/経験豊富なユーザーや招待されたチームメイトは初回利用フローをスキップできるようにし、既存のデータが入ったワークスペースに参加する人にオンボーディングをやり直させてはいけません。
空の状態から価値を生むまでの経路: すべての空状態の CTA は、3ステップ以内で実際に価値を生み出す場所へ誘導します。ドキュメントへのリンクだけを並べたオンボーディングチェックリストは、飾りにすぎません。
12. コンテキストに応じた先回り(認識すべき標準スケルトン)
製品の説明が以下のいずれかに当てはまる場合は、一部分ではなくスケルトン全体を組み立てます。
- インデックス/テーブルページ:見出し + ツールバー(検索、フィルター、主要アクション、表示密度) + 適用済みフィルターのチップ + テーブル(固定ヘッダー、選択機能) + ページネーションフッター + 3 種類の空状態 + エラー状態。
- リスト詳細/マスター詳細:リスト領域 + 約 1024px 未満でオーバーレイ表示するインスペクターの LayoutPanel + 「何も選択されていません」という空状態。
- 設定ページ:セクションナビゲーション + FormLayout パネル + 自動保存または保存バー + 危険な操作の領域のみを囲む Card。
- 複数ステップのウィザード:実際に機能するステッパー + 順次進むステップ + ステップごとの検証 + 確認ステップ + 破壊的でない終了方法。
- ダッシュボード/概要:KPI タイルの行(Cards) + ウィジェットのグリッド + 詳細に移動できる Table(行)を 1 つまたは 2 つ。
- 一括操作に対応する管理コンソール:テーブル + 選択状態の保持 + 一括操作バー(最大 5 項目 + オーバーフロー) + 確認ダイアログ。
コンポーネントの完全な実装はホストシステムにあり、ここには複製されていません。ホストシステムのテンプレート/スケルトンコマンド、またはドキュメント(references/canonical-sources.md)から参照してください。特に価値の高い 3 つのスケルトンは references/block-skeletons.md にまとめられています。これは、誤りが最も起きやすい仕様を定義したものです。標準的な形として扱い、ホストシステムに合わせて調整してください。
13. パフォーマンスとアクセシビリティのガードレール
- テーブルの読み込み/フィルター適用への応答は瞬時に感じられるようにします。約 1 秒を超える場合は楽観的なフィードバックまたは読み込み中のフィードバックを表示し、6.B のしきい値を超えた場合は対応を強化します。
- エンタープライズグリッドのパフォーマンス:レンダリング時に行ごとのスタイル/メニューオブジェクトを割り当てないこと、スコープを限定して自動サイズ調整すること、メモ化していない場合は
columnResizeMode:"onEnd"を使用すること、約 100~200 行を超えたら仮想化すること。 - 製品 UI のモーションはフィードバックのみにします。状態遷移、フォーカスの移動、行の出入りなどが対象です。装飾的なスクロールアニメーション、マーキー、視差効果は使用しません。すべての動きで
prefers-reduced-motionを尊重してください。 - Core Web Vitals も引き続き適用されます。CLS < 0.1(スケルトンは実際のコンテンツと同じ寸法を確保し、コンテンツが跳ねないようにする必要があります)、操作時の INP < 200ms。
14. ダークモード
トークン駆動とし、アプリごとにテーマを 1 つ固定します(taste-skill Section 8/11 と同じ方針で、アプリの途中で領域ごとに反転させません)。ホストシステムのセマンティックレイヤートークン(var(--color-*)、Carbon $layer/$border-subtle など)を使用し、グレーをハードコードしないでください。両モードでテーブル、固定列(単色背景には必ずテーマトークンを使用しないと、ダークモードで背景が透けます)、オーバーレイ、フォーカスリングをテストしてください。テーマはシェルのルートで一度だけ設定し、セクションごとには設定しないでください。
15. プロダクト UI における AI らしさの兆候(禁止パターン)
taste-skill のランディングページにおける兆候とは異なります。これらは、モデルがプロダクト画面を装っているときに現れる特徴です。要件で本当に必要とされる場合を除き、厳禁です。
- カードの氾濫:すべてのレコードを Card + Badge で囲みます。行を使ってください。(Section 0.C)
- ページを横スクロールさせる固定幅の
<table>:スクロールを独占するラッパーは使わないでください。(Section 4) - 切り詰められたボタンラベル(「Save chang...」)。コンテンツに合わせてサイズを調整してください。(Section 5.C)
- 識別に必要なコンテンツ(名前/ID/エラー)の切り詰めと、ホバー時にしか表示されない
title。(Section 5.A-B) - 5 個以上のフィールドがあるフォームで、検証方法が
disabledの Submit しかない。(Section 8.C) - 初回起動、フィルター結果が空の場合、読み込み失敗で、同じ汎用の空状態を使い回す。(Section 6.C-D)
- 原因、対処方法、コードのない 「エラーが発生しました」。(Section 6.D)
- 解消されないスケルトン、または実際のレイアウトを反映しない枠だけの灰色のボックス。(Section 6.B)
- キャッシュ済みのデータをバックグラウンドで更新する際に、ページ全体のスピナーを表示する(ユーザーが読んでいたデータを再び消してしまう)。(Section 6.B)
- 透明な背景の固定/フリーズ列(スクロール時に背後のコンテンツが透けて見える)。(Section 7.B)
- 段階的なトークンスケールではなく、場当たり的な z-index(
9999、99999)を使う。(Section 9.D) - ドイツ語/フィンランド語で文字列が長くなると切れてしまう 固定幅のバッジ/ラベル/タブ。(Sections 4, 10.B)
- キーボードでフォーカス可能なままにすべきメニュー/タブ項目に、ネイティブの
disabledを使う(aria-disabledを使用してください)。(Section 9.C) - 装飾としてのバッジ、および すべての行にステータスドットを乱用すること(ステータス表示に限り、控えめに使用してください)。
- 業務ツールにおける 見せるためだけのモーション(装飾的なスクロール/ホバーアニメーション)。(Section 13)
- 偽のステップラベル(「Stage 1 / Stage 2」)、偽の精密な数値、リリース画面での「Jane Doe」/「Acme」のようなプレースホルダーデータ。
- **画面に見える場所でのエムダッシュ(
—)の使用。**ヘッダー、セル、ラベル、空状態/エラーの文言、ツールチップ、「未設定」を示すセルのプレースホルダーでは使用禁止です。ハイフン(-)を使ってください。これは最も頻繁に違反される兆候です。一切許容されません。(taste-skill Section 9.G、社内ルール)
16. 参考用語集(エージェントが知っておくべき名称)
アプリシェル/PageLayout(ヘッダー/コンテンツ/ペイン/サイドバー/フッター) · 標準レイアウト(リスト詳細/補助ペイン/フィード) · マスター詳細 + インスペクター · IndexTable/リソースリスト/DataGrid/Table · フリーズ(ピン留め)列 · 仮想化 · ロービング tabindex · スタッキングコンテキスト · コンテナークエリー · スケルトンスクリーン(形状の反映) · Blankslate/EmptyState/ErrorState · オーバーフロー/プライオリティプラス · 適用済みフィルターチップ/ファセット検索 · alertdialog · 楽観的更新と悲観的更新 · 密度モード · 疑似ローカライズ · コマンドパレット · ワークスペース切り替え · ジョブ/ステータストレイ。
17. 再設計プロトコル(既存の高密度画面の監査)
ビジュアルスタイルに手を加える前に、見た目よりも重要な正確性に関する不具合を監査してください。
- スクロールの担当範囲の違反(ページが横スクロールする、二重の入れ子スクロール)。
- 主要コンテンツの切り詰め、およびホバー時にしか表示されない代替表示。
- 状態の区別が不足している(3 種類の状態に 1 つの空ボックスを使う、エラー状態がない、スケルトンがない)。
- フリーズ列の透け、および透明な固定背景。
- z-index の場当たり的な指定、および入れ子のスタッキングコンテキストによるレイヤリングの不具合。
- i18n 用の余裕がない、および固定幅のテキストコンテナー。
- キーボード操作/アクセシビリティの不足:ロービング tabindex がない、素の
:focus、ラベルのないアイコンボタン、唯一のシグナルが Submit の無効化。
まずこれらを修正してください。その後で、密度/トークン/ビジュアルの調整を行ってください。ホストのデザインシステムを維持し、「再設計」として Carbon を shadcn に置き換えないでください。
18. taste-skill との使い分け + 対象外
taste-skillに引き継ぐ:マーケティング用のヒーローセクション、ランディングページ、ポートフォリオ、エディトリアル、イラストを多用したブランド向けの画面。画面の半分がプロダクト、半分がマーケティングの場合は分けてください。マーケティング部分には taste-skill を、プロダクト画面にはこのスキルを使います。同じコンポーネントに両方を適用しないでください。datavizに引き継ぐ:チャートやグラフの作成(色、エンコーディング、軸)。このスキルの対象は、チャートそのものではなく、テーブルとチャートを囲むダッシュボードのフレームです。- 対象外:レスポンシブウェブの範囲を超えるネイティブモバイル(iOS/Android)アプリのクローム(タブバー、ネイティブナビゲーション)は、別のスキルの役割です。黙って近似せず、その旨を伝えてください。リアルタイム共同作業のプリミティブ(プレゼンス、カーソル、OT)は、別の問題領域です。
19. 最終リリース前チェック
すべての項目を確認してください。1つでも満たしていなければ、画面は完成していません。
- プロダクトの読み取りを明示しましたか(Section 0.B の一文)?
- 各種設定値(DENSITY / DATA_COMPLEXITY / CONSEQUENCE)は、黙ってデフォルト値にせず、読み取り内容に基づいて設定しましたか?
- フレームはコンテンツを入れる前に px 単位で予算を決め、シェルとランドマーク領域に名前を付けましたか(Header/Content/Pane/Sidebar を混同していませんか)?
- 密度の高いデータにはカードではなく行を使っていますか。Card はウィジェットに限られていますか。カードの乱用やカードの入れ子はありませんか?
- スクロールの所有権:各軸のラッパーはそれぞれ1つだけですか。ページは横スクロールせず、sticky はラッパーの範囲内に限定されていますか?
- 切り詰められるすべての flex テキスト子要素に min-width:0 を指定しましたか?
- 主要なコンテンツ(名前/ID/エラー/ヘッダー)を切り詰めておらず、ボタンのラベルも切り詰めていませんか。切り詰める場合はすべて、フォーカスで全文を確認できる代替手段がありますか?
- 行数制限の設定はすべてそろっていますか(4つのプロパティすべてを設定し、制限対象ノードに padding を指定していませんか)?
- すべてのデータ領域について、4つの状態(理想/読み込み中/空/エラー)を設計しましたか?
- 読み込みのしきい値:1秒未満は何も表示せず、スケルトンはレイアウトを反映し、10秒を超える場合は進捗を明示し、キャッシュの更新時に表示を消して再表示していませんか?
- 異なる3つの空状態(初回利用/フィルター結果なし/ゼロが成功)を用意しましたか。読み込み失敗は原因・アクション・コードを含む ErrorState になっていますか?
- 部分的な失敗/古いデータ/オフライン状態に対応していますか(1つのウィジェットの失敗はその場だけに限定し、キャッシュの更新時に表示を消して再表示せず、オフライン時は通知したうえでキューに追加または処理をブロックし、楽観的更新にはロールバック手段がありますか)?
- コンテンツのエッジケース:件数は複数形を正しく使っていますか(0/1/N、「1 items」のような表記はありませんか)。大きな数値は省略形にしても正確な値を確認でき、精度が重要な場面では省略していませんか。データがない場合は、ゼロや空とは区別できる一貫したプレースホルダーを使っていますか?
- ロールやティアがある場合、権限/プランの状態(無効化と非表示の使い分け、読み取り専用、プランによるロック)を考慮しましたか?
- テーブル:スクロールを所有するラッパーと高さの上限を設けていますか。ヘッダーは sticky ですか。固定列にはテーマトークンの単色背景、inset-shadow の区切り線、z-index を指定していますか。仮想化には2つのラッパーを使う DOM を採用していますか?
- ページネーションと無限スクロールを適切に選びましたか(50件を超える場合や検索にはページネーションを使い、無限スクロールは pushState とモバイル向けのフラットなバッチ処理を備えたフィードに限っていますか)?
- 選択状態はページ切り替え/並べ替え/フィルタリングをまたいで維持されますか。一括操作ツールバーは5項目までに制限し、超過分はオーバーフローにまとめていますか?
- フィルターがある場合、適用済みフィルターのチップを表示する行がありますか。検索とフィルターは別々のコントロールですか?
- フォーム:デフォルトで上部にラベルを配置し、フォーカスを外したときと送信時に検証し、フィールド内と要約の両方にエラーを表示していますか。送信失敗時に入力内容を保持し、5項目以上のフォームで送信ボタンの無効化だけを唯一の通知手段にしていませんか?
- 非同期検証はデバウンスし、AbortSignal でキャンセルしていますか?
- 自動保存はグループ単位で行い、パスワード/権限/金銭に関するフィールドには適用せず、Saving/Saved の状態を表示し、失敗時は再試行の案内を表示し続けていますか?
- 破壊的操作の確認ダイアログでは安全なアクションにフォーカスし、
role=alertdialogを指定し、閉じたときにフォーカスを戻しますか? - ダイアログはフォーカスをトラップして元の位置に戻し、Escape キーで閉じ、Title/Description を備えていますか。トーストはホバー中に表示時間を一時停止し、閉じられるようになっていますか?
- z-indexは段階的なトークンスケールで管理していますか(9999 は使っていませんか)。レイヤーの問題は値を大きくするのではなく、スタッキングコンテキストを追跡して解決していますか?
- i18n 用の余白をすべてのテキストコンテナに確保していますか(ドイツ語で +30〜40%)。テキスト幅を固定値でハードコードしていませんか。比較する数値には tabular-nums を使っていますか?
- RTL 対応が対象範囲の場合、方向を示すアイコンだけを反転していますか?
- キーボード/アクセシビリティ:カスタムグリッドに roving tabindex を設定し、
:focus-visibleを使い、アイコンボタンにラベルを付け、キーボード操作でアクセスできるメニュー項目にはdisabledではなくaria-disabledを使い、ターゲットサイズを 44px/24px にしていますか? - DENSITY の設定に応じて、密度の切り替えを用意していますか(ズームではなくモードの切り替え)?
- ダークモード:テーマは1つに固定し、固定列の背景にテーマトークンを使い、両方のモードでテストしましたか?
- 共通ルール:実際のドキュメントから props を確認し(何も独自に作っていませんか)、レイアウトに
<div>を使わず、トークンのみを使用し、Badge は件数のみに使い、状態はステータスインジケーターで示していますか? - Section 15 のプロダクト UI らしさの露呈がなく、表示される場所に em ダッシュを一切使っていませんか?
- アプリごとにデザインシステムを1つに統一していますか(Carbon と Fluent を混在させていませんか)?
1つでも正直にチェックできない項目があれば、納品前に修正してください。
参考資料
references/block-skeletons.md- 間違いやすい3つのブロック契約(インデックス+詳細ドロワーのフレーム、テーブルのスクロール/固定ヘッダー/固定列のCSS契約、省略表示の契約)を実装したリファレンス。references/install-commands.md- 付録A:ホストのデザインシステム別のパッケージマネージャー用インストールコマンド。references/canonical-sources.md- 付録B:このスキルのルールの根拠となる、gsearchで検証済みの一次ソースURLをトピック別に分類したもの。