mcpField
U
ui-design

ui-design

github.com/ckorhonenデザイン・UI2470 回インストール5 週間前に更新
リポジトリ

ui-design について

Tailwind CSS を使った画面の構築やレビュー向けに、エージェントへ設計指針を与えるスキルです。アクセシビリティ、操作状態、アニメーション、タイポグラフィ、レイアウトに関する制約をまとめています。

主な機能

  1. 01Tailwind CSS のデフォルト値を優先
  2. 02操作性とアクセシビリティの要件
  3. 03アニメーションのプロパティと時間の制約
  4. 04タイポグラフィとレイアウトの規則
  5. 05ローディング・エラー・空状態への対応

活用シーン

  1. 01Tailwind CSS による UI コンポーネントの構築
  2. 02フロントエンドのアクセシビリティとキーボード操作の確認
  3. 03アニメーションやローディング・エラー状態の設計

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ckorhonen/claude-skills --skill ui-design

インストール後、Claude Code で/ui-designを入力して読み込まれたか確認します。

詳細

UI デザイン

エージェントを使ってより優れたインターフェースを構築するための、明確な制約です。

使用する場面

  • Tailwind CSS で UI コンポーネントを構築する
  • アニメーションやトランジションを実装する
  • キーボード/フォーカスの動作を伴うインタラクティブな要素を追加する
  • アクセシビリティの観点からフロントエンドコードをレビューする
  • 適切な z-index と間隔を用いてレイアウトを設計する
  • ローディング状態、エラー処理、または空の状態を扱う

スタック

要件ルール
Tailwind CSSカスタム値より先に、デフォルト値(間隔、角丸、シャドウ)を必ず使用する
アニメーションライブラリJavaScript アニメーションには motion/react(旧 framer-motion)を必ず使用する
CSS アニメーション登場アニメーションやマイクロアニメーションには tw-animate-css を使用することが推奨されます
クラスのロジックcn ユーティリティ(clsx + tailwind-merge)を必ず使用する

コンポーネント

要件ルール
インタラクティブなプリミティブキーボード/フォーカスの動作には、アクセシブルなプリミティブ(Base UI、React Aria、Radix)を必ず使用する
既存のコンポーネントまずプロジェクトに既存のプリミティブを必ず使用する
一貫性同じインタラクション領域内で、異なるプリミティブシステムを決して混在させない
新しいプリミティブスタックとの互換性がある場合は、Base UI を優先することが推奨されます
アイコンボタンアイコンのみのボタンには、必ず aria-label を追加する
カスタム動作明示的に依頼されない限り、キーボードやフォーカスの動作を手作業で再構築しない

インタラクション

要件ルール
破壊的な操作破壊的、または取り消せない操作には、必ず AlertDialog を使用する
ローディング状態構造を反映したスケルトンを使用することが推奨されます
ビューポートの高さh-screen は決して使用せず、h-dvh を使用する
固定要素必ず safe-area-inset を考慮する
エラー表示操作が発生する場所の近くに、必ずエラーを表示する
入力の動作input または textarea 要素での貼り付けを決してブロックしない

アニメーション

要件ルール
デフォルト明示的に依頼されない限り、アニメーションを追加しない
コンポジターのプロパティtransform と opacity のみを必ずアニメーションさせる
レイアウトのプロパティwidth、height、top、left、margin、padding は決してアニメーションさせない
描画のプロパティ小規模で局所的な UI(テキスト、アイコン)を除き、background と color は避けることが推奨されます
登場時のイージング登場時には ease-out を使用することが推奨されます
フィードバックの時間インタラクションのフィードバックは、決して 200ms を超えないようにする
ループ画面外にある場合、ループアニメーションを必ず一時停止する
アクセシビリティ必ず prefers-reduced-motion に対応する
カスタムイージング明示的に依頼されない限り、カスタムのイージングカーブを導入しない
大きな領域大きな画像や全画面の領域をアニメーションさせないことが推奨されます

タイポグラフィ

要件ルール
見出し必ず text-balance を使用する
本文段落には必ず text-pretty を使用する
データ必ず tabular-nums を使用する
情報量の多い UItruncate または line-clamp を使用することが推奨されます
文字間隔明示的に依頼されない限り、letter-spacing(tracking-)を変更しない

レイアウト

要件ルール
Z-index固定スケールを使用する(任意の z-x は使用しない)
正方形の要素w-x + h-x ではなく、size-x を使用することが推奨されます

パフォーマンス

要件ルール
ぼかし効果大きな blur() または backdrop-filter の領域を決してアニメーションさせない
Will-changeアニメーションの実行中以外は、will-change を決して適用しない
useEffectレンダリングロジックで表現できる処理には、決して使用しない

デザイン

要件ルール
グラデーション明示的に依頼されない限り、決して使用しない
紫色/マルチカラーのグラデーション決して使用しない
グロー効果主要なアフォーダンスとして決して使用しない
シャドウ明示的に依頼されない限り、Tailwind CSS のデフォルトスケールを使用することが推奨されます
空の状態次に取るべき明確なアクションを必ず示す
アクセントカラー1 つのビューにつき 1 色までに抑えることが推奨されます
カラートークン新しいトークンを導入する前に、既存のテーマまたは Tailwind CSS のトークンを使用することが推奨されます

よくある落とし穴

UI デザインで最もよくある失敗は、余白、色のコントラスト、モバイル対応、状態管理、視覚的階層に関するものです。これらの落とし穴は、インターフェースの構築時にエージェントが繰り返し犯す間違いから明らかになっています。

1. 余白の不統一(任意のマージン、システムなし)

問題: 意図したシステムなしに、Tailwind の余白値を一貫性なく組み合わせると、レイアウトに統一感がなくなります。

// ❌ WRONG - arbitrary pixel values, no spacing system
<div className="ml-[13px] mr-[22px] pt-[7px]">Content</div>

// ✅ CORRECT - use system scales
<div className="ml-4 mr-4 pt-4">Content</div>

うまくいかない理由:

  • デザインが場当たり的に見えます(ある余白は2px、別の余白は16pxで、パターンがありません)
  • モバイルレイアウトが崩れます(余白が比例して拡大・縮小されません)
  • 配置を揃えられません(間隔がグリッドに揃いません)

修正方法:

  • 必ず Tailwind の余白スケール(0、1、2、3、4、6、8、12、16、24、32など)を使用します
  • よく使うパターンには余白システムを定義します:
    • ボタンのパディング:px-4 py-2
    • カードのパディング:p-6
    • セクション間の間隔:gap-8
    • ネストされた要素の間隔:ml-6(ml-3ではなく)
  • 関連する要素をグループ化し、間隔を統一します(すべての横並びリストで同じgap-xを使用します)
2. 色のコントラスト不足(アクセシビリティ違反)

問題: WCAG AA のコントラスト比(テキストは4.5:1、UI コンポーネントは3:1)を満たさない配色により、弱視のユーザーがコンテンツを読めなくなります。

// ❌ WRONG - gray-400 text on gray-100 background fails contrast
<p className="text-gray-400 bg-gray-100">Important content</p>

// ✅ CORRECT - gray-700 on gray-100 passes WCAG AA
<p className="text-gray-700 bg-gray-100">Important content</p>

うまくいかない理由:

  • ユーザーの約4~5%は色覚異常があり、さらに広い範囲にあたる15%以上は何らかの視覚障害を抱えています
  • 明るいディスプレイ(スマートフォン)では、コントラストの低いテキストを読めません
  • 法的責任が生じる可能性があります(多くの管轄区域で WCAG への準拠が必須です)

修正方法:

  • 十分なコントラストがある色の組み合わせを使用します:
    • どのような背景でもtext-gray-900/text-white(常に安全です)
    • 明るい背景ではtext-gray-700
    • 暗い背景ではtext-gray-300
  • 色をテストします: オンラインのコントラストチェッカーを使用します(https://webaim.org/resources/contrastchecker/)
  • 問題のある組み合わせを避けます:
    • gray-100上のgray-400 ❌
    • pink-100上のred-500 ❌
    • 無効状態:白地ではtext-gray-400、色付き背景ではtext-gray-600を使用します
  • グレー以外の色付きテキストの場合: より暗い色合いを使用します(red-300ではなくred-700)
3. モバイルに不向きなレイアウト(小さなタッチターゲット、横スクロール)

問題: 44×44px未満のタッチターゲット、横スクロール、または狭いビューポートに適応しないレイアウトです。

// ❌ WRONG - button too small, no responsive layout
<button className="px-2 py-1 text-xs">Click me</button>
<div className="flex gap-2">
  <div className="w-80">Sidebar</div>
  <div className="w-80">Content</div>
</div>

// ✅ CORRECT - hit target size, responsive stack
<button className="px-4 py-3 md:px-6 md:py-3">Click me</button>
<div className="flex flex-col md:flex-row gap-4">
  <div className="w-full md:w-80">Sidebar</div>
  <div className="flex-1">Content</div>
</div>

うまくいかない理由:

  • トラフィックの40%はモバイルからであり、小さなボタンはタップできません
  • スマートフォンでの横スクロールは、UX を著しく損ないます
  • デスクトップで機能するレイアウトが、モバイルでは使いものにならない場合があります

修正方法:

  • タッチターゲットは最低44×44pxにします
    • ボタン:少なくともpx-4 py-3(標準:px-6 py-3)
    • リンク:クリック可能な領域を広く取ります
  • モバイルでは縦に並べます: デフォルトではflex-colを使用し、デスクトップではmd:flex-rowを使用します
  • レスポンシブな幅を使用します:
    • モバイルではw-full、デスクトップではmd:w-80またはmd:w-1/3を使用します
    • 柔軟なレイアウトでは、固定幅ではなくflex-1を使用します
  • レスポンシブ対応をテストします: 375px(iPhone SE)と768px(タブレット)のブレークポイントで確認します
  • 横スクロールを避けます: ユーザーに横方向へのスクロールを強制してはいけません
4. 状態の欠落(読み込み中、エラー、空、無効)

問題: 正常な場合しか表示せず、読み込み中、エラー発生時、または空の場合に何も表示されないインターフェースです。

// ❌ WRONG - no feedback states
<button onClick={save}>Save</button>
<div>{data.map(item => <ItemCard item={item} />)}</div>

// ✅ CORRECT - all states accounted for
<button onClick={save} disabled={loading}>
  {loading ? 'Saving...' : 'Save'}
</button>

{loading && <LoadingSkeleton />}
{error && <ErrorAlert message={error} />}
{!loading && data.length === 0 && <EmptyState action="Create your first item" />}
{!loading && !error && data.map(item => <ItemCard item={item} />)}

うまくいかない理由:

  • ユーザーは操作が成功したかどうかわかりません(フィードバックがないと、失敗したと感じます)
  • 空のリストは、意図的に空なのではなく、壊れているように見えます
  • エラーからの復旧手段がありません

修正方法:

  • 読み込み状態: スピナーではなく、構造を示すスケルトンを使用します
    • コンテンツの読み込み中もレイアウトを安定させます
    • 回転する表示だけでなく、これから表示される内容をユーザーに示します
  • エラー状態: 失敗した操作の近くに表示します
    • 何が起きたのか、どうすれば解決できるのかを説明します
    • 再試行ボタンを含めます
  • 空の状態: 意図を示し、操作につなげます
    • 「まだ項目がありません」→「最初の項目を作成しましょう」(ボタン)
    • 空の状態が何を意味するのか、どうすれば内容を追加できるのかを示します
  • 無効状態: 必ず視覚的なフィードバックを提供します
    • disabled={true}とopacity-50またはcursor-not-allowedを組み合わせます
    • 理由を説明せず、何も知らせないまま無効にしてはいけません
5. 階層が不明瞭(すべてが同じくらい重要に見える)

問題: すべてのテキストが同じサイズで、すべての色の強さが同じで、すべての間隔が同じです。目立つものがありません。

// ❌ WRONG - flat hierarchy
<div className="text-lg text-gray-700">
  <div className="text-lg text-gray-700">Subheading</div>
  <div className="text-lg text-gray-700">Subtitle</div>
  <button className="text-lg px-4 py-2">Action</button>
</div>

// ✅ CORRECT - clear hierarchy
<div>
  <h1 className="text-3xl font-bold text-gray-900">Title</h1>
  <h2 className="text-xl font-semibold text-gray-700 mt-6">Subheading</h2>
  <p className="text-base text-gray-600 mt-2">Subtitle</p>
  <button className="mt-6 px-6 py-3 bg-blue-600 text-white font-semibold rounded">
    Primary Action
  </button>
</div>

うまくいかない理由:

  • ユーザーは最初にどこを見ればよいかわかりません
  • ページをざっと見て把握するのに時間がかかります(流し読みできる構造がありません)
  • 重要な操作が見逃されます

修正方法:

  • サイズの階層:
    • 見出し:text-3xl(h1)→ text-2xl(h2)→ text-xl(h3)
    • 本文:text-base(段落)→ text-sm(補助テキスト)
  • ウェイトの階層:
    • 主な操作:font-bold または font-semibold
    • 重要なテキスト:font-semibold
    • 本文:font-normal
    • 補助テキスト:より薄い色で font-normal
  • 色の階層:
    • 主なテキスト:text-gray-900
    • 補助テキスト:text-gray-600
    • 第三階層:text-gray-500
  • 間隔の階層:
    • セクション間:gap-8(最大)
    • コンポーネント間:gap-4
    • 密度の高いコンテンツ:gap-2
  • ビューごとにアクセントカラーを1色にする: CTA には単一の色を使い、複数の色が注意を奪い合わないようにします

クイックリファレンス

使用可能なアニメーションプロパティ
transform, opacity
使用禁止のアニメーションプロパティ
width, height, top, left, margin, padding, blur(), backdrop-filter
必須のアクセシビリティパターン
// Icon button - always add aria-label
<button aria-label="Close dialog">
  <XIcon />
</button>

// Respect reduced motion
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; }
}
クラスユーティリティのパターン
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

コメント