senior-frontend について
senior-frontendは、ReactおよびNext.jsプロジェクト向けのフロントエンド開発スキルです。プロジェクトのスキャフォールディング、コンポーネント生成、バンドル分析、実装指針を扱い、制約と測定可能な性能・アクセシビリティ目標に基づいて判断します。
主な機能
- 01Next.jsとReactプロジェクトの作成
- 02コンポーネント、フック、テスト、ストーリーの生成
- 03バンドルサイズと依存関係の分析
- 04ReactとNext.jsの実装ガイド
- 05プロジェクト制約に応じたプロファイル選択
活用シーン
- 01TypeScriptフロントエンドプロジェクトの作成
- 02テスト付き再利用可能コンポーネントの生成
- 03バンドル内の大きな依存関係の特定
- 04レンダリング方式と性能目標の選定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ alirezarezvani/ claude-skills --skill senior-frontendインストール後、Claude Code で/senior-frontendを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
profiles/astro-or-static.jsonnext-app-router.jsonremix-or-sveltekit.jsonvite-spa.jsonreferences/composition_map.mdforcing_questions.mdfrontend_best_practices.mdnextjs_optimization_guide.mdreact_patterns.mdscripts/bundle_analyzer.pycomponent_generator.pyfrontend_decision_engine.pyfrontend_scaffolder.py詳細
シニアフロントエンド
React/Next.js アプリケーションのフロントエンド開発パターン、パフォーマンス最適化、自動化ツールです。
目次
プロジェクトのスキャフォールディング
TypeScript、Tailwind CSS、ベストプラクティスに基づく設定を備えた Next.js または React プロジェクトを新規作成します。
ワークフロー:新しいフロントエンドプロジェクトの作成
-
プロジェクト名とテンプレートを指定してスキャフォルダーを実行します:
python scripts/frontend_scaffolder.py my-app --template nextjs -
オプション機能(認証、API、フォーム、テスト、Storybook)を追加します:
python scripts/frontend_scaffolder.py dashboard --template nextjs --features auth,api -
プロジェクトに移動して、依存関係をインストールします:
cd my-app && npm install -
開発サーバーを起動します:
npm run dev
スキャフォルダーのオプション
| オプション | 説明 |
|---|---|
--template nextjs | App Router と Server Components を備えた Next.js 14 以降 |
--template react | TypeScript を使用する React + Vite |
--features auth | NextAuth.js 認証を追加します |
--features api | React Query + API クライアントを追加します |
--features forms | React Hook Form + Zod バリデーションを追加します |
--features testing | Vitest + Testing Library を追加します |
--dry-run | ファイルを作成せずにプレビューします |
生成される構成(Next.js)
my-app/
├── app/
│ ├── layout.tsx # Root layout with fonts
│ ├── page.tsx # Home page
│ ├── globals.css # Tailwind + CSS variables
│ └── api/health/route.ts
├── components/
│ ├── ui/ # Button, Input, Card
│ └── layout/ # Header, Footer, Sidebar
├── hooks/ # useDebounce, useLocalStorage
├── lib/ # utils (cn), constants
├── types/ # TypeScript interfaces
├── tailwind.config.ts
├── next.config.js
└── package.json
コンポーネントの生成
TypeScript、テスト、Storybook ストーリーを備えた React コンポーネントを生成します。
ワークフロー:新しいコンポーネントの作成
-
クライアントコンポーネントを生成します:
python scripts/component_generator.py Button --dir src/components/ui -
サーバーコンポーネントを生成します:
python scripts/component_generator.py ProductCard --type server -
テストファイルとストーリーファイルを含めて生成します:
python scripts/component_generator.py UserProfile --with-test --with-story -
カスタムフックを生成します:
python scripts/component_generator.py FormValidation --type hook
ジェネレーターのオプション
| オプション | 説明 |
|---|---|
--type client | 'use client' を指定したクライアントコンポーネント(デフォルト) |
--type server | 非同期サーバーコンポーネント |
--type hook | カスタム React フック |
--with-test | テストファイルを含めます |
--with-story | Storybook ストーリーを含めます |
--flat | サブディレクトリを作成せず、出力ディレクトリに作成します |
--dry-run | ファイルを作成せずにプレビューします |
生成されるコンポーネントの例
'use client';
import { useState } from 'react';
import { cn } from '@/lib/utils';
interface ButtonProps {
className?: string;
children?: React.ReactNode;
}
export function Button({ className, children }: ButtonProps) {
return (
<div className={cn('', className)}>
{children}
</div>
);
}
バンドルの分析
package.json とプロジェクト構成を分析し、バンドル最適化の機会を特定します。
ワークフロー:バンドルサイズの最適化
-
プロジェクトでアナライザーを実行します:
python scripts/bundle_analyzer.py /path/to/project -
ヘルススコアと問題を確認します:
Bundle Health Score: 75/100 (C) HEAVY DEPENDENCIES: moment (290KB) Alternative: date-fns (12KB) or dayjs (2KB) lodash (71KB) Alternative: lodash-es with tree-shaking -
重い依存関係を置き換えて、推奨される修正を適用します。
-
詳細モードで再実行し、インポートパターンを確認します:
python scripts/bundle_analyzer.py . --verbose
バンドルスコアの見方
| スコア | 評価 | 対応 |
|---|---|---|
| 90-100 | A | バンドルは十分に最適化されています |
| 80-89 | B | 小規模な最適化が可能です |
| 70-79 | C | 重い依存関係を置き換えてください |
| 60-69 | D | 複数の問題への対処が必要です |
| 0-59 | F | バンドルサイズに重大な問題があります |
検出される重い依存関係
アナライザーは、次のような一般的なサイズの大きいパッケージを特定します:
| パッケージ | サイズ | 代替 |
|---|---|---|
| moment | 290KB | date-fns(12KB)または dayjs(2KB) |
| lodash | 71KB | tree-shaking に対応した lodash-es |
| axios | 14KB | ネイティブの fetch または ky(3KB) |
| jquery | 87KB | ネイティブの DOM API |
| @mui/material | 大 | shadcn/ui または Radix UI |
React のパターン
参照:references/react_patterns.md
複合コンポーネント
関連するコンポーネント間で状態を共有します:
const Tabs = ({ children }) => {
const [active, setActive] = useState(0);
return (
<TabsContext.Provider value={{ active, setActive }}>
{children}
</TabsContext.Provider>
);
};
Tabs.List = TabList;
Tabs.Panel = TabPanel;
// Usage
<Tabs>
<Tabs.List>
<Tabs.Tab>One</Tabs.Tab>
<Tabs.Tab>Two</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>Content 1</Tabs.Panel>
<Tabs.Panel>Content 2</Tabs.Panel>
</Tabs>
カスタムフック
再利用可能なロジックを切り出します:
function useDebounce<T>(value: T, delay = 500): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Usage
const debouncedSearch = useDebounce(searchTerm, 300);
Render Props
レンダリングロジックを共有します:
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url).then(r => r.json()).then(setData).finally(() => setLoading(false));
}, [url]);
return render({ data, loading });
}
// Usage
<DataFetcher
url="/api/users"
render={({ data, loading }) =>
loading ? <Spinner /> : <UserList users={data} />
}
/>
Next.js の最適化
参照:references/nextjs_optimization_guide.md
サーバーコンポーネントとクライアントコンポーネント
デフォルトではサーバーコンポーネントを使用してください。次の場合にのみ 'use client' を追加します:
- イベントハンドラー(onClick、onChange)
- 状態(useState、useReducer)
- エフェクト(useEffect)
- ブラウザー API
// Server Component (default) - no 'use client'
async function ProductPage({ params }) {
const product = await getProduct(params.id); // Server-side fetch
return (
<div>
<h1>{product.name}</h1>
<AddToCartButton productId={product.id} /> {/* Client component */}
</div>
);
}
// Client Component
'use client';
function AddToCartButton({ productId }) {
const [adding, setAdding] = useState(false);
return <button onClick={() => addToCart(productId)}>Add</button>;
}
画像の最適化
import Image from 'next/image';
// Above the fold - load immediately
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority
/>
// Responsive image with fill
<div className="relative aspect-video">
<Image
src="/product.jpg"
alt="Product"
fill
sizes="(max-width: 768px) 100vw, 50vw"
className="object-cover"
/>
</div>
データ取得パターン
// Parallel fetching
async function Dashboard() {
const [user, stats] = await Promise.all([
getUser(),
getStats()
]);
return <div>...</div>;
}
// Streaming with Suspense
async function ProductPage({ params }) {
return (
<div>
<ProductDetails id={params.id} />
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews productId={params.id} />
</Suspense>
</div>
);
}
アクセシビリティとテスト
参照:references/frontend_best_practices.md
アクセシビリティのチェックリスト
- セマンティック HTML:適切な要素(
<button>、<nav>、<main>)を使用します - キーボードナビゲーション:すべてのインタラクティブな要素にフォーカスできるようにします
- ARIA ラベル:アイコンや複雑なウィジェットにラベルを付けます
- 色のコントラスト:通常のテキストでは最低 4.5:1 にします
- フォーカスインジケーター:フォーカス状態を視認できるようにします
// Accessible button
<button
type="button"
aria-label="Close dialog"
onClick={onClose}
className="focus-visible:ring-2 focus-visible:ring-blue-500"
>
<XIcon aria-hidden="true" />
</button>
// Skip link for keyboard users
<a href="#main-content" className="sr-only focus:not-sr-only">
Skip to main content
</a>
テスト戦略
// Component test with React Testing Library
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('button triggers action on click', async () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click me</Button>);
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
// Test accessibility
test('dialog is accessible', async () => {
render(<Dialog open={true} title="Confirm" />);
expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(screen.getByRole('dialog')).toHaveAttribute('aria-labelledby');
});
クイックリファレンス
Next.js の一般的な設定
// next.config.js
const nextConfig = {
images: {
remotePatterns: [{ protocol: 'https', hostname: 'cdn.example.com' }],
formats: ['image/avif', 'image/webp'],
},
experimental: {
optimizePackageImports: ['lucide-react', '@heroicons/react'],
},
};
Tailwind CSS ユーティリティ
// Conditional classes with cn()
import { cn } from '@/lib/utils';
<button className={cn(
'px-4 py-2 rounded',
variant === 'primary' && 'bg-blue-500 text-white',
disabled && 'opacity-50 cursor-not-allowed'
)} />
TypeScript のパターン
// Props with children
interface CardProps {
className?: string;
children: React.ReactNode;
}
// Generic component
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}
リソース
- React のパターン:
references/react_patterns.md - Next.js の最適化:
references/nextjs_optimization_guide.md - ベストプラクティス:
references/frontend_best_practices.md - 強制質問ライブラリ(Matt Pocock の厳しい質問):
references/forcing_questions.md - コンポジションマップ(どのスペシャリストに処理を委ねるか):
references/composition_map.md
前提条件と検証可能な成功基準(Karpathy の規律)
このスキルがコンポーネントのひな形を作成したり、フレームワークを推奨したり、バンドルを監査したりする前に、次の4つの前提条件を必ず明らかにしてください。
- 主なユーザーのデバイスとネットワーク — mobile-4G、desktop-fiber、low-end-Android、corporate-network のいずれかです。すべてのパフォーマンス上の判断に影響します。
- LCP の目標値(ミリ秒) — 「速い」ではなく、単一の数値で指定します。バンドルの予算とレンダリング方式に影響します。
- SEO に依存するか、認証で保護されているか — レンダリング方式(SSR/SSG/RSC と SPA)の選択に影響します。
- WCAG の目標と担当するアクセシビリティ責任者 — AA、AAA、またはベストエフォートです。アクセシビリティへの投資と CI ゲートに影響します。
検証可能な成功基準(Karpathy #4)— すべての推奨事項に次の項目を含める必要があります。
- 主なデバイスでの p75 における Core Web Vitals の目標値(LCP、INP、CLS)
- ルートごとの JS バンドル予算(KB-gzip)
- Lighthouse のアクセシビリティ下限値とパフォーマンス下限値
この3項目のいずれかが記載されていない場合、推奨事項は不完全です。強制質問ライブラリの Q2 に戻ってください。
スクリプト/frontend_decision_engine.py ツールはこれらのチェックを実行します。4つの前提条件が入力されていない場合はプロファイルの推奨を拒否し、該当するプロファイルの検証可能な閾値を表示します。
カスタマイズプロファイル
組み込みの4つのプロファイル(profiles/)が、すべての推奨事項の基準を設定します。
| プロファイル | 選択する場合 | LCP の目標値(mobile-4G p75) | バンドル予算 |
|---|---|---|---|
next-app-router | SaaS の顧客向け、SEO + 動的、RSC 優先 | 2000ms | 150 KB-gzip / ルート |
remix-or-sveltekit | mobile-4G が主な環境、低 JS 優先、プログレッシブエンハンスメント | 1500ms | 80 KB-gzip / ルート |
vite-spa | 認証で保護されたアプリ、デスクトップ/企業ネットワークが主な環境 | 2500ms | 初期表示で 200 KB + 80 KB / ルート |
astro-or-static | マーケティング/ドキュメント/ブログ、書き込みがほぼない、SEO が重要 | 1200ms | 30 KB JS / ページ |
次のようにプロファイルを選択します。
python scripts/frontend_decision_engine.py \
--primary-device mobile-4g --lcp-target-ms 2000 \
--seo-dependent true --auth-walled false --team-size 5
このツールは、最適なプロファイル、次点のプロファイルとのトレードオフ(差が 15% 以内の場合)、スタックの選択肢、そのプロファイルで避けるべきアンチパターン、必須の CI ゲートを返します。
カスタムプロファイルを追加するには(たとえば、組織の内部ツール向けのデフォルトを追加する場合)、profiles/vite-spa.json を profiles/<your-org>.json にコピーし、constraints と success_thresholds を調整します。
コンポジションマップ
このスキルは、POWERFUL ティアのスペシャリストが担当する範囲を再実装しません。該当するスペシャリストに処理を委ねます。ルーティングテーブルの全容は references/composition_map.md を参照してください。主な委譲先は次のとおりです。
| 対象 | 委譲先 |
|---|---|
| WCAG 監査、コントラスト、スクリーンリーダー | engineering-team/skills/a11y-audit/ |
| バンドルのプロファイリング + ランタイムのパフォーマンス | engineering/skills/performance-profiler/ |
| 映画のような演出/スクロールストーリーテリングを用いたランディングページ | engineering-team/skills/epic-design/ |
| Apple HIG(iOS / macOS / visionOS) | product-team/skills/apple-hig-expert/ |
| コミット前の Karpathy レビュー | engineering/karpathy-coder/ |
| 着手前のアーキテクチャに関する厳しい質問 | engineering/grill-me/ |
cs-frontend-engineer エージェントは、context: fork を介してこれらの委譲先を調整します。別のエージェントから Agent({subagent_type: "cs-frontend-engineer", prompt: "..."}) を使って呼び出すか、/cs-frontend-review <your problem> を実行してください。
強制質問ライブラリ(Matt Pocock の詰問)
フレームワークやレンダリング方式を決定する前に、references/forcing_questions.md の7つの強制質問に順番に答えてください。次のルールを守ります。
- 1回につき質問は1つです。まとめて質問しないでください。
- 根拠となる情報源を引用し、必ず回答を推奨してください。
- 回答を
/tmp/frontend-grill-<date>.mdに記録してください。 - 中止基準に該当したら、そこで停止してください。未解決の課題を残したまま足場を構築しないでください。
- Q7の後に、7つの回答を使って
frontend_decision_engine.pyを実行してください。
概要:
- 主なデバイスとネットワークは何ですか?
- LCPの目標値(ミリ秒)とINP、CLSは何ですか?
- RSC/SPA/SSR/SSGのどれを選びますか?選択の根拠は何ですか?
- ルートごとのJSバンドル予算はいくらですか?
- SEOが必要ですか、それとも認証で保護されますか?
- デザインシステムの信頼できる情報源は何ですか?
- WCAGの目標と、名前が明示されたa11yの担当者は誰ですか?
他のエージェントやスキルからの呼び出し
呼び出し方法は3つあります。
- スラッシュコマンド:
/cs-frontend-review <prompt>— 強制質問、意思決定エンジン、構成ルーティングをすべて実行します。 - エージェントのサブエージェント:
Agent({subagent_type: "cs-frontend-engineer", prompt: "..."})— コンテキストを分岐し、200語以内の要約を返します。 - ツールの直接呼び出し:
python scripts/frontend_decision_engine.py ...— 入力が既知の場合に、決定論的にプロファイルを照合します。
呼び出しの詳細な仕様については、agents/engineering/cs-frontend-engineer.md を参照してください。