mcpField
W
web-ui-best-practices

web-ui-best-practices

github.com/jamditisデザイン・UI438160 回インストール4 日前に更新
リポジトリ

web-ui-best-practices について

開発者向けの Web UI 設計スキルです。操作速度、アクセシビリティ、ナビゲーション、状態の永続化、最新の CSS を実践的な指針とともに扱います。

主な機能

  1. 01素早い操作フィードバック
  2. 02レスポンシブ CSS パターン
  3. 03再開可能なユーザー状態
  4. 04ナビゲーションと URL の指針
  5. 05アクセシビリティと入力の指針

活用シーン

  1. 01Web インターフェースのレビュー
  2. 02レスポンシブコンポーネントの設計
  3. 03フォームやナビゲーションの改善

インストール

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

$npx skills add https://github.com/jamditis/claude-skills-journalism --skill web-ui-best-practices

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

同梱ファイルagents/openai.yaml

詳細

Web UI のベストプラクティス

速く、意図が明確で、ユーザーの時間を尊重するウェブインターフェースを構築するための原則です。ここにあるルールはどれも目安です。理由があれば破っても問題ありませんが、いくつも破っているなら UI を見直す必要があります。

速度

あらゆる操作は 100ms 未満で完了します。それが難しい場合は、即座に完了したように見せます。

  • 楽観的 UI 更新を行い、サーバーの確認を待たずに結果を表示します
  • 入力はデバウンスしますが、体感上の応答は決して遅らせません
  • 次に表示される可能性の高いルートを、ホバー時またはビューポートに入った時点でプリフェッチします
  • アニメーションには will-change と transform を使い、top/left は使いません
  • 感覚ではなく performance.now() で計測します
// Optimistic delete, remove from UI immediately, reconcile later
async function handleDelete(id) {
  setItems(prev => prev.filter(i => i.id !== id));
  try {
    await api.delete(`/items/${id}`);
  } catch {
    setItems(prev => [...prev, originalItem]);
    toast("Couldn't delete. Restored.");
  }
}
スケルトンローディング状態

表示される内容の形が分かっている場合、スピナーは表示しません。レイアウトに合ったスケルトンを表示し、実際のコンテンツに切り替えます。

.skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: 4px;
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

モダン CSS ツールキット

2023 年から 2026 年にかけて成熟した 4 つの機能により、JavaScript を使わずにコンポーネント単位のレスポンシブレイアウトや SPA のような画面遷移を実現できます。フレームワークを追加する前に、これらを活用してください。

コンテナクエリ

コンテナクエリを使うと、コンポーネントはビューポートではなく、そのコンテナのサイズに応じて表示を変えられます。同じカードを、ページ単位でメディアクエリを調整することなく、幅 300px のサイドバーにも幅 900px のメインカラムにも表示できます。

.card-list {
  container-type: inline-size;
  container-name: cards;
}

@container cards (min-width: 480px) {
  .card { display: grid; grid-template-columns: 120px 1fr; }
}

2023 年以降、主要なすべてのブラウザーで安定して動作します。「同じコンポーネントを 2 か所で使うときに見た目を変える必要がある」という場合のハックの大半を置き換えられます。

:has() 親セレクター

:has() を使うと、子孫要素に基づいて親要素自身のスタイルを設定できます。長らく求められていた「親セレクター」です。JavaScript を使わずに、フォームフィールドをエラー状態にしたり、画像が添付されたカードを示したり、フォーカスされた入力欄を含む行を示したりする場合に便利です。

/* Highlight a form group when its input has focus */
.form-group:has(input:focus) {
  outline: 2px solid var(--color-primary);
}

/* Add bottom margin to articles that contain a figure */
article:has(figure) {
  margin-bottom: 2rem;
}

2023 年後半以降、Chrome、Safari、Firefox で安定して動作します。JavaScript によるクラス切り替えが必要だった具体的な場面を減らせます。

ビュートランジション

View Transitions API を使うと、フレームワークなしで、2 つの DOM 状態間のアニメーション(ルート変更、モーダルの開閉、リスト項目の入れ替え)を実行できます。ブラウザーが以前の状態をスナップショットし、新しい状態に切り替えた後、クロスフェードまたはスライドでつなぎます。

// Same-document transition (Chrome 111+, Safari TP, Firefox behind a flag)
function navigate(newView) {
  if (!document.startViewTransition) {
    renderView(newView);
    return;
  }
  document.startViewTransition(() => renderView(newView));
}
/* Smooth crossfade by default; override per element */
::view-transition-old(*) { animation-duration: 200ms; }
::view-transition-new(*) { animation-duration: 200ms; }

ドキュメント間のビュートランジション(ページ全体のナビゲーション間)は 2024 年に Chrome 126 で導入され、MPA を SPA のように感じさせることができます。動きに敏感なユーザーにアニメーションではなく瞬時の切り替えを提供できるよう、prefers-reduced-motion と組み合わせてください。

スクロール駆動アニメーション

animation-timeline: scroll() と animation-timeline: view() を使うと、時計の経過時間ではなくスクロール位置に応じて CSS アニメーションを動かせます。典型的な用途は、スクロールに合わせて進捗状況を表示する、記事上部のインジケーターです。

@keyframes fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.read-progress {
  position: fixed; top: 0; left: 0; right: 0; height: 3px;
  background: var(--color-primary);
  transform-origin: left;
  animation: fill linear;
  animation-timeline: scroll(root);
}

Chromium ベースのブラウザー(Chrome 115 以降、Edge)では安定して動作しますが、2026-05 時点では Safari と Firefox ではまだ利用できません。プログレッシブエンハンスメントとして使い、JavaScript のフォールバックを用意するか、ほかのブラウザーでは控えめな表示にします。

プロダクトツアーをなくす

UI の説明にツアーが必要なら、UI に問題があります。代わりに、次の方法を使います。

  • 実際の操作を通じて学べる空の状態(「最初のプロジェクトを作成」など)
  • プログレッシブディスクロージャーを使い、機能が必要になったときに表示します
  • 初回利用後に消えるインラインヒント
  • 設定なしで使えるデフォルト値

URL

スラッグは短く、読みやすく、人が推測しやすいものにします。UUID やクエリパラメーターだらけの URL は使いません。

Good:  /projects/weather-app
       /settings/billing
       /docs/api/auth

Bad:   /projects/550e8400-e29b-41d4-a716-446655440000
       /app?view=settings&tab=billing&subsection=plan
       /dashboard#!/module/documents/list?filter=active
  • ユーザーが入力した名前をもとにスラッグを作成します
  • 階層は最大 3 セグメントにします
  • URL はコピーや共有ができるようにします。URL はプロダクトの記憶です

永続化され、再開できる状態

ユーザーは離れた後、また戻ってきます。それを考慮してください。

  • フォームの下書き状態を localStorage またはサーバーに保存します
  • 戻る操作時にスクロール位置を復元します
  • フィルターや並べ替えの選択状態をセッションをまたいで保持します
  • URL に現在の表示状態をエンコードし、URL を共有すると同じ表示を再現できるようにします
// Persist form state across sessions
function usePersistentForm(key, defaults) {
  const [state, setState] = useState(() => {
    const saved = localStorage.getItem(key);
    return saved ? JSON.parse(saved) : defaults;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(state));
  }, [key, state]);

  return [state, setState];
}

色を控えめに

色は 3 色までにします。メインカラー、アクセントカラー、危険・破壊的操作用の色を 1 色ずつにします。それ以外はすべてグレーの濃淡にします。

:root {
  --color-primary: #2563eb;
  --color-accent: #f59e0b;
  --color-danger: #ef4444;

  --gray-50: #fafafa;
  --gray-100: #f4f4f5;
  --gray-200: #e4e4e7;
  --gray-400: #a1a1aa;
  --gray-600: #52525b;
  --gray-900: #18181b;
}
  • 新しい色相ではなく、不透明度や明度で階層を作ります
  • ダークモードでは、同じ 3 色を使い、グレーの明暗を反転させます
  • 4 色目が必要になったら、弱いレイアウトを色で補おうとしている可能性があります

スクロールバーを表示しない

ユーザーがスクロールしている間だけ表示します。コンテンツが無限に続くように感じられ、閉じ込められた印象を与えません。

/* Hide scrollbar across browsers */
.scroll-container {
  overflow-y: auto;
  scrollbar-width: none;          /* Firefox */
  -ms-overflow-style: none;       /* IE/Edge */
}
.scroll-container::-webkit-scrollbar {
  display: none;                  /* Chrome/Safari */
}

スクロールシャドウを使って、余計な装飾なしで続きがあることを示します。

.scroll-shadow {
  background:
    linear-gradient(white 30%, transparent),
    linear-gradient(transparent, white 70%) 0 100%,
    radial-gradient(farthest-side at 50% 0, rgba(0,0,0,.15), transparent),
    radial-gradient(farthest-side at 50% 100%, rgba(0,0,0,.15), transparent) 0 100%;
  background-repeat: no-repeat;
  background-size: 100% 40px, 100% 40px, 100% 12px, 100% 12px;
  background-attachment: local, local, scroll, scroll;
}

ナビゲーションの階層

どこからでも、ナビゲーションは3ステップ以内にします。目的地にたどり着くまでに3回以上クリックする必要がある場合は、階層をフラットにします。

  • パンくずリストは階層を示すために使い、ナビゲーションには使わない
  • グローバルナビゲーションは常に表示し、デスクトップではハンバーガーメニューの中に隠さない
  • パワーユーザー向けの抜け道として、Cmd+K / Ctrl+K を使う
コマンドパレット(Cmd+K)

ページが複数あるアプリには、コマンドパレットが必要です。

// Minimal Cmd+K listener
useEffect(() => {
  function handleKeyDown(e) {
    if ((e.metaKey || e.ctrlKey) && e.key === "k") {
      e.preventDefault();
      setCommandPaletteOpen(true);
    }
  }
  document.addEventListener("keydown", handleKeyDown);
  return () => document.removeEventListener("keydown", handleKeyDown);
}, []);

パレットはシンプルに保ちます:

  • ページ名、最近の操作、設定をあいまい検索する
  • キーボードショートカットをインラインで表示する
  • 最近使った項目を先に表示する
  • コマンドが20個以上になるまでは、カテゴリを設けない

クリップボード

ユーザーが期待する場所では、どこでもコピーと貼り付けが使えるようにします。

  • コード、URL、API キー、IDはワンクリックでコピーできるようにする
  • ファイルのアップロード欄や画像フィールドにクリップボードから貼り付けられるようにする
  • コピー時に短い確認(「コピーしました!」)を表示し、自動的に消す
async function copyToClipboard(text, label = "Copied") {
  await navigator.clipboard.writeText(text);
  toast(label, { duration: 1500 });
}

タップ領域

ボタンや入力欄のタップ領域は大きくします。WCAG 2.2(成功基準 2.5.8)では、最低サイズを24×24 CSS ピクセルと定めています。Apple のヒューマンインターフェイスガイドラインや、iOS/Android のネイティブアプリで一般的な規約では、使いやすいサイズとして44×44 ポイントが推奨されています。主要な操作には44pxを実用上の最小サイズとして使い、二次的なコントロール(密度の高いテーブル行のアイコンなど)には、絶対的な最低基準である24pxを使います。

button, .btn, [role="button"] {
  min-height: 44px;
  min-width: 44px;
  padding: 10px 20px;
}

input, select, textarea {
  min-height: 44px;
  padding: 10px 12px;
  font-size: 16px;  /* Prevents iOS Safari zoom on focus */
}
  • 隣り合うクリック可能な要素の間には、少なくとも8pxの間隔を設ける
  • アイコンのみのボタンには、ラベル付きボタンより大きなパディングを設定する
  • 重要な操作の手がかりをホバー状態だけに頼らない。タッチ操作では使えないためです

誠実なキャンセル

ワンクリックでキャンセルできるようにします。罪悪感をあおったり、ダークパターンを使ったり、「本当に損をしてもよいですか?」と聞いたりしません。

  • 確定ボタンの横に、キャンセルボタンを常に表示する
  • アカウント削除は、一度で完了できるようにする
  • 配信停止はワンクリックでできるようにし、設定画面を挟まない
  • ダウングレードにサポートへの問い合わせを求めない

ツールチップ

できる限り使いません。ツールチップは、UIだけでは説明できないことの表れです。

  • アイコンのみのボタンに限って使う(ラベルを補うため)
  • すでに読めるテキストには使わない
  • すぐに表示せず、ホバーしてから300ms後に表示する
  • スクロールしたら閉じる
  • 必須情報にツールチップを使わない

コピー文

能動態にします。1文は最大7語にします。法的文書ではなく、人に話しかけるように書きます。

Good:  "Project created"
       "Saved 2 minutes ago"
       "Delete this file?"

Bad:   "Your project has been successfully created!"
       "Changes were last saved approximately 2 minutes ago"
       "Are you sure you want to permanently delete this file? This action cannot be undone."
  • ボタンには「保存」「削除」「送信」のような動詞を使い、「送信する」「OK」「確認」は使わない
  • エラーメッセージでは、何が起きたかと次にすべきことを伝える
  • ユーザーのせいにしない(「入力が無効です」ではなく「有効なメールアドレスを入力してください」)
  • UIのコピーはすべてセンテンスケースにし、タイトルケースは使わない

視覚的な位置合わせ

幾何学的な位置合わせより、視覚的な位置合わせを優先します。目に映るのはピクセルではなく、重みです。

  • 再生アイコンは円の中で2~3px右にずらし、中央に見えるようにする
  • 先頭が大文字のテキストは、バウンディングボックスの左端より視覚的に左にそろえる
  • テキストの横に置くアイコンは、グリフに応じて上下に1~2pxずらす
  • テキストの周囲のパディングは、数学的な均等さではなく視覚的なバランスを取る。下のパディングは上より1~2px大きくすることがよくあります
/* Geometric center ≠ optical center */
.play-button svg {
  transform: translateX(2px);
}

/* Visually balanced card padding */
.card {
  padding: 20px 24px 22px 24px;
}

左から右への読み進め方

左から右への読み進め方と、F字型の視線走査に最適化します。

  • 最も重要なコンテンツは左上の領域に置く
  • 主要な操作は右側に置く(視線は行の終わりに向かうため)
  • ラベルは入力欄の横ではなく上に置く
  • テーブルでは、最もよく見られる列を左端に置く
  • 本文を中央揃えにしない。1行の見出し以外はすべて左揃えにする

データ損失への不安を和らげる

ユーザーは作業内容を失うことを恐れます。防止策を講じ、保存されたことを示します。

  • 自動保存を行い、「保存済み」の表示とタイムスタンプを見えるようにする
  • 破壊的な操作の後に元に戻せるようにする(完全削除ではなくソフト削除)
  • ページを離れるときに「未保存の変更があります」と表示する
  • ツイートより長いコンテンツには、バージョン履歴を用意する
  • 確認が必要なのは取り消せない操作だけにし、日常的な操作では求めない
// Warn on unsaved changes
useEffect(() => {
  function handleBeforeUnload(e) {
    if (hasUnsavedChanges) {
      e.preventDefault();
      e.returnValue = "";
    }
  }
  window.addEventListener("beforeunload", handleBeforeUnload);
  return () => window.removeEventListener("beforeunload", handleBeforeUnload);
}, [hasUnsavedChanges]);

ダウンロード可能なブランド素材

ダウンロード可能な SVG ロゴとブランドキットを掲載した /brand または /press ページを用意してください。ロゴをスクリーンショットさせないでください。

  • 背景が透明な SVG ロゴ
  • カラーコード(hex、RGB、HSL)
  • フォント名とウェイト
  • 使用ガイドライン(最小サイズ、クリアスペース、禁止事項)
  • ワンクリックで ZIP をダウンロード

チェックリスト

Web UI をレビューするときは、次の項目を確認してください。

  • すべての操作が 100ms 以内に完了する(または楽観的に処理される)
  • プロダクトツアーやオンボーディングモーダルがない
  • URL が短く読みやすく、UUID を含まない
  • 状態がセッション間やページ再読み込み後も保持される
  • 色は最大 3 色(グレーを除く)
  • 操作していないときにスクロールバーが表示されない
  • すべての遷移先に 3 ステップ以内で到達できる
  • SVG ロゴとブランドキットをダウンロードできる
  • スピナーではなくスケルトンローダーを使っている
  • コード、キー、URL をクリップボードにコピーできる
  • タッチターゲットの最小サイズが 44px
  • キャンセルが誠実で、ワンクリックで完了する
  • Cmd+K のコマンドパレットがある
  • ツールチップはアイコンのみのボタンに限る
  • コピーは能動態で、7 語以内
  • 幾何学的ではなく、視覚的に整列している
  • コンテンツが左から右への F 字型パターンに沿っている
  • 状態が見える自動保存と元に戻す機能がある

コメント