12 KiB
Designing Workspace UI
配布物/workspace-ui-kit で UI を編集する際は、以下の2軸を必ず両方適用する:
- shadcn idiom に従ってコード生成する — base UI / semantic token / 親管理の余白
- SSoT を経由しないデザイン変更を拒否する — トークン・部品・パターン・情報設計の不足は4分岐で診断し、ユーザーに確認する
CLAUDE.md の「コード生成ルール」を常時適用の禁止事項として参照する。本スキルはその詳細根拠とプロジェクト固有規律を提供する。
依存
このスキルが参照する SSoT:
app/globals.css— デザイントークン(@theme内の CSS 変数)components.json— shadcn 設定(base / style / iconLibrary / alias。ファイルを読んで確認する)components/ui/— shadcn 素体(編集可能。方針は後述)components/primitives/— プロジェクト固有プリミティブ(lsして確認する。名前はハードコードしない)openspec/decision/— ADR(ペイン責務・配色・角丸等。番号はハードコードせずlsで最新を確認する)
前提: shadcn スキル(.claude/skills/shadcn/)がインストール済みであること。ただしこのスキルから shadcn スキルを読みに行く指示は出さない。必要なルールは自己完結で持つ。
コード生成ルール
CLAUDE.md の禁止事項の根拠と、このプロジェクトでの適用方法を示す。
中心の禁止事項
| 禁止 | 正しい方法 | なぜ |
|---|---|---|
space-y-* / space-x-* |
flex flex-col gap-* / flex gap-* |
子要素が条件で消えたとき余白が崩れる。親が管理すれば子が何個でも安定 |
className で色・フォントサイズ・フォントウェイトを上書き |
variant / semantic token / CSS 変数 | コピペが蔓延し、ダークモードで対応漏れが起きる。部品側にサイズ展開を用意する |
生の色クラス(bg-blue-500 等) |
bg-primary 等の semantic token |
テーマ変更時に全ファイル書き換えになる。名前で指定すれば定義 1箇所で済む |
w-N h-N(正方形時) |
size-N |
「正方形にしたい」意図が明確になる |
asChild(radix の API) |
render(base の API) |
このプロジェクトは base を使用(components.json で確認)。radix の書き方は動かない |
| shadcn 部品がある場面で自前の div | Badge / Separator / Skeleton 等 |
a11y・ダークモード・テーマ連動が組み込み済み。自作すると全部欠落する |
このプロジェクトで最も誤りやすいパターン(必ず暗記):
// Incorrect — radix の書き方は base では動かない
<DialogTrigger asChild>
<Button variant="outline">設定を開く</Button>
</DialogTrigger>
// Correct — base の API
<DialogTrigger render={<Button variant="outline" />}>
設定を開く
</DialogTrigger>
その他のパターン(スペーシング・タイポグラフィ・セマンティックカラー・アイコン・コンポジション・フォーム)は references/coding-rules.md を参照。
components/ui/ の編集方針
shadcn の Open Code 思想に基づき、components/ui/ はプロジェクトのコードとして編集してよい。ただし方法に規律がある。
OK:
- 部品に新しい variant を追加する(例:
CardTitleにdata-size="sm"を追加) - 部品の間隔やスタイルを構造的に変更する
- 新しい CSS 変数を
globals.cssに定義して部品から参照する
NG:
- 呼び出し側で
classNameを使って見た目を毎回打ち消す - 部品ファイルを丸ごとコピーして別名で作る
部品を編集した後、公式の更新を取り込みたいときは npx shadcn@latest add <component> --diff <file> で差分を確認してマージする。
出力後セルフレビュー
コードを出力した後、以下の 3点をセルフレビューし、違反があれば修正版を出す:
- shadcn の書き方: 間隔は親管理(
gap-*)か? 色は名前指定(semantic token)か? 部品の見た目をclassNameで上書きしていないか? - base 準拠:
asChildではなくrenderを使っているか? - プロジェクトトークン:
globals.cssの既存トークンで表現できるか?
SSoT エスカレーション規律
このプロジェクトは教材。受講生が「自分の思想を画面にする」ことがゴール。AI が独断でデザインを決めると受講生の学習機会を奪う。
SSoT を経由しない UI 変更は禁止。 既存 SSoT で作れないものが出てきたら、以下の 4分岐で診断し、必ずユーザーに聞く。
作業前の確認(必ず実行)
以下のチェックリストを応答にそのままコピーし、各項目を完了したら [x] に置き換えながら進む。スキップは禁止。
SSoT 把握:
- [ ] app/globals.css を読み、既存の semantic token を列挙した
- [ ] components/ui/ を ls し、利用可能な shadcn 部品を列挙した
- [ ] components/primitives/ を ls し、Inline* 等のプロジェクト固有部品を列挙した
- [ ] openspec/decision/ を ls し、関連する ADR を読了した
このチェックが全て埋まる前にコード生成を開始してはならない。
4 分岐の決定木
既存 SSoT で作れる → そのまま実装。作れない → 以下に進む。
いずれの分岐でも、ユーザーの判断なしに実装に進んではならない。
3a. トークンの穴
色・余白・角丸・影・フォント等の値が既存トークンで足りない。
やること: 何が足りないかを 1行で説明 → @theme への追加案を提示 → ユーザーの判断を仰ぐ
やってはいけないこと: 生の色クラスの使用 / 「一時的に hardcoded で後で直す」式の仮対応 / ユーザーに聞かず @theme を変更
3b. 部品の穴
既存部品で表現できない形・variant が必要。
やること: 既存部品の variant / size で代替できないか先に試す → 不可能なら新 variant または新プリミティブを起案 → ユーザーの判断を仰ぐ
やってはいけないこと: 部品ファイルを fork コピー / 呼び出し側で className の上書きで済ませる
3c. パターンの穴
レイアウトパターン・複数部品の組み合わせ規則・状態(empty / error / loading)の規律がない。
やること: 既存 ADR を読み、本当に規律がないことを確認 → 規律案を起案 → ユーザーの判断を仰ぐ
やってはいけないこと: 「とりあえず置いてみる」で恒久パターンを既成事実化
3d. 情報設計の穴
ペインに何を載せるか・並び順・情報の増減の規律がない。
やること: コードを書かない。何を載せるか・優先順位・並び順を質問する → ユーザーが決めてから実装
やってはいけないこと: 「採用管理ならこれが普通だろう」と独断で情報を足す / 削る / 並び替える
エスカレーションのテンプレート
[診断]
何が足りない: <1 行説明>
分岐: 3a / 3b / 3c / 3d のどれか
[根拠]
Refactoring UI の <軸> / ADR の <規律> / shadcn idiom 上の <理由>
[提案]
案1: <内容>(メリット / デメリット)
案2: <内容>(メリット / デメリット)
[ユーザーへの質問]
どちらで進めますか?別案ありますか?
評価軸(Refactoring UI)
エスカレーション時の根拠に使う:
- Hierarchy(階層)/ Spacing(余白)/ Typography(タイポ)/ Color(色)/ Depth(影)/ Polish(空状態・エラー状態・ローディング状態・微調整)
ペイン編集導線
Pane 3 と Pane 4 の責務分離
「Pane 3 = 読む場所、Pane 4 = 編集の本拠地」。詳細は openspec/decision/ 配下の最新 ADR を確認する。
- Pane 3 のカードに inline 編集 UI(input・textarea・編集ボタン)を新たに置かない
- 編集機能を足したい時は Pane 4 側に置く
- 「Pane 3 にも編集を持たせたい」と要望が来たら、まず原則との衝突を伝え、本当に必要かを問い直す
インライン編集プリミティブの再利用
components/primitives/ の既存プリミティブを再利用する(ls して名前を確認すること)。新しい編集 UI のスタイル流派を独自に増やさない。
導入禁止の編集 UI: 鉛筆アイコン式 / 「編集」ボタン式 / フォーム・モーダル一括編集式
入力保存規約
- 1 行
input: Enter で保存 / Esc でキャンセル / blur で保存 - 複数行
textarea: Cmd+Enter で保存(Enter は改行)/ Esc でキャンセル / blur で保存 - ホバー時のみ薄い枠を出す。常時の枠は出さない
担当の用語分離
候補者単位とステージ単位で用語を使い分ける。混ぜない。
- 候補者単位: 「採用担当」
- ステージ単位: 「面接官」(書類選考は「審査担当」)
角丸(border-radius)の階層ルール
rounded-* クラスの選択に迷ったとき、以下の 4 ルールで判断する。役割 × R クラスの対応表は openspec/changes/add-4pane-workspace-template/mockup-guide.md の角丸セクションを参照。
ルール 1: 親 R ≧ 子 R
親の箱に当てた R より大きな R を、その中の要素に付けない。
ルール 2: 同格の島は同じ R
bg-card で囲った「島」は原則 rounded-lg に揃える。Card 本体だけは rounded-xl の例外。
ルール 3: 厳密な同心までは求めない
Tailwind のスケール(xl/lg/md/sm)で段差が視覚的に分かれば十分。
ルール 4: 高密度 UI での例外
行密度を上げるために bg-card の島を rounded-md に下げてよい。ただし子要素には rounded-md より大きな R を付けない(ルール 1 を維持)。
React 19 ルール
Effect 内同期 setState 禁止
useEffect の中で同期的に setState を呼んで初期値を入れ直すパターンは禁止。
代替パターン:
- 親側で
keyを変更して子コンポーネントを再マウントし、useState(initialValue)で初期化する - フォーカス + 全選択は
autoFocus+onFocus={(e) => e.target.select()}で実現する