From f6a781c47e810c7a4badac5ed3f03f6c2e276992 Mon Sep 17 00:00:00 2001 From: snc777 Date: Sat, 9 May 2026 14:24:17 +0900 Subject: [PATCH] initial commit Co-authored-by: Cursor --- .../skills/designing-workspace-ui/SKILL.md | 223 + .../references/coding-rules.md | 270 + .claude/skills/next-best-practices/SKILL.md | 153 + .../next-best-practices/async-patterns.md | 87 + .../skills/next-best-practices/bundling.md | 180 + .../next-best-practices/data-patterns.md | 297 + .../next-best-practices/debug-tricks.md | 105 + .../skills/next-best-practices/directives.md | 73 + .../next-best-practices/error-handling.md | 227 + .../next-best-practices/file-conventions.md | 140 + .claude/skills/next-best-practices/font.md | 245 + .../skills/next-best-practices/functions.md | 108 + .../next-best-practices/hydration-error.md | 91 + .claude/skills/next-best-practices/image.md | 173 + .../skills/next-best-practices/metadata.md | 301 + .../next-best-practices/parallel-routes.md | 287 + .../next-best-practices/route-handlers.md | 146 + .../next-best-practices/rsc-boundaries.md | 159 + .../next-best-practices/runtime-selection.md | 39 + .claude/skills/next-best-practices/scripts.md | 141 + .../next-best-practices/self-hosting.md | 371 + .../suspense-boundaries.md | 67 + .claude/skills/shadcn/SKILL.md | 260 + .claude/skills/shadcn/agents/openai.yml | 5 + .claude/skills/shadcn/assets/shadcn-small.png | Bin 0 -> 1049 bytes .claude/skills/shadcn/assets/shadcn.png | Bin 0 -> 3852 bytes .claude/skills/shadcn/cli.md | 276 + .claude/skills/shadcn/customization.md | 209 + .claude/skills/shadcn/evals/evals.json | 47 + .claude/skills/shadcn/mcp.md | 94 + .claude/skills/shadcn/rules/base-vs-radix.md | 306 + .claude/skills/shadcn/rules/composition.md | 195 + .claude/skills/shadcn/rules/forms.md | 192 + .claude/skills/shadcn/rules/icons.md | 101 + .claude/skills/shadcn/rules/styling.md | 162 + .../vercel-react-best-practices/README.md | 123 + .../vercel-react-best-practices/SKILL.md | 149 + .../vercel-react-best-practices/metadata.json | 15 + .../rules/_sections.md | 46 + .../rules/_template.md | 28 + .../rules/advanced-effect-event-deps.md | 56 + .../rules/advanced-event-handler-refs.md | 55 + .../rules/advanced-init-once.md | 42 + .../rules/advanced-use-latest.md | 39 + .../rules/async-api-routes.md | 38 + .../async-cheap-condition-before-await.md | 37 + .../rules/async-defer-await.md | 82 + .../rules/async-dependencies.md | 51 + .../rules/async-parallel.md | 28 + .../rules/async-suspense-boundaries.md | 99 + .../rules/bundle-analyzable-paths.md | 63 + .../rules/bundle-barrel-imports.md | 60 + .../rules/bundle-conditional.md | 31 + .../rules/bundle-defer-third-party.md | 49 + .../rules/bundle-dynamic-imports.md | 35 + .../rules/bundle-preload.md | 50 + .../rules/client-event-listeners.md | 74 + .../rules/client-localstorage-schema.md | 71 + .../rules/client-passive-event-listeners.md | 48 + .../rules/client-swr-dedup.md | 56 + .../rules/js-batch-dom-css.md | 107 + .../rules/js-cache-function-results.md | 80 + .../rules/js-cache-property-access.md | 28 + .../rules/js-cache-storage.md | 70 + .../rules/js-combine-iterations.md | 32 + .../rules/js-early-exit.md | 50 + .../rules/js-flatmap-filter.md | 60 + .../rules/js-hoist-regexp.md | 45 + .../rules/js-index-maps.md | 37 + .../rules/js-length-check-first.md | 49 + .../rules/js-min-max-loop.md | 82 + .../rules/js-request-idle-callback.md | 105 + .../rules/js-set-map-lookups.md | 24 + .../rules/js-tosorted-immutable.md | 57 + .../rules/rendering-activity.md | 26 + .../rules/rendering-animate-svg-wrapper.md | 47 + .../rules/rendering-conditional-render.md | 40 + .../rules/rendering-content-visibility.md | 38 + .../rules/rendering-hoist-jsx.md | 46 + .../rules/rendering-hydration-no-flicker.md | 82 + .../rendering-hydration-suppress-warning.md | 30 + .../rules/rendering-resource-hints.md | 85 + .../rules/rendering-script-defer-async.md | 68 + .../rules/rendering-svg-precision.md | 28 + .../rules/rendering-usetransition-loading.md | 75 + .../rules/rerender-defer-reads.md | 39 + .../rules/rerender-dependencies.md | 45 + .../rules/rerender-derived-state-no-effect.md | 40 + .../rules/rerender-derived-state.md | 29 + .../rules/rerender-functional-setstate.md | 74 + .../rules/rerender-lazy-state-init.md | 58 + .../rules/rerender-memo-with-default-value.md | 38 + .../rules/rerender-memo.md | 44 + .../rules/rerender-move-effect-to-event.md | 45 + .../rules/rerender-no-inline-components.md | 82 + .../rerender-simple-expression-in-memo.md | 35 + .../rules/rerender-split-combined-hooks.md | 64 + .../rules/rerender-transitions.md | 40 + .../rules/rerender-use-deferred-value.md | 59 + .../rerender-use-ref-transient-values.md | 73 + .../rules/server-after-nonblocking.md | 73 + .../rules/server-auth-actions.md | 96 + .../rules/server-cache-lru.md | 41 + .../rules/server-cache-react.md | 76 + .../rules/server-dedup-props.md | 65 + .../rules/server-hoist-static-io.md | 149 + .../rules/server-no-shared-module-state.md | 50 + .../rules/server-parallel-fetching.md | 83 + .../rules/server-parallel-nested-fetching.md | 34 + .../rules/server-serialization.md | 38 + .gitignore | 41 + .prettierignore | 24 + .prettierrc | 4 + CLAUDE.md | 74 + README.md | 169 + __tests__/page.test.tsx | 8 + __tests__/profile.test.ts | 77 + __tests__/schema.test.ts | 66 + __tests__/scorecards-derived.test.ts | 161 + __tests__/scorecards.test.ts | 29 + __tests__/setup.ts | 1 + app/error.tsx | 42 + app/favicon.ico | Bin 0 -> 25931 bytes app/globals.css | 221 + app/layout.tsx | 38 + app/page.tsx | 35 + components.json | 25 + components/primitives/InlineComboboxField.tsx | 151 + components/primitives/InlineDateField.tsx | 84 + components/primitives/InlineFieldRow.tsx | 35 + components/primitives/InlineSelectField.tsx | 65 + components/primitives/InlineTextField.tsx | 62 + components/primitives/InlineTextareaField.tsx | 54 + components/primitives/ScoreLabel.tsx | 34 + components/primitives/SectionLabel.tsx | 31 + components/primitives/index.ts | 38 + components/ui/alert-dialog.tsx | 187 + components/ui/avatar.tsx | 109 + components/ui/badge.tsx | 58 + components/ui/breadcrumb.tsx | 125 + components/ui/button.tsx | 58 + components/ui/calendar.tsx | 221 + components/ui/card.tsx | 121 + components/ui/collapsible.tsx | 21 + components/ui/command.tsx | 193 + components/ui/dialog.tsx | 133 + components/ui/dropdown-menu.tsx | 268 + components/ui/field.tsx | 238 + components/ui/input-group.tsx | 158 + components/ui/input.tsx | 20 + components/ui/label.tsx | 20 + components/ui/popover.tsx | 90 + components/ui/scroll-area.tsx | 55 + components/ui/select.tsx | 201 + components/ui/separator.tsx | 25 + components/ui/sheet.tsx | 138 + components/ui/sidebar.tsx | 723 + components/ui/skeleton.tsx | 13 + components/ui/textarea.tsx | 18 + components/ui/toggle-group.tsx | 89 + components/ui/toggle.tsx | 45 + components/ui/tooltip.tsx | 66 + components/workspace/AddItemDialog.tsx | 86 + components/workspace/AttachmentList.tsx | 111 + .../workspace/AttachmentPreviewDialog.tsx | 72 + .../workspace/CandidateDashboardPane.tsx | 682 + components/workspace/CandidateDetailPane.tsx | 329 + components/workspace/CandidateListPane.tsx | 223 + components/workspace/DeleteConfirmDialog.tsx | 45 + components/workspace/GlobalHeader.tsx | 91 + components/workspace/Pane1Toggle.tsx | 46 + components/workspace/Pane4Section.tsx | 26 + components/workspace/Pane4Toggle.tsx | 42 + components/workspace/PositionPane.tsx | 168 + components/workspace/SettingsDialog.tsx | 148 + components/workspace/Workspace.tsx | 382 + data/candidates.json | 206 + data/positions.json | 19 + data/workspace.json | 4 + docs/screenshot-workspace.png | Bin 0 -> 395328 bytes eslint.config.mjs | 18 + hooks/use-mobile.ts | 25 + lib/computed/profile.ts | 53 + lib/computed/scorecards.ts | 118 + lib/data/factories.ts | 56 + lib/labels.ts | 54 + lib/schema.ts | 216 + lib/utils.ts | 6 + next.config.ts | 17 + package-lock.json | 12884 ++++++++++++++++ package.json | 48 + postcss.config.mjs | 7 + scripts/check-radius-drift.mjs | 58 + skills-lock.json | 23 + tsconfig.json | 34 + vitest.config.ts | 15 + 196 files changed, 31290 insertions(+) create mode 100644 .claude/skills/designing-workspace-ui/SKILL.md create mode 100644 .claude/skills/designing-workspace-ui/references/coding-rules.md create mode 100644 .claude/skills/next-best-practices/SKILL.md create mode 100644 .claude/skills/next-best-practices/async-patterns.md create mode 100644 .claude/skills/next-best-practices/bundling.md create mode 100644 .claude/skills/next-best-practices/data-patterns.md create mode 100644 .claude/skills/next-best-practices/debug-tricks.md create mode 100644 .claude/skills/next-best-practices/directives.md create mode 100644 .claude/skills/next-best-practices/error-handling.md create mode 100644 .claude/skills/next-best-practices/file-conventions.md create mode 100644 .claude/skills/next-best-practices/font.md create mode 100644 .claude/skills/next-best-practices/functions.md create mode 100644 .claude/skills/next-best-practices/hydration-error.md create mode 100644 .claude/skills/next-best-practices/image.md create mode 100644 .claude/skills/next-best-practices/metadata.md create mode 100644 .claude/skills/next-best-practices/parallel-routes.md create mode 100644 .claude/skills/next-best-practices/route-handlers.md create mode 100644 .claude/skills/next-best-practices/rsc-boundaries.md create mode 100644 .claude/skills/next-best-practices/runtime-selection.md create mode 100644 .claude/skills/next-best-practices/scripts.md create mode 100644 .claude/skills/next-best-practices/self-hosting.md create mode 100644 .claude/skills/next-best-practices/suspense-boundaries.md create mode 100644 .claude/skills/shadcn/SKILL.md create mode 100644 .claude/skills/shadcn/agents/openai.yml create mode 100644 .claude/skills/shadcn/assets/shadcn-small.png create mode 100644 .claude/skills/shadcn/assets/shadcn.png create mode 100644 .claude/skills/shadcn/cli.md create mode 100644 .claude/skills/shadcn/customization.md create mode 100644 .claude/skills/shadcn/evals/evals.json create mode 100644 .claude/skills/shadcn/mcp.md create mode 100644 .claude/skills/shadcn/rules/base-vs-radix.md create mode 100644 .claude/skills/shadcn/rules/composition.md create mode 100644 .claude/skills/shadcn/rules/forms.md create mode 100644 .claude/skills/shadcn/rules/icons.md create mode 100644 .claude/skills/shadcn/rules/styling.md create mode 100644 .claude/skills/vercel-react-best-practices/README.md create mode 100644 .claude/skills/vercel-react-best-practices/SKILL.md create mode 100644 .claude/skills/vercel-react-best-practices/metadata.json create mode 100644 .claude/skills/vercel-react-best-practices/rules/_sections.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/_template.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/advanced-init-once.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/advanced-use-latest.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/async-api-routes.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/async-defer-await.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/async-dependencies.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/async-parallel.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/bundle-analyzable-paths.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/bundle-conditional.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/bundle-preload.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/client-event-listeners.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/client-localstorage-schema.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/client-swr-dedup.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-batch-dom-css.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-cache-function-results.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-cache-property-access.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-cache-storage.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-combine-iterations.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-early-exit.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-flatmap-filter.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-hoist-regexp.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-index-maps.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-length-check-first.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-min-max-loop.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-request-idle-callback.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-set-map-lookups.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-activity.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-conditional-render.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-content-visibility.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-resource-hints.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-svg-precision.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-defer-reads.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-dependencies.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-derived-state.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-memo.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-transitions.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-after-nonblocking.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-auth-actions.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-cache-lru.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-cache-react.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-dedup-props.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-hoist-static-io.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-parallel-fetching.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md create mode 100644 .claude/skills/vercel-react-best-practices/rules/server-serialization.md create mode 100644 .gitignore create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 CLAUDE.md create mode 100644 README.md create mode 100644 __tests__/page.test.tsx create mode 100644 __tests__/profile.test.ts create mode 100644 __tests__/schema.test.ts create mode 100644 __tests__/scorecards-derived.test.ts create mode 100644 __tests__/scorecards.test.ts create mode 100644 __tests__/setup.ts create mode 100644 app/error.tsx create mode 100644 app/favicon.ico create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/page.tsx create mode 100644 components.json create mode 100644 components/primitives/InlineComboboxField.tsx create mode 100644 components/primitives/InlineDateField.tsx create mode 100644 components/primitives/InlineFieldRow.tsx create mode 100644 components/primitives/InlineSelectField.tsx create mode 100644 components/primitives/InlineTextField.tsx create mode 100644 components/primitives/InlineTextareaField.tsx create mode 100644 components/primitives/ScoreLabel.tsx create mode 100644 components/primitives/SectionLabel.tsx create mode 100644 components/primitives/index.ts create mode 100644 components/ui/alert-dialog.tsx create mode 100644 components/ui/avatar.tsx create mode 100644 components/ui/badge.tsx create mode 100644 components/ui/breadcrumb.tsx create mode 100644 components/ui/button.tsx create mode 100644 components/ui/calendar.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/collapsible.tsx create mode 100644 components/ui/command.tsx create mode 100644 components/ui/dialog.tsx create mode 100644 components/ui/dropdown-menu.tsx create mode 100644 components/ui/field.tsx create mode 100644 components/ui/input-group.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/label.tsx create mode 100644 components/ui/popover.tsx create mode 100644 components/ui/scroll-area.tsx create mode 100644 components/ui/select.tsx create mode 100644 components/ui/separator.tsx create mode 100644 components/ui/sheet.tsx create mode 100644 components/ui/sidebar.tsx create mode 100644 components/ui/skeleton.tsx create mode 100644 components/ui/textarea.tsx create mode 100644 components/ui/toggle-group.tsx create mode 100644 components/ui/toggle.tsx create mode 100644 components/ui/tooltip.tsx create mode 100644 components/workspace/AddItemDialog.tsx create mode 100644 components/workspace/AttachmentList.tsx create mode 100644 components/workspace/AttachmentPreviewDialog.tsx create mode 100644 components/workspace/CandidateDashboardPane.tsx create mode 100644 components/workspace/CandidateDetailPane.tsx create mode 100644 components/workspace/CandidateListPane.tsx create mode 100644 components/workspace/DeleteConfirmDialog.tsx create mode 100644 components/workspace/GlobalHeader.tsx create mode 100644 components/workspace/Pane1Toggle.tsx create mode 100644 components/workspace/Pane4Section.tsx create mode 100644 components/workspace/Pane4Toggle.tsx create mode 100644 components/workspace/PositionPane.tsx create mode 100644 components/workspace/SettingsDialog.tsx create mode 100644 components/workspace/Workspace.tsx create mode 100644 data/candidates.json create mode 100644 data/positions.json create mode 100644 data/workspace.json create mode 100644 docs/screenshot-workspace.png create mode 100644 eslint.config.mjs create mode 100644 hooks/use-mobile.ts create mode 100644 lib/computed/profile.ts create mode 100644 lib/computed/scorecards.ts create mode 100644 lib/data/factories.ts create mode 100644 lib/labels.ts create mode 100644 lib/schema.ts create mode 100644 lib/utils.ts create mode 100644 next.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.mjs create mode 100644 scripts/check-radius-drift.mjs create mode 100644 skills-lock.json create mode 100644 tsconfig.json create mode 100644 vitest.config.ts 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 | +|---------|-------|-----| +| `