senior-frontend について
senior-frontendは、ReactとNext.jsの開発者向けのフロントエンド開発スキルです。プロジェクトやコンポーネントの生成に加え、バンドル分析、最適化、アクセシビリティ確認、テストの指針を扱います。
主な機能
- 01ReactとNext.jsのプロジェクト生成
- 02TypeScriptコンポーネントとフックの生成
- 03バンドル分析と改善提案
- 04ReactとNext.jsのパターン指針
- 05アクセシビリティとテストのチェックリスト
活用シーン
- 01ReactまたはNext.jsアプリのひな形作成
- 02テストやStorybook付きコンポーネントの生成
- 03プロジェクト内の大きな依存関係の特定
- 04アクセシビリティとフロントエンドテストの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill senior-frontendインストール後、Claude Code で/senior-frontendを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/frontend_best_practices.mdnextjs_optimization_guide.mdreact_patterns.mdscripts/component_generator.pybundle_analyzer.pyfrontend_scaffolder.py詳細
シニアフロントエンド
React/Next.js アプリケーション向けのフロントエンド開発パターン、パフォーマンス最適化、自動化ツールです。
使用する場面
- TypeScript と Tailwind CSS を使用して新しい React または Next.js プロジェクトをスキャフォールディングするときに使用します。
- 新しいコンポーネントやカスタムフックを生成するときに使用します。
- フロントエンドアプリケーションのバンドルサイズを分析し、最適化するときに使用します。
- Compound Components や Render Props などの高度な React パターンを実装またはレビューするときに使用します。
- アクセシビリティへの準拠を確保し、堅牢なテスト戦略を実装するときに使用します。
目次
プロジェクトのスキャフォールディング
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 -
推奨される修正を適用し、サイズの大きい依存関係を置き換えます。
-
verbose モードで再実行し、インポートパターンを確認します。
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 | ツリーシェイキング対応の 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);
レンダープロップ
レンダリングロジックを共有します。
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: [{ 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
制限事項
- 上記で説明されている範囲にタスクが明確に該当する場合にのみ、このスキルを使用してください。
- 出力を、環境固有の検証、テスト、または専門家によるレビューの代わりとして扱わないでください。
- 必要な入力、権限、安全上の境界、または成功基準が不足している場合は、作業を中断して確認を求めてください。
スナップショットに関する制限事項
例は抜粋であり、本番環境での認証を保証するものではありません。スキャフォルダーの依存関係は、過去の Next14/React18/Tailwind3 を対象としており、インストール前にサポート対象でパッチ適用済みの依存関係プランが必要です。オプションのスキャフォールド機能テンプレートは未完成のため、現在は生成が拒否されます。生成される React/Vite の設定には、対応するエイリアス、TSX 設定、lint 設定が必要です。認証にはフロントエンドの HOC ではなく、サーバー側の強制適用が必要です。コンポーネントとテストは、アプリケーションのインポート、スタイル、フィクスチャを前提としています。バンドルスコアと KB の表は参考用の経験則であり、実測値でも、パッケージの自動置換の根拠でもありません。
クライアントコンポーネントはサーバーで事前レンダリングされる場合があります。localStorage の初期化子はハイドレーションの不一致を引き起こす可能性があるため、マウント後にローディング境界を設けて読み込み、エラーやキーの変更を処理してください。useAsync/DataFetcher の例には、キャンセル、古いレスポンスへの対処、エラー処理が必要です。カスタムのセレクト/ダイアログの例では、完全なキーボード操作、フォーカスの復元、背景の inert 化が省略されています。テスト済みのプリミティブを優先してください。ベアラートークンを localStorage に保存せず、ログアウト時にセッションデータを消去し、API ルートで認可を強制してください。ユーザーまたはテナントごとにスコープを設定したキーを使わずに、ユーザー固有のキャッシュデータを共有しないでください。params、fetch のキャッシュ、revalidateTag、Image のプリロード、動的 SSR API を、インストールされている Next のバージョンに合わせてください。