senior-frontend について
本番向けの React と TypeScript のフロントエンド開発を支援する構造化スキルです。コードベースの調査、開発、引き継ぎの 3 段階で、テストやアクセシビリティ、品質確認を扱います。
主な機能
- 01調査、開発、引き継ぎの 3 段階
- 02アトミックデザインのコンポーネント構成
- 03厳格な TypeScript と状態管理の指針
- 04ユニット、統合、E2E テスト
- 05アクセシビリティとパフォーマンスの確認
活用シーン
- 01変更前に既存フロントエンドを調査
- 02テストを併設した React コンポーネントの開発
- 03フロントエンド機能の状態管理を計画
- 04引き継ぎ前にフロントエンド品質を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill senior-frontendインストール後、Claude Code で/senior-frontendを入力して読み込まれたか確認します。
詳細
シニアフロントエンドエンジニア
概要
コンテキストの把握、開発、引き継ぎという3段階の構造化されたワークフローに従って、本番環境で使用できる品質のフロントエンドコードを提供します。このスキルでは、Atomic Design に基づくコンポーネントアーキテクチャ、包括的な状態管理パターン、SSR/SSG/ISR の最適化、Vitest、React Testing Library、Playwright を使用した85%を超えるテストカバレッジの必須要件など、厳格な品質基準を徹底します。
開始時に宣言してください:「本番環境向けの React/TypeScript 開発にシニアフロントエンドスキルを使用しています。」
フェーズ1:コンテキストの把握
目標: コードを書く前に、既存のコードベースを理解します。
実施項目
- 既存のコードベースの構造と規約を分析します
- 技術スタックのバージョンを特定します(React 18/19、Next.js 14/15、TypeScript のバージョン)
- 既存のコンポーネントライブラリとデザインシステムを確認します
- すでに使用されている状態管理の方法を確認します
- ビルドツールと CI パイプラインを把握します
- 既存のテスト基盤とカバレッジを把握します
停止 — 以下を満たすまでフェーズ2に進まないでください:
- 技術スタックのバージョンを特定している
- 既存のパターンと規約を文書化している
- テスト基盤を把握している
- 状態管理の方法を特定している
フェーズ2:開発
目標: 厳格な TypeScript、Atomic Design、TDD に基づいて実装します。
実施項目
- Atomic Design に従ってコンポーネントアーキテクチャを設計します
- TypeScript の strict モードで実装します
- 実装と並行してテストを作成します(適切な場合は TDD を実施します)
- パフォーマンスを最適化します(バンドルサイズ、レンダリング、読み込み)
- アクセシビリティ基準への準拠を確保します
コンポーネントアーキテクチャの判断表(Atomic Design)
| レベル | 説明 | ビジネスロジック | 例 |
|---|---|---|---|
| Atoms(アトム) | 最小の構成要素 | なし | Button、Input、Icon、Badge |
| Molecules(モレキュール) | アトムで構成 | 最小限 | FormField、SearchBar、Card |
| Organisms(オーガニズム) | ビジネスロジックを含む複雑な構成要素 | あり | DataTable、NavigationBar、CommentThread |
| Templates(テンプレート) | データを含まないページ構造 | レイアウトのみ | DashboardLayout、AuthLayout |
| Pages(ページ) | データと連携したテンプレート | データ取得 | UsersPage、SettingsPage |
アトムの例
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
}
export function Button({ variant = 'primary', size = 'md', isLoading, children, ...props }: ButtonProps) {
return (
<button className={cn(buttonVariants({ variant, size }))} disabled={isLoading || props.disabled} {...props}>
{isLoading ? <Spinner size={size} /> : children}
</button>
);
}
状態管理の判断表
| 状態の種類 | ソリューション | 使用する場面 |
|---|---|---|
| サーバー状態 | React Query/TanStack Query | API データ、キャッシュ、同期 |
| フォーム状態 | React Hook Form + Zod | フォームのバリデーション、送信 |
| グローバル UI 状態 | Zustand | テーマ、サイドバーの開閉、モーダル |
| ローカル UI 状態 | useState/useReducer | コンポーネント固有の状態 |
| URL 状態 | nuqs/useSearchParams | フィルター、ページネーション、タブ |
| 複雑なローカル状態 | useReducer | 複数の関連する状態遷移 |
| 共有コンテキスト | React Context | テーマ、ロケール、認証(更新頻度が低い場合) |
SSR/SSG/ISR の判断表(Next.js App Router)
| パターン | 使用する場面 | キャッシュ戦略 |
|---|---|---|
| 静的(SSG) | コンテンツがほとんど変更されない | ビルド時 |
| ISR | コンテンツが定期的に変更される | 再検証間隔 |
| SSR | リクエストごとにコンテンツが変更される | キャッシュなし |
| クライアント | ユーザー固有、インタラクティブ | ブラウザー |
サーバーコンポーネントとクライアントコンポーネントの判断
| 要件 | コンポーネントの種類 |
|---|---|
| データを直接取得する | サーバー(デフォルト) |
| イベントハンドラー(onClick、onChange) | クライアント('use client') |
| useState/useReducer | クライアント |
| useEffect/useLayoutEffect | クライアント |
| ブラウザー API(window、localStorage) | クライアント |
| クライアント機能を使用するサードパーティーライブラリ | クライアント |
| インタラクティブ性が不要 | サーバー(デフォルト) |
停止 — 以下を満たすまでフェーズ3に進まないでください:
- コンポーネントが Atomic Design の階層に従っている
- TypeScript の strict モードが有効で、
any型を使用していない - すべてのコンポーネントのテストを作成している
- アクセシビリティを検証している(axe-core)
フェーズ3:引き継ぎ
目標: 品質ゲートを確認し、レビューに備えます。
アクション
- テストカバレッジが85%を超えていることを確認します
- lintと型チェックをすべて実行します
- 複雑なコンポーネントにJSDoc/TSDocでドキュメントを記述します
- UIコンポーネントのStorybookストーリーを作成します
- パフォーマンス監査を実施します(Lighthouse、バンドル分析)
パフォーマンスチェックリスト
- バンドルサイズが200KB未満(gzip圧縮後、初回読み込み時)
- Largest Contentful Paintが2.5秒未満
- First Input Delayが100ms未満
- Cumulative Layout Shiftが0.1未満
- 画像:適切なサイズと形式で
next/imageを使用 - フォント:
next/fontでdisplay swapを使用 - レイアウトスラッシングがない(DOMの読み取り/書き込みをまとめる)
- 100項目を超えるリストには仮想化を適用
カバレッジのしきい値
{
"coverageThreshold": {
"global": {
"branches": 85,
"functions": 85,
"lines": 85,
"statements": 85
}
}
}
STOP — 次の項目が完了したら引き継ぎ完了です:
- テストカバレッジが85%を超えていることを確認済み
- lintと型チェックがエラーゼロで完了
- パフォーマンス監査を完了
- 複雑なコンポーネントのドキュメントを作成済み
テスト要件
単体テスト(Vitest + React Testing Library)
describe('Button', () => {
it('renders children', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
});
it('shows loading state', () => {
render(<Button isLoading>Click me</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
it('calls onClick when clicked', async () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click me</Button>);
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledOnce();
});
});
統合テスト
- コンポーネントの組み合わせ(フォーム送信のフロー)
- MSW(Mock Service Worker)を使ったデータ取得
- ルーティングとナビゲーション
- エラーバウンダリーとフォールバック
E2Eテスト(Playwright)
test('user can complete checkout', async ({ page }) => {
await page.goto('/products');
await page.getByRole('button', { name: 'Add to cart' }).first().click();
await page.getByRole('link', { name: 'Cart' }).click();
await expect(page.getByText('1 item')).toBeVisible();
await page.getByRole('button', { name: 'Checkout' }).click();
});
React Queryのパターン
function useUsers(filters: UserFilters) {
return useQuery({
queryKey: ['users', filters],
queryFn: () => fetchUsers(filters),
staleTime: 5 * 60 * 1000,
placeholderData: keepPreviousData,
});
}
function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
await queryClient.cancelQueries({ queryKey: ['users'] });
const previous = queryClient.getQueryData(['users']);
queryClient.setQueryData(['users'], (old) =>
old.map(u => u.id === newUser.id ? { ...u, ...newUser } : u)
);
return { previous };
},
onError: (err, newUser, context) => {
queryClient.setQueryData(['users'], context.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
}
メモ化の判断表
| 手法 | 使用する場合 | 使用しない場合 |
|---|---|---|
useMemo | コストの高い計算、依存値の参照等価性 | 単純な計算、プリミティブ値 |
useCallback | メモ化された子コンポーネントに渡す関数 | propsとして渡さない関数 |
React.memo | 同じpropsでコンポーネントが頻繁に再レンダリングされる場合 | レンダリングごとにpropsが変わる場合 |
| なし | デフォルト — メモ化しない | 必ず最初にプロファイリングする |
アンチパターン/よくある間違い
| アンチパターン | 問題点 | 正しいアプローチ |
|---|---|---|
データ取得にuseEffectを使う | 競合状態、キャッシュなし、重複排除なし | React QueryまたはServer Components |
| 2階層を超えるpropsのバケツリレー | 密結合、保守の負担 | コンポジション、コンテキスト、またはZustand |
| コンポーネント内のビジネスロジック | テスト不能、再利用不可 | フックまたはユーティリティ関数に切り出す |
| バレルエクスポート | ツリーシェイキングを妨げ、ビルドを遅くする | 直接インポート |
| 実装の詳細をテストする | リファクタリングで壊れる脆いテスト | ユーザーの操作と結果という振る舞いをテストする |
どこでもany型を使う | TypeScriptの目的を損なう | unknownと型ガードを使う |
| 動的でない値にインラインスタイルを使う | 一貫性がなく、保守しにくい | CSS modules、Tailwind、またはstyled-components |
| すべてをメモ化する | 複雑さが増し、かえって遅くなることが多い | まずプロファイリングし、その後メモ化する |
ドキュメントの検索(Context7)
最新のドキュメントを参照するには、mcp__context7__resolve-library-idを実行してからmcp__context7__query-docsを実行してください。返されたドキュメントは、記憶している知識より優先されます。
react— フックAPI、コンポーネントのライフサイクル、またはReact 19以降の機能について不明な場合next.js— App Router、Server Components、またはNext.js固有のAPIについて調べる場合typescript— 高度な型のパターンやコンパイラーオプションについて調べる場合tailwindcss— ユーティリティクラス、設定、またはプラグインAPIについて調べる場合vitest— テストランナーAPI、マッチャー、またはモックユーティリティについて調べる場合
統合ポイント
| スキル | 関係 |
|---|---|
testing-strategy | テスト戦略でフロントエンドのテストフレームワークを定義します |
test-driven-development | コンポーネントはTDDサイクルに沿って構築します |
react-best-practices | 詳細なReactのパターンで、このスキルを補完します |
performance-optimization | フロントエンドのパフォーマンスは最適化の手法に従います |
code-review | コンポーネントのアーキテクチャとテストカバレッジをレビューします |
clean-code | コード品質の原則はコンポーネントのコードにも適用されます |
webapp-testing | PlaywrightのE2Eテストでは、このスキルのページ構造を使用します |
acceptance-testing | UIの受け入れ基準に基づいてコンポーネントをテストします |
主要な原則
- TypeScript の厳格モードを使用し、
anyは使わない(unknownと型ガードを使用する) - 継承よりもコンポジションを優先する
- テスト、スタイル、ストーリーはコンポーネントと同じ場所に配置する
- デフォルトではサーバーコンポーネントを使用し、クライアントコンポーネントは必要な場合にのみ使用する
- ルートと機能の境界にエラーバウンダリーを設ける
- アクセシビリティは必須(axe-core でテストする)
スキルの種類
FLEXIBLE — コンポーネントアーキテクチャと状態管理は、既存プロジェクトの規約に合わせて調整してください。3 フェーズのワークフローを強く推奨します。テストカバレッジは 85% を超える必要があります。TypeScript の厳格モードは必須です。