commit f6a781c47e810c7a4badac5ed3f03f6c2e276992 Author: snc777 Date: Sat May 9 14:24:17 2026 +0900 initial commit Co-authored-by: Cursor diff --git a/.claude/skills/designing-workspace-ui/SKILL.md b/.claude/skills/designing-workspace-ui/SKILL.md new file mode 100644 index 0000000..74ba4dc --- /dev/null +++ b/.claude/skills/designing-workspace-ui/SKILL.md @@ -0,0 +1,223 @@ +--- +name: designing-workspace-ui +description: 配布物/workspace-ui-kit のワークスペース UI 作業(ペイン修正・コンポーネント追加・色や角丸や余白の変更・レイアウト調整・情報設計の検討・JSX の編集)で、shadcn idiom に準拠したコード生成とデザイン規律を強制するスキル。「Pane を変えたい」「ボタンを追加したい」「色を変えたい」「コンポーネントを作りたい」「workspace の見た目を直したい」「余白を直したい」「フォームを追加したい」「レイアウトを調整したい」と依頼された際に使用する。次の場合は使用しない: components/ 配下を編集しない単純な質問・タイポ修正・README やドキュメントのみの編集・openspec/ の ADR 起草のみ・テストコードのみの修正・依存パッケージ更新。 +--- + +# Designing Workspace UI + +`配布物/workspace-ui-kit` で UI を編集する際は、以下の2軸を必ず両方適用する: + +1. **shadcn idiom に従ってコード生成する** — base UI / semantic token / 親管理の余白 +2. **SSoT を経由しないデザイン変更を拒否する** — トークン・部品・パターン・情報設計の不足は4分岐で診断し、ユーザーに確認する + +CLAUDE.md の「コード生成ルール」を常時適用の禁止事項として参照する。本スキルはその詳細根拠とプロジェクト固有規律を提供する。 + +## 依存 + +このスキルが参照する SSoT: + +- `app/globals.css` — デザイントークン(`@theme` 内の CSS 変数) +- `components.json` — shadcn 設定(base / style / iconLibrary / alias。ファイルを読んで確認する) +- `components/ui/` — shadcn 素体(編集可能。方針は後述) +- `components/primitives/` — プロジェクト固有プリミティブ(`ls` して確認する。名前はハードコードしない) +- `openspec/decision/` — ADR(ペイン責務・配色・角丸等。番号はハードコードせず `ls` で最新を確認する) + +前提: shadcn スキル(`.claude/skills/shadcn/`)がインストール済みであること。ただしこのスキルから shadcn スキルを読みに行く指示は出さない。必要なルールは自己完結で持つ。 + +## コード生成ルール + +CLAUDE.md の禁止事項の根拠と、このプロジェクトでの適用方法を示す。 + +### 中心の禁止事項 + +| 禁止 | 正しい方法 | なぜ | +| ---------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------ | +| `space-y-*` / `space-x-*` | `flex flex-col gap-*` / `flex gap-*` | 子要素が条件で消えたとき余白が崩れる。親が管理すれば子が何個でも安定 | +| `className` で色・フォントサイズ・フォントウェイトを上書き | variant / semantic token / CSS 変数 | コピペが蔓延し、ダークモードで対応漏れが起きる。部品側にサイズ展開を用意する | +| 生の色クラス(`bg-blue-500` 等) | `bg-primary` 等の semantic token | テーマ変更時に全ファイル書き換えになる。名前で指定すれば定義 1箇所で済む | +| `w-N h-N`(正方形時) | `size-N` | 「正方形にしたい」意図が明確になる | +| `asChild`(radix の API) | `render`(base の API) | このプロジェクトは base を使用(`components.json` で確認)。radix の書き方は動かない | +| shadcn 部品がある場面で自前の div | `Badge` / `Separator` / `Skeleton` 等 | a11y・ダークモード・テーマ連動が組み込み済み。自作すると全部欠落する | + +このプロジェクトで最も誤りやすいパターン(**必ず暗記**): + +```tsx +// Incorrect — radix の書き方は base では動かない + + + + +// Correct — base の API +}> + 設定を開く + +``` + +その他のパターン(スペーシング・タイポグラフィ・セマンティックカラー・アイコン・コンポジション・フォーム)は [references/coding-rules.md](references/coding-rules.md) を参照。 + +### components/ui/ の編集方針 + +shadcn の Open Code 思想に基づき、`components/ui/` は**プロジェクトのコード**として編集してよい。ただし方法に規律がある。 + +**OK**: + +- 部品に新しい variant を追加する(例: `CardTitle` に `data-size="sm"` を追加) +- 部品の間隔やスタイルを構造的に変更する +- 新しい CSS 変数を `globals.css` に定義して部品から参照する + +**NG**: + +- 呼び出し側で `className` を使って見た目を毎回打ち消す +- 部品ファイルを丸ごとコピーして別名で作る + +部品を編集した後、公式の更新を取り込みたいときは `npx shadcn@latest add --diff ` で差分を確認してマージする。 + +### 出力後セルフレビュー + +コードを出力した後、以下の 3点をセルフレビューし、違反があれば修正版を出す: + +1. **shadcn の書き方**: 間隔は親管理(`gap-*`)か? 色は名前指定(semantic token)か? 部品の見た目を `className` で上書きしていないか? +2. **base 準拠**: `asChild` ではなく `render` を使っているか? +3. **プロジェクトトークン**: `globals.css` の既存トークンで表現できるか? + +## SSoT エスカレーション規律 + +このプロジェクトは教材。受講生が「自分の思想を画面にする」ことがゴール。AI が独断でデザインを決めると受講生の学習機会を奪う。 + +**SSoT を経由しない UI 変更は禁止。** 既存 SSoT で作れないものが出てきたら、以下の 4分岐で診断し、必ずユーザーに聞く。 + +### 作業前の確認(必ず実行) + +以下のチェックリストを応答にそのままコピーし、各項目を完了したら `[x]` に置き換えながら進む。スキップは禁止。 + +``` +SSoT 把握: +- [ ] app/globals.css を読み、既存の semantic token を列挙した +- [ ] components/ui/ を ls し、利用可能な shadcn 部品を列挙した +- [ ] components/primitives/ を ls し、Inline* 等のプロジェクト固有部品を列挙した +- [ ] openspec/decision/ を ls し、関連する ADR を読了した +``` + +このチェックが全て埋まる前にコード生成を開始してはならない。 + +### 4 分岐の決定木 + +既存 SSoT で作れる → そのまま実装。作れない → 以下に進む。 + +**いずれの分岐でも、ユーザーの判断なしに実装に進んではならない。** + +#### 3a. トークンの穴 + +色・余白・角丸・影・フォント等の値が既存トークンで足りない。 + +やること: 何が足りないかを 1行で説明 → `@theme` への追加案を提示 → ユーザーの判断を仰ぐ + +やってはいけないこと: 生の色クラスの使用 / 「一時的に hardcoded で後で直す」式の仮対応 / ユーザーに聞かず `@theme` を変更 + +#### 3b. 部品の穴 + +既存部品で表現できない形・variant が必要。 + +やること: 既存部品の variant / size で代替できないか先に試す → 不可能なら新 variant または新プリミティブを起案 → ユーザーの判断を仰ぐ + +やってはいけないこと: 部品ファイルを fork コピー / 呼び出し側で className の上書きで済ませる + +#### 3c. パターンの穴 + +レイアウトパターン・複数部品の組み合わせ規則・状態(empty / error / loading)の規律がない。 + +やること: 既存 ADR を読み、本当に規律がないことを確認 → 規律案を起案 → ユーザーの判断を仰ぐ + +やってはいけないこと: 「とりあえず置いてみる」で恒久パターンを既成事実化 + +#### 3d. 情報設計の穴 + +ペインに何を載せるか・並び順・情報の増減の規律がない。 + +やること: **コードを書かない**。何を載せるか・優先順位・並び順を質問する → ユーザーが決めてから実装 + +やってはいけないこと: 「採用管理ならこれが普通だろう」と独断で情報を足す / 削る / 並び替える + +### エスカレーションのテンプレート + +``` +[診断] +何が足りない: <1 行説明> +分岐: 3a / 3b / 3c / 3d のどれか + +[根拠] +Refactoring UI の <軸> / ADR の <規律> / shadcn idiom 上の <理由> + +[提案] +案1: <内容>(メリット / デメリット) +案2: <内容>(メリット / デメリット) + +[ユーザーへの質問] +どちらで進めますか?別案ありますか? +``` + +### 評価軸(Refactoring UI) + +エスカレーション時の根拠に使う: + +- Hierarchy(階層)/ Spacing(余白)/ Typography(タイポ)/ Color(色)/ Depth(影)/ Polish(空状態・エラー状態・ローディング状態・微調整) + +## ペイン編集導線 + +### Pane 3 と Pane 4 の責務分離 + +**「Pane 3 = 読む場所、Pane 4 = 編集の本拠地」**。詳細は `openspec/decision/` 配下の最新 ADR を確認する。 + +- Pane 3 のカードに inline 編集 UI(input・textarea・編集ボタン)を新たに置かない +- 編集機能を足したい時は Pane 4 側に置く +- 「Pane 3 にも編集を持たせたい」と要望が来たら、まず原則との衝突を伝え、本当に必要かを問い直す + +### インライン編集プリミティブの再利用 + +`components/primitives/` の既存プリミティブを再利用する(`ls` して名前を確認すること)。新しい編集 UI のスタイル流派を独自に増やさない。 + +導入禁止の編集 UI: 鉛筆アイコン式 / 「編集」ボタン式 / フォーム・モーダル一括編集式 + +### 入力保存規約 + +- 1 行 `input`: Enter で保存 / Esc でキャンセル / blur で保存 +- 複数行 `textarea`: **Cmd+Enter で保存**(Enter は改行)/ Esc でキャンセル / blur で保存 +- ホバー時のみ薄い枠を出す。常時の枠は出さない + +### 担当の用語分離 + +候補者単位とステージ単位で用語を使い分ける。混ぜない。 + +- 候補者単位: 「採用担当」 +- ステージ単位: 「面接官」(書類選考は「審査担当」) + +## 角丸(border-radius)の階層ルール + +`rounded-*` クラスの選択に迷ったとき、以下の 4 ルールで判断する。役割 × R クラスの対応表は `openspec/changes/add-4pane-workspace-template/mockup-guide.md` の角丸セクションを参照。 + +### ルール 1: 親 R ≧ 子 R + +親の箱に当てた R より大きな R を、その中の要素に付けない。 + +### ルール 2: 同格の島は同じ R + +`bg-card` で囲った「島」は原則 `rounded-lg` に揃える。`Card` 本体だけは `rounded-xl` の例外。 + +### ルール 3: 厳密な同心までは求めない + +Tailwind のスケール(`xl/lg/md/sm`)で段差が視覚的に分かれば十分。 + +### ルール 4: 高密度 UI での例外 + +行密度を上げるために `bg-card` の島を `rounded-md` に下げてよい。ただし子要素には `rounded-md` より大きな R を付けない(ルール 1 を維持)。 + +## React 19 ルール + +### Effect 内同期 setState 禁止 + +`useEffect` の中で同期的に `setState` を呼んで初期値を入れ直すパターンは禁止。 + +代替パターン: + +- 親側で `key` を変更して子コンポーネントを再マウントし、`useState(initialValue)` で初期化する +- フォーカス + 全選択は `autoFocus` + `onFocus={(e) => e.target.select()}` で実現する diff --git a/.claude/skills/designing-workspace-ui/references/coding-rules.md b/.claude/skills/designing-workspace-ui/references/coding-rules.md new file mode 100644 index 0000000..a702370 --- /dev/null +++ b/.claude/skills/designing-workspace-ui/references/coding-rules.md @@ -0,0 +1,270 @@ +# コーディングルール詳細 + +このプロジェクト固有の Incorrect/Correct ペアを収録する。shadcn スキルの `rules/` にある汎用例とは異なり、workspace-ui-kit の実コードに基づく固有パターンを扱う。 + +## 目次 + +- base(Base UI)固有のパターン +- スペーシング +- タイポグラフィ +- セマンティックカラー +- アイコン +- コンポジション +- フォーム +- components/ui/ 編集の具体例 + +--- + +## base(Base UI)固有のパターン + +このプロジェクトは base(Base UI)を使用している(`components.json` の `style` フィールドで確認できる)。AI は radix の API をデフォルトで生成しやすいので、特に注意する。 + +**Incorrect:** + +```tsx + + + +``` + +**Correct:** + +```tsx +}>設定を開く +``` + +ポイント: base では `asChild` ではなく `render` を使う。`render` は JSX 要素を受け取り、そのまま描画する。 + +**Incorrect:** + +```tsx + + + +``` + +**Correct:** + +```tsx + + + +``` + +ポイント: base の Select は `items` prop でオプションを渡す方式もある。`components.json` の `base` フィールドを確認して API を選ぶ。 + +--- + +## スペーシング + +**Incorrect — Pane4 のセクション内で子要素に余白を持たせている:** + +```tsx +
+

プロフィール

+
+ ... + ... + {showSource && ...} +
+
+``` + +**Correct — 入れ物が子同士の隙間を管理:** + +```tsx +
+

プロフィール

+
+ ... + ... + {showSource && ...} +
+
+``` + +`showSource` が `false` になったとき、`space-y-3` だと最後の `Row` に余計な `margin-top` が残ることがある。`gap-3` なら親が管理するので子が何個でも崩れない。 + +--- + +## タイポグラフィ + +**Incorrect — 呼び出し側で見た目を毎回打ち消す(コピペ蔓延の原因):** + +```tsx + + 評価サマリ + +``` + +**Correct — 部品側にサイズ展開を用意して、呼び出し側はシンプルに:** + +```tsx +評価サマリ +``` + +`components/ui/card.tsx` に `data-size="sm"` の variant を追加する(components/ui/ 編集の具体例を参照)。 + +**Incorrect — Pane4 セクション見出しを h2 + className で毎回書く:** + +```tsx +

+ 基本情報 +

+``` + +この h2 + className の組み合わせが 11 セクション分コピペされているのが前回の監査で発見された。 + +**Correct — 見出し用のプリミティブを抽出:** + +```tsx +基本情報 +``` + +共通の見出しプリミティブ `` を `components/primitives/` に作り、内部で h2 + スタイルを持たせる。 + +--- + +## セマンティックカラー + +**Incorrect — 生の色クラスで状態を表す:** + +```tsx +通過 +不合格 +``` + +**Correct — semantic token または Badge variant:** + +```tsx +通過 +不合格 +``` + +このプロジェクトのサーフェス階層は `openspec/decision/` の配色 ADR で定義されている。`app/globals.css` の `@theme` セクションと ADR を読み、既存トークンで表現できるか確認する。 + +--- + +## アイコン + +**Incorrect — Button 内のアイコンにサイジングクラスを付ける:** + +```tsx + +``` + +**Correct — 部品が CSS でアイコンサイズを制御:** + +```tsx + +``` + +shadcn の Button は内部でアイコンのサイズを制御している。サイジングクラスを付けると二重制御になり、サイズ変更時に壊れる。 + +正方形のアイコンコンテナには `size-*` を使う: + +```tsx + + + +``` + +--- + +## コンポジション + +**Card は Header / Title / Content のフル構成で使う。** 中身を全部 CardContent に詰めない: + +```tsx + + + 評価サマリ + 最新の面接評価 + + {/* 本文 */} + +``` + +**Dialog / Sheet / Drawer には Title が必須。** 視覚的に不要でも `sr-only` で付ける: + +```tsx + + + 候補者を追加 + {/* 本文 */} + + +``` + +**Avatar には AvatarFallback が必須。** 画像が読み込めなかった場合の代替表示: + +```tsx + + + {candidate.name.charAt(0)} + +``` + +--- + +## フォーム + +shadcn の Forms ルールに従い、`FieldGroup` + `Field` + `FieldLabel` で構成する。生の `div` + `Label` で組まない。 + +```tsx + + + 氏名 + + + + メールアドレス + + + +``` + +2〜7 択の選択肢には `ToggleGroup` を使う。`Button` をループして独自の active state を管理しない。 + +--- + +## components/ui/ 編集の具体例 + +### variant を追加する手順 + +例: `CardTitle` に `data-size="sm"` variant を追加する。 + +`components/ui/card.tsx` を開き、`CardTitle` の定義に data 属性によるスタイル切替を追加: + +```tsx +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} +``` + +呼び出し側: + +```tsx +評価サマリ +``` + +### upstream の更新を取り込む手順 + +1. `npx shadcn@latest add card --dry-run` で影響範囲を確認 +2. `npx shadcn@latest add card --diff card.tsx` で自分の変更と公式の変更を比較 +3. 差分を見て、自分の variant 追加を保持しつつ公式の修正を取り込む +4. `--overwrite` はユーザーの明示的な承認なしに使わない diff --git a/.claude/skills/next-best-practices/SKILL.md b/.claude/skills/next-best-practices/SKILL.md new file mode 100644 index 0000000..437896b --- /dev/null +++ b/.claude/skills/next-best-practices/SKILL.md @@ -0,0 +1,153 @@ +--- +name: next-best-practices +description: Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling +user-invocable: false +--- + +# Next.js Best Practices + +Apply these rules when writing or reviewing Next.js code. + +## File Conventions + +See [file-conventions.md](./file-conventions.md) for: +- Project structure and special files +- Route segments (dynamic, catch-all, groups) +- Parallel and intercepting routes +- Middleware rename in v16 (middleware → proxy) + +## RSC Boundaries + +Detect invalid React Server Component patterns. + +See [rsc-boundaries.md](./rsc-boundaries.md) for: +- Async client component detection (invalid) +- Non-serializable props detection +- Server Action exceptions + +## Async Patterns + +Next.js 15+ async API changes. + +See [async-patterns.md](./async-patterns.md) for: +- Async `params` and `searchParams` +- Async `cookies()` and `headers()` +- Migration codemod + +## Runtime Selection + +See [runtime-selection.md](./runtime-selection.md) for: +- Default to Node.js runtime +- When Edge runtime is appropriate + +## Directives + +See [directives.md](./directives.md) for: +- `'use client'`, `'use server'` (React) +- `'use cache'` (Next.js) + +## Functions + +See [functions.md](./functions.md) for: +- Navigation hooks: `useRouter`, `usePathname`, `useSearchParams`, `useParams` +- Server functions: `cookies`, `headers`, `draftMode`, `after` +- Generate functions: `generateStaticParams`, `generateMetadata` + +## Error Handling + +See [error-handling.md](./error-handling.md) for: +- `error.tsx`, `global-error.tsx`, `not-found.tsx` +- `redirect`, `permanentRedirect`, `notFound` +- `forbidden`, `unauthorized` (auth errors) +- `unstable_rethrow` for catch blocks + +## Data Patterns + +See [data-patterns.md](./data-patterns.md) for: +- Server Components vs Server Actions vs Route Handlers +- Avoiding data waterfalls (`Promise.all`, Suspense, preload) +- Client component data fetching + +## Route Handlers + +See [route-handlers.md](./route-handlers.md) for: +- `route.ts` basics +- GET handler conflicts with `page.tsx` +- Environment behavior (no React DOM) +- When to use vs Server Actions + +## Metadata & OG Images + +See [metadata.md](./metadata.md) for: +- Static and dynamic metadata +- `generateMetadata` function +- OG image generation with `next/og` +- File-based metadata conventions + +## Image Optimization + +See [image.md](./image.md) for: +- Always use `next/image` over `` +- Remote images configuration +- Responsive `sizes` attribute +- Blur placeholders +- Priority loading for LCP + +## Font Optimization + +See [font.md](./font.md) for: +- `next/font` setup +- Google Fonts, local fonts +- Tailwind CSS integration +- Preloading subsets + +## Bundling + +See [bundling.md](./bundling.md) for: +- Server-incompatible packages +- CSS imports (not link tags) +- Polyfills (already included) +- ESM/CommonJS issues +- Bundle analysis + +## Scripts + +See [scripts.md](./scripts.md) for: +- `next/script` vs native script tags +- Inline scripts need `id` +- Loading strategies +- Google Analytics with `@next/third-parties` + +## Hydration Errors + +See [hydration-error.md](./hydration-error.md) for: +- Common causes (browser APIs, dates, invalid HTML) +- Debugging with error overlay +- Fixes for each cause + +## Suspense Boundaries + +See [suspense-boundaries.md](./suspense-boundaries.md) for: +- CSR bailout with `useSearchParams` and `usePathname` +- Which hooks require Suspense boundaries + +## Parallel & Intercepting Routes + +See [parallel-routes.md](./parallel-routes.md) for: +- Modal patterns with `@slot` and `(.)` interceptors +- `default.tsx` for fallbacks +- Closing modals correctly with `router.back()` + +## Self-Hosting + +See [self-hosting.md](./self-hosting.md) for: +- `output: 'standalone'` for Docker +- Cache handlers for multi-instance ISR +- What works vs needs extra setup + +## Debug Tricks + +See [debug-tricks.md](./debug-tricks.md) for: +- MCP endpoint for AI-assisted debugging +- Rebuild specific routes with `--debug-build-paths` + diff --git a/.claude/skills/next-best-practices/async-patterns.md b/.claude/skills/next-best-practices/async-patterns.md new file mode 100644 index 0000000..dce8d8c --- /dev/null +++ b/.claude/skills/next-best-practices/async-patterns.md @@ -0,0 +1,87 @@ +# Async Patterns + +In Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous. + +## Async Params and SearchParams + +Always type them as `Promise<...>` and await them. + +### Pages and Layouts + +```tsx +type Props = { params: Promise<{ slug: string }> } + +export default async function Page({ params }: Props) { + const { slug } = await params +} +``` + +### Route Handlers + +```tsx +export async function GET( + request: Request, + { params }: { params: Promise<{ id: string }> } +) { + const { id } = await params +} +``` + +### SearchParams + +```tsx +type Props = { + params: Promise<{ slug: string }> + searchParams: Promise<{ query?: string }> +} + +export default async function Page({ params, searchParams }: Props) { + const { slug } = await params + const { query } = await searchParams +} +``` + +### Synchronous Components + +Use `React.use()` for non-async components: + +```tsx +import { use } from 'react' + +type Props = { params: Promise<{ slug: string }> } + +export default function Page({ params }: Props) { + const { slug } = use(params) +} +``` + +### generateMetadata + +```tsx +type Props = { params: Promise<{ slug: string }> } + +export async function generateMetadata({ params }: Props): Promise { + const { slug } = await params + return { title: slug } +} +``` + +## Async Cookies and Headers + +```tsx +import { cookies, headers } from 'next/headers' + +export default async function Page() { + const cookieStore = await cookies() + const headersList = await headers() + + const theme = cookieStore.get('theme') + const userAgent = headersList.get('user-agent') +} +``` + +## Migration Codemod + +```bash +npx @next/codemod@latest next-async-request-api . +``` diff --git a/.claude/skills/next-best-practices/bundling.md b/.claude/skills/next-best-practices/bundling.md new file mode 100644 index 0000000..ac5e814 --- /dev/null +++ b/.claude/skills/next-best-practices/bundling.md @@ -0,0 +1,180 @@ +# Bundling + +Fix common bundling issues with third-party packages. + +## Server-Incompatible Packages + +Some packages use browser APIs (`window`, `document`, `localStorage`) and fail in Server Components. + +### Error Signs + +``` +ReferenceError: window is not defined +ReferenceError: document is not defined +ReferenceError: localStorage is not defined +Module not found: Can't resolve 'fs' +``` + +### Solution 1: Mark as Client-Only + +If the package is only needed on client: + +```tsx +// Bad: Fails - package uses window +import SomeChart from 'some-chart-library' + +export default function Page() { + return +} + +// Good: Use dynamic import with ssr: false +import dynamic from 'next/dynamic' + +const SomeChart = dynamic(() => import('some-chart-library'), { + ssr: false, +}) + +export default function Page() { + return +} +``` + +### Solution 2: Externalize from Server Bundle + +For packages that should run on server but have bundling issues: + +```js +// next.config.js +module.exports = { + serverExternalPackages: ['problematic-package'], +} +``` + +Use this for: +- Packages with native bindings (sharp, bcrypt) +- Packages that don't bundle well (some ORMs) +- Packages with circular dependencies + +### Solution 3: Client Component Wrapper + +Wrap the entire usage in a client component: + +```tsx +// components/ChartWrapper.tsx +'use client' + +import { Chart } from 'chart-library' + +export function ChartWrapper(props) { + return +} + +// app/page.tsx (server component) +import { ChartWrapper } from '@/components/ChartWrapper' + +export default function Page() { + return +} +``` + +## CSS Imports + +Import CSS files instead of using `` tags. Next.js handles bundling and optimization. + +```tsx +// Bad: Manual link tag + + +// Good: Import CSS +import './styles.css' + +// Good: CSS Modules +import styles from './Button.module.css' +``` + +## Polyfills + +Next.js includes common polyfills automatically. Don't load redundant ones from polyfill.io or similar CDNs. + +Already included: `Array.from`, `Object.assign`, `Promise`, `fetch`, `Map`, `Set`, `Symbol`, `URLSearchParams`, and 50+ others. + +```tsx +// Bad: Redundant polyfills + + +// Good: Next.js Script component +import Script from 'next/script' + + +``` + +## Don't Put Script in Head + +`next/script` should not be placed inside `next/head`. It handles its own positioning. + +```tsx +// Bad: Script inside Head +import Head from 'next/head' +import Script from 'next/script' + + + + +// Good: Next.js component +import { GoogleAnalytics } from '@next/third-parties/google' + +export default function Layout({ children }) { + return ( + + {children} + + + ) +} +``` + +## Google Tag Manager + +```tsx +import { GoogleTagManager } from '@next/third-parties/google' + +export default function Layout({ children }) { + return ( + + + {children} + + ) +} +``` + +## Other Third-Party Scripts + +```tsx +// YouTube embed +import { YouTubeEmbed } from '@next/third-parties/google' + + + +// Google Maps +import { GoogleMapsEmbed } from '@next/third-parties/google' + + +``` + +## Quick Reference + +| Pattern | Issue | Fix | +|---------|-------|-----| +| `