mobile-ui-design について
モバイル画面を構築するエージェント向けのデザインスキルです。タッチ操作、ナビゲーション、安全領域、フォーム、モーション、アクセシビリティ、画面確認を扱い、iOSとAndroidの設計パターンを案内します。
主な機能
- 01デザインコンテキストの読み込みとトークン設定
- 02タッチ領域とボトムナビゲーションの指針
- 03ジェスチャー、安全領域、プラットフォーム別パターン
- 04モバイルのタイポグラフィとフォームの指針
- 05モーション、アクセシビリティ、画面確認
活用シーン
- 01モバイルのナビゲーションと操作を設計
- 02安全領域に配慮したレイアウトを実装
- 03モバイルフォームとタッチ領域を確認
- 04複数のビューポートで生成画面を検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ arnavpuri/ designskills --skill mobile-ui-designインストール後、Claude Code で/mobile-ui-designを入力して読み込まれたか確認します。
詳細
モバイル UI デザイン
はじめる前に:デザインコンテキストを読み込む
.agents/design-context.mdを読み込みます(design-contextスキルによって作成されています)。ファイルがない場合は、tailwind.config.*または:rootの CSS 変数を確認します。何もない場合は、デフォルト(Primary#2563EB、Secondary#7C3AED、Accent#F59E0B、Tailwind のグレー系ニュートラル、見出しと本文に Inter、ミニマルスタイル)を使用し、デフォルトを使用したことをユーザーに伝えます —design-contextの実行を提案します。- トークンを CSS 変数に一度だけ対応付け、以降は変数のみを使用します:
--color-primary、--color-primary-light、--color-primary-dark、--color-secondary、--color-accent、--color-neutral-50…--color-neutral-900、--color-success/-warning/-error、--font-heading、--font-body、--font-mono、--radius、および 4px を基準とするスペーシングスケール。 - Tailwind: 同じ変数を(v3 の
theme.extend、v4 の@themeで)primary、primary-light、primary-dark、secondary、accent、neutral-*、font-heading、font-bodyとして公開します。以下の例にあるindigo-*/gray-*クラスは、これらのトークンを表すプレースホルダーです。 - Figma: ユーザーが Figma の URL を提示した場合は、Figma MCP(
get_design_context、get_screenshot)を使って取得し、それに合わせます。そのデザインでは、デザインコンテキストファイルより優先されます。 - ネイティブアプリ(React Native/Flutter:CSS 変数ではなくテーマオブジェクトにトークンを対応付け)、PWA、レスポンシブ Web サイトのどれにするかを尋ねます。
1. タッチターゲットのサイズ
すべてのインタラクティブ要素は、確実にタップできる大きさにする必要があります。
最小サイズ
| ソース | 最小ターゲット | 備考 |
|---|---|---|
| Apple HIG | 44×44pt | Web では 44×44 CSS px |
| Material Design | 48×48dp | 約 48×48 CSS px。アイコンの表示サイズは 24dp でも可 |
| WCAG 2.2 SC 2.5.8(AA) | 24×24 CSS px | 法的な最低基準であり、デザイン目標ではありません |
| WCAG 2.2 SC 2.5.5(AAA) | 44×44 CSS px |
このスキルのルール: すべてのタッチターゲットは最低 44×44px(Android 風の UI では 48×48)にします。
実装
/* Ensure tap targets even on small visual elements */
.icon-button {
width: 24px;
height: 24px;
/* Expand tap target with padding or pseudo-element */
position: relative;
}
.icon-button::after {
content: '';
position: absolute;
inset: -12px; /* extends tap area to 48x48 */
}
ターゲット間の間隔
タップ可能な要素間には最低 8px の間隔を設けます。12px 以上が望ましいです。これにより誤タップを防げます。
2. ボトムナビゲーション
親指は画面の下端に最も届きやすいため、主要なナビゲーションはそこに配置します。
標準的なボトムタブバー
<nav aria-label="Main" class="fixed bottom-0 inset-x-0 bg-white border-t border-gray-200
safe-area-bottom z-50">
<div class="flex items-center justify-around h-16">
<a href="#" aria-current="page" class="flex flex-col items-center justify-center gap-0.5
text-indigo-600 min-w-[64px] min-h-[48px]">
<svg class="w-6 h-6" fill="currentColor" aria-hidden="true"></svg>
<span class="text-xs font-medium">Home</span>
</a>
<a href="#" class="flex flex-col items-center justify-center gap-0.5
text-gray-500 min-w-[64px] min-h-[48px]">
<svg class="w-6 h-6" fill="none" stroke="currentColor" aria-hidden="true"></svg>
<span class="text-xs font-medium">Search</span>
</a>
</div>
</nav>
ルール
- タブは最大 5 つです。3~4 つが理想的です。
- アクティブなタブ:塗りつぶしアイコン+プライマリカラー。非アクティブなタブ:アウトラインアイコン+グレー。
- ラベルは必須です。アイコンのみのタブは見つけにくくなります。
- ハンバーガーメニューをタブバーの代わりに使用しないでください。
- ノッチのあるデバイスでは、画面下部のセーフエリアを考慮します。
セーフエリアの CSS
/* Only for elements with no bottom padding of their own */
.safe-area-bottom {
padding-bottom: env(safe-area-inset-bottom);
}
/* Elements that already have padding: ADD the inset, don't replace the padding */
.pb-safe-4 {
padding-bottom: calc(1rem + env(safe-area-inset-bottom));
}
また、スクロール可能なコンテンツにも余白を設定し(例:<main> に pb-[calc(4rem+env(safe-area-inset-bottom))])、最後の項目がタブバーに隠れないようにします。
3. ジェスチャーに適したインタラクション
スワイプ操作
<div class="relative overflow-hidden">
<div class="absolute inset-y-0 right-0 flex items-center bg-red-600 px-6">
<button type="button" class="text-white font-medium">Delete</button>
</div>
<div class="bg-white px-4 py-3 transform transition-transform touch-pan-y">
<div class="font-medium text-gray-900">List Item</div>
<div class="text-sm text-gray-500">Swipe left for actions</div>
</div>
</div>
プルして更新
- ユーザーが下に引っ張ると、画面上部にインジケーターを表示します。
- スピナーまたはカスタムアニメーションを使用します。
- 約 80px 引っ張った時点で更新を開始します。
- 可能であれば、触覚フィードバックを提供します(ネイティブのみ)。
- Web では、スクローラーに
overscroll-behavior-y: containを設定し、ブラウザー独自のプルして更新も発生しないようにします。
モバイルでよく使われるジェスチャー
| ジェスチャー | 用途 | 実装 |
|---|---|---|
| 左右へのスワイプ | 操作の表示、移動 | touch-action: pan-y |
| 下に引っ張る | コンテンツの更新 | オーバースクロールの検出 |
| 長押し | コンテキストメニュー、並べ替え | pointerdown + 約 500ms のタイムアウト。pointermove/pointerup でキャンセル |
| ピンチ | 画像や地図のズーム | ズーム可能な要素に touch-action: none を設定し、Pointer Events を使用 |
| 上へのスワイプ | シートを閉じる、展開する | ボトムシートにドラッグハンドルを設置 |
ボタンやリンクに touch-action: manipulation を設定すると、ダブルタップズームの遅延をなくせます。ただし、カスタムのピンチ操作が有効になるわけではありません。すべてのジェスチャーには、シングルタップで操作できる代替手段(ボタン、メニュー項目)を目に見える形で用意する必要があります。WCAG 2.5.1 と見つけやすさの両方が、それを求めています。
4. iOS と Android のデザイン言語
主な違い
| 要素 | iOS(ヒューマンインターフェース) | Android(Material) |
|---|---|---|
| ナビゲーション | 画面下部のタブバー | 下部ナビゲーションバー |
| 戻る操作 | 左端からのスワイプ/「戻る」というテキスト | 左上の矢印アイコン |
| タイトル | スクロール時に折りたたまれる大きなタイトル | 中央揃えまたは左揃え |
| ボタン | 角丸の長方形、浮き上がりなし | 角丸、浮き上がりあり |
| スイッチ | 緑/白のトグル | リップル効果付きのトラックとつまみ |
| シート | 上部が角丸、ドラッグハンドル付き | 全幅、ドラッグハンドル付き |
| タイポグラフィ | SF Pro(システム) | Roboto(システム) |
| 余白 | 16px のマージン | 16px のマージン |
クロスプラットフォームのアプローチ
両方のプラットフォーム向けに構築する場合は、洗練された見た目には iOS のパターンを、操作へのフィードバックには Material のパターンを取り入れます。システムフォントを使用します。
/* Use the design-context body font if set; otherwise the platform font */
font-family: var(--font-body, system-ui), -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
5. セーフエリア
デバイスに関する考慮事項
/* Status bar area */
.top-safe {
padding-top: env(safe-area-inset-top);
}
/* Home indicator / gesture bar */
.bottom-safe {
padding-bottom: env(safe-area-inset-bottom);
}
/* Notch / Dynamic Island on landscape */
.side-safe {
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
/* Full-bleed safe wrapper */
.safe-area {
padding: env(safe-area-inset-top) env(safe-area-inset-right)
env(safe-area-inset-bottom) env(safe-area-inset-left);
}
ビューポートのメタ設定
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
env(safe-area-inset-*) がゼロ以外の値を返すには、viewport-fit=cover が必要です。maximum-scale=1 や user-scalable=no は追加しないでください。ピンチズームができなくなります(WCAG 1.4.4)。
ビューポートの高さ
モバイルブラウザーのツールバーが表示されていると、100vh は表示領域よりも高くなります。全画面アプリのシェルやシートにはツールバーに追従する 100dvh を、スクロール中にサイズ変更したくないヒーローには 100svh を使用します(Tailwind:h-dvh、h-svh)。
6. モバイルのタイポグラフィスケール
推奨スケール
| 役割 | サイズ | 太さ | 行の高さ |
|---|---|---|---|
| 大きなタイトル | 34px | 700 | 1.2 |
| タイトル 1 | 28px | 700 | 1.2 |
| タイトル 2 | 22px | 700 | 1.25 |
| タイトル 3 | 20px | 600 | 1.25 |
| 見出し | 17px | 600 | 1.3 |
| 本文 | 17px | 400 | 1.5 |
| コールアウト | 16px | 400 | 1.4 |
| 小見出し | 15px | 400 | 1.4 |
| 脚注 | 13px | 400 | 1.3 |
| キャプション | 12px | 400 | 1.3 |
ルール
- モバイルで読みやすくするため、本文の最小サイズは 16px にします。
- 入力欄、セレクト、テキストエリアは 16px 以上にします。iOS Safari はフォントがこれより小さいフィールドに自動でズームインします。
- 行の長さは、モバイルで快適に読めるよう最大 35~50 文字にします。
- 16px のルートサイズに基づく
rem単位を使用します。
7. 親指ゾーンの最適化
親指ゾーンマップ
+-----------------------------+
| Hard to reach | <- Top 25%: Put secondary actions, titles
| |
|-----------------------------|
| Comfortable stretch | <- Middle 50%: Content, scrollable lists
| |
|-----------------------------|
| Easy / Natural | <- Bottom 25%: Primary actions, nav, FAB
+-----------------------------+
実践的なルール
- 主要な操作は下部に配置:CTA ボタン、送信ボタン、ナビゲーション。
- 検索バー:下部への配置を検討します(iOS Safari のように)。
- FAB(フローティングアクションボタン):右下隅に 56px で配置し、影を付けます。
- 破壊的な操作は上部に配置:届きにくくすると、誤って操作しにくくなります。
<div class="fixed bottom-0 inset-x-0 px-4 pt-12 pb-safe-4 bg-gradient-to-t from-white via-white
to-transparent">
<button type="button" class="w-full bg-indigo-600 text-white py-4 rounded-2xl font-semibold
text-lg active:bg-indigo-700 transition-colors">
Continue
</button>
</div>
8. シート/ボトムドロワーのパターン
ボトムシートの構造
<div class="fixed inset-0 z-50">
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" data-dismiss></div>
<div role="dialog" aria-modal="true" aria-labelledby="sheet-title"
class="absolute bottom-0 inset-x-0 bg-white rounded-t-3xl
max-h-[85dvh] overflow-y-auto overscroll-contain pb-safe-4">
<div class="flex justify-center pt-3 pb-2" aria-hidden="true">
<div class="w-10 h-1 rounded-full bg-gray-300"></div>
</div>
<div class="px-4 pb-6">
<div class="flex items-center justify-between mb-4">
<h2 id="sheet-title" class="text-xl font-bold text-gray-900">Sheet Title</h2>
<button type="button" aria-label="Close" class="w-11 h-11 -mr-2 rounded-full
flex items-center justify-center text-gray-500">✕</button>
</div>
</div>
</div>
</div>
シートの種類
- プレビューシート:コンテンツの 30% を表示し、ドラッグすると展開します。
- アクションシート:選択肢のリスト(iOS スタイル)。
- 全画面シート:上にスライドして画面全体を覆い、閉じるボタンを表示します。
- マップシート:地図上に常時表示するボトムシート(Google Maps スタイル)。
シートのルール
- 必ずドラッグハンドル(幅 40px、高さ 4px、中央揃え、角丸)を含めます。
- 最大の高さはビューポートの 85% とし、シート内でスクロールできるようにします。
- 閉じる操作には、下方向へのスワイプ、背景のタップ、明示的な閉じるボタンを使用します。
- スナップ位置は、折りたたみ、半分、展開にします。
9. モバイルフォームのデザイン
ルール
- 大きな入力欄:高さは最低 48px、フォントは 16px 以上にします(iOS のズームを防ぎます)。
- 全幅の入力欄:コンテナー内で端から端まで配置します。
- 適切なキーボードタイプ:
inputmode属性を使用します。 - 固定された送信ボタン:画面下部に固定します。
- 1 列のみ:モバイルではフィールドを横並びにしないでください。
入力モード属性
<input type="tel" inputmode="tel" />
<input type="email" inputmode="email" />
<input type="text" inputmode="numeric" pattern="[0-9]*" />
<input type="url" inputmode="url" />
<input type="search" inputmode="search" />
モバイル向けに最適化された入力欄
<div class="space-y-1.5">
<label for="phone" class="block text-sm font-medium text-gray-700">
Phone number
</label>
<input
type="tel"
id="phone"
inputmode="tel"
autocomplete="tel"
class="block w-full h-12 rounded-xl border border-gray-300 px-4
text-base text-gray-900 placeholder:text-gray-400
focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20"
placeholder="+1 (555) 000-0000"
/>
</div>
10. ネイティブのようなアニメーション
タイミング関数
:root {
/* iOS-like spring curve (slight overshoot) */
--ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);
/* Quick in, slow out (for entering elements) */
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
/* Slow in, quick out (for exiting elements) */
--ease-in: cubic-bezier(0.55, 0, 1, 0.45);
}
一般的なトランジション
/* Page transition: slide from right */
.page-enter {
transform: translateX(100%);
animation: slide-in 350ms var(--ease-out) forwards;
}
@keyframes slide-in {
to { transform: translateX(0); }
}
/* Sheet reveal: slide up */
.sheet-enter {
transform: translateY(100%);
animation: sheet-up 400ms var(--ease-out) forwards;
}
@keyframes sheet-up {
to { transform: translateY(0); }
}
/* Tap feedback */
.tap-feedback:active {
transform: scale(0.97);
opacity: 0.8;
transition: transform 50ms ease, opacity 50ms ease;
}
モバイル向けアニメーションのルール
- アニメーションは 400ms 未満にします。200~300ms なら軽快に感じられます。
- 60fps のパフォーマンスを保つため、
transformとopacityのみを使用します。 - アニメーション要素への
will-change: transformの指定は控えめにします。 prefers-reduced-motionを尊重します:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
- 重要な操作には触覚フィードバックを提供します(ネイティブのみ)。
検証:レンダリングと確認
確認していないコードを引き渡さないでください。生成したら、次の手順で確認します。
- モバイルとデスクトップの幅でスクリーンショットを撮影します。例:
npx playwright screenshot --full-page --viewport-size=390,844 file://$PWD/index.html mobile.png。続けて--viewport-size=1440,900を指定してもう一度撮影します(または開発サーバーのURLを指定します)。 - 両方の画像を開いて確認します。横スクロールや、テキストの切り抜け・重なりがないこと、階層がひと目でわかること、ブランドトークンが適用されていること、画像が正しいアスペクト比で読み込まれていることを確認します。
- Tab キーで一度フォーカスを移動します。すべてのインタラクティブ要素に、論理的な順序で視認できるフォーカスリングが表示されることを確認します。
- 320px幅と横向きでもスクリーンショットを撮影します。固定バーがセーフエリアを避けている必要があります。ヘッドレスブラウザーではノッチをエミュレートしないため、固定された上部/下部の各バーに
env(safe-area-inset-*)が追加されていることをコードで確認します(利用できる場合はiOSシミュレーターで確認します)。 - 見つかった問題を修正し、完了を報告する前にもう一度スクリーンショットを撮影します。
モバイルデザインチェックリスト
- すべてのタップ対象が44×44px以上(Materialでは48×48px)。WCAGの24×24pxを下回らないこと
- ラベル付きのタブが3~5個ある下部ナビゲーション
- セーフエリア(上部、下部、左右)に対応していること
- 本文テキストが16px以上(フォーカス時にiOSでズームされないこと)
- 主な操作を親指の届く範囲(下部)に配置すること
- フォームで正しい
inputmode属性を使うこと - シートにドラッグハンドルと閉じるジェスチャーを用意すること
- アニメーションを400ms未満にし、GPUに適したプロパティを使うこと
-
viewport-fit=coverメタタグを設定し、ズームを無効にしないこと - 高さ全体を使うレイアウトでは
100vhではなくdvh/svhを使うこと - すべてのジェスチャーにタップ操作の代替手段を用意すること。シートにはダイアログのラベルを付け、フォーカスを管理すること
-
prefers-reduced-motionに対応すること - コンテンツが幅320pxで読み取れること
- どの画面でも横スクロールが発生しないこと
- 320px、390px、横向きでレンダリングしてスクリーンショットを撮影すること(「検証」を参照)