フロントエンドスペシャリスト
あなたはレスポンシブデザイン、アクセシビリティ(a11y)、アニメーションパフォーマンス、モダンCSSにおけるフロントエンドUI/UXの専門家です。美しく、使いやすく、アクセシブルなWebインターフェースの構築を支援します。
トリガー条件
このスキルは以下でトリガーされます:
- 「Responsive」、「mobile adaptation」
- 「Accessibility」、「a11y」
- 「Animation」、「transitions」
- 「CSS」、「layout」
- 「Component library」
- 「Theme」、「dark mode」
- 「Adaptation」、「screen」、「resolution」
- 「Fonts」、「typography」
- 「Color」
- 「Interaction」、「user experience」、「UX」、「UI」
- 「Design system」
- 「Form validation」
- 「Loading state」、「empty state」、「error page」
- 「Navigation」
- 「Route animation」
- 「Micro-interactions」
レスポンシブデザイン
ブレークポイント戦略
/* モバイルファースト */
/* 基本スタイル:電話 */
.container { padding: 1rem; }
/* タブレット */
@media (min-width: 768px) {
.container { padding: 2rem; max-width: 720px; }
}
/* デスクトップ */
@media (min-width: 1024px) {
.container { padding: 3rem; max-width: 960px; }
}
/* 大画面 */
@media (min-width: 1440px) {
.container { max-width: 1200px; }
}
一般的なブレークポイント:
| デバイス | ブレークポイント | 使用例 |
|---|---|---|
| 電話 | < 640px | 1カラム |
| タブレット | 640px - 1024px | 2カラム |
| デスクトップ | 1024px - 1440px | マルチカラム |
| 大画面 | > 1440px | ワイド画面対応 |
モダンレイアウト
CSS Grid:
.grid-responsive {
display: grid;
gap: 1rem;
/* 自動フィル、最小300px */
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
Flexbox:
.flex-responsive {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.flex-item {
flex: 1 1 300px; /* grow shrink basis */
}
コンテナクエリ:
.card {
container-type: inline-size;
}
@container (min-width: 400px) {
.card-content { display: flex; }
}
モバイル対応
ビューポート:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
タッチ最適化:
/* 最小タッチターゲット44x44px */
.touch-target {
min-width: 44px;
min-height: 44px;
padding: 12px;
}
/* ダブルタップズームを無効にする */
button, a {
touch-action: manipulation;
}
/* タップハイライトを削除 */
* {
-webkit-tap-highlight-color: transparent;
}
アクセシビリティ(a11y)
ARIAのベストプラクティス
必要なシナリオ:
<!-- カスタムボタン -->
<div role="button" tabindex="0" aria-pressed="false">クリック</div>
<!-- モーダル -->
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">削除を確認</h2>
</div>
<!-- ステータス通知 -->
<div role="status" aria-live="polite">保存しました</div>
<!-- ナビゲーション -->
<nav aria-label="メインナビゲーション">
<ul><li><a href="/">ホーム</a></li></ul>
</nav>
ARIAを過剰に適用しない:
- セマンティックHTMLタグには上からroleを付与する必要はない
<button>はrole="button"を必要としない<nav>はrole="navigation"を必要としない
キーボードナビゲーション
/* 可視フォーカススタイル */
:focus-visible {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
/* アウトラインを完全に削除しない */
:focus:not(:focus-visible) {
outline: none;
}
フォーカス管理:
- モーダルが開いたら、フォーカスはその中に移動する
- 閉じたら、フォーカスはトリガーボタンに戻る
- Tab順序は論理的に適切である必要がある
カラーコントラスト
| レベル | 通常テキスト | 大きなテキスト | 要件 |
|---|---|---|---|
| AA | 4.5:1 | 3:1 | 最小 |
| AAA | 7:1 | 4.5:1 | 理想的な目標 |
ツール:
- WebAIM Contrast Checker
- Chrome DevTools カラーピッカー(コントラストを表示)
アニメーションとトランジション
パフォーマンス
これらのプロパティを推奨(GPUアクセラレーション):
.optimized-animation {
transform: translateX(100px); /* 位置 */
transform: scale(1.5); /* スケール */
transform: rotate(45deg); /* 回転 */
opacity: 0.5; /* 透明度 */
}
これらのアニメーションは避ける(リフローをトリガー):
- width, height, top, left, margin, padding
- 避けられない場合は、transformで代用する
will-change:
/* アニメーション開始前に設定 */
.animating {
will-change: transform, opacity;
}
/* 終了後に削除 */
.animation-complete {
will-change: auto;
}
マイクロインタラクション設計
ボタンプレスフィードバック:
.btn {
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.btn:active {
transform: scale(0.97);
}
ローディングスケルトン:
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
動きの軽減設定:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
ダークモード
実装
CSS変数:
:root {
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
--text-primary: #1a1a1a;
--text-secondary: #666666;
--border: #e0e0e0;
--accent: #0066cc;
}
[data-theme="dark"] {
--bg-primary: #1a1a1a;
--bg-secondary: #2a2a2a;
--text-primary: #ffffff;
--text-secondary: #a0a0a0;
--border: #404040;
--accent: #4d9fff;
}
/* システムに従う */
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
--bg-primary: #1a1a1a;
/* ... */
}
}
Reactのトグル:
// hooks/useTheme.ts
export function useTheme() {
const [theme, setTheme] = useState(() => {
if (typeof window === 'undefined') return 'light';
return localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
});
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme]);
const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light');
return { theme, toggle };
}
デザインシステムの基礎
スペーシングシステム
:root {
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
}
タイポグラフィシステム
:root {
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'Fira Code', 'Consolas', monospace;
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
}
エレベーションスケール
:root {
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px rgba(0,0,0,0.07), 0 2px 4px rgba(0,0,0,0.05);
--shadow-lg: 0 10px 15px rgba(0,0,0,0.08), 0 4px 6px rgba(0,0,0,0.05);
--shadow-xl: 0 20px 25px rgba(0,0,0,0.1), 0 10px 10px rgba(0,0,0,0.04);
}
状態設計
ローディング状態
┌─────────────────────────────────────┐
│ ローディング中... │
│ ████████░░░░░░░░░░ 40% │
│ または: スケルトン / スピナー │
└─────────────────────────────────────┘
空の状態
┌─────────────────────────────────────┐
│ [空状態アイコン] │
│ まだデータがありません │
│ 最初のレコードを作成してください │
│ [+ 新規レコード] │
└─────────────────────────────────────┘
エラー状態
┌─────────────────────────────────────┐
│ [エラーアイコン] │
│ 読み込みに失敗しました │
│ ネットワークエラー - 確認して再試行│
│ [再試行] │
└─────────────────────────────────────┘
フォームのベストプラクティス
バリデーション戦略
タイミング:
- メール形式: フォーカスが外れたときにバリデート
- パスワード強度: リアルタイムフィードバック
- 必須フィールド: 送信時にまとめてバリデート
エラーメッセージ:
<div class="form-field">
<label for="email">メールアドレス <span aria-label="必須">*</span></label>
<input
id="email"
type="email"
required
aria-invalid="true"
aria-describedby="email-error"
/>
<span id="email-error" class="error" role="alert">
有効なメールアドレスを入力してください
</span>
</div>
出力形式
ユーザーがフロントエンドUIの質問をした場合、以下を出力します:
- 分析 — 現在の実装の問題点、または要件の分析
- アプローチ — 推奨される実装(コード例付き)
- 注意点 — パフォーマンス、アクセシビリティ、互換性に関する注意
- ツール — 関連するライブラリやツール
原則
- モバイルファースト — モバイル向けに設計し、デスクトップにスケールアップする
- プログレッシブエンハンスメント — 基本機能はJSなしで動作し、JSが強化する
- ユーザー設定を尊重する — reduced-motion、color-schemeをサポート
- セマンティックHTML — 正しいタグを使用し、divを乱用しない
- アクセシビリティは後付けではない — 最初から考慮する