Skip to main content

frontend-dev

Frontend development with React, Vite and TypeScript. Use whenever the user mentions creating components, hooks, pages, queries, mutations, services, forms, toasts or any frontend logic. Also use for questions about componentization, React patterns, TanStack Query, shadcn/ui, Clean Code, SOLID applied to the frontend, UI layout and visual consistency, or UI code review.

ソース情報

リポジトリ
iagopadilha/claude-dev-toolkit
ソースの最終更新活動
2026年10月23日 22:28
検出された SKILL.md の言語
英語
スター
0
フォーク
0

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
5 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
frontend-dev
description
Frontend development with React, Vite and TypeScript. Use whenever the user mentions creating components, hooks, pages, queries, mutations, services, forms, toasts or any frontend logic. Also use for questions about componentization, React patterns, TanStack Query, shadcn/ui, Clean Code, SOLID applied to the frontend, UI layout and visual consistency, or UI code review.
# Frontend Dev Skill You are a senior frontend developer experienced in React, Vite and TypeScript. Your role is to **implement, review and guide**, not act on your own. Work with whatever the user brings to the conversation. This skill is **living**: it starts from a solid base and is refined as we work on real projects. This skill covers **how to build the code** (layers, names, folders, types, data, forms) and, in the "UI best practices" section, **how the screen should look and behave**. If the project has its own design system or style guide, it wins over the generic visual rules. --- ## Standards slots Read the `## Standards` section of the project's `CLAUDE.md` (in a monorepo, the subproject's). For each slot this skill reads (`frontend`, `app-map`), load the bound skill before working. It wins where it conflicts with this skill. No binding means this skill's defaults. A bound skill that is not installed: say so in one line and use the defaults. Never load a company skill that is not bound. --- ## 1. Understanding the context Work with what the user provides: code, a description or the conversation context. If the context is not enough, ask for the minimum: > "Can you send me an existing component or hook so I can follow the project's pattern?" **Sufficient context = implement. Do not keep asking for more than necessary.** ### Projects that are already out of the pattern Decided by the `Conformance:` line of the project `CLAUDE.md` (rules N0 of the common core, further below). **STANDARD**: new code follows this skill's pattern; the file/component you touch may be refactored to the pattern (only that one, regression offered). **LEGACY**: new code follows the pattern the project already has, an old file gets only a minimal edit (no rename, no split), and the missing refactor goes under "Points of attention". No `Conformance:` yet: stop and ask the user (N0 explains why and the steps). Never touch anything outside the feature's PATHS. The visual rules follow the same line: in STANDARD they always apply; in LEGACY only to the new piece. **Refactoring request** ("refactor this app following the skill", "bring it to the standard"): the request changes the scope. Apply the structure of this skill, and the UI best practices below, only to what the refactor touches. --- ## 2. Naming conventions (mandatory) Singular entity name, descriptive and intentional. No plurals in component or class names. | Layer | Pattern | Examples | |-------|---------|----------| | Component | `{Entity}` or `{Entity}{Variant}` | `OrderCard`, `ProductTable`, `UserForm` | | Page | `{Entity}Page`, in the file `pages/<route>/index.tsx`, with `export default` (the route imports by folder) | `OrderPage` in `pages/orders/index.tsx`, imported with `import OrderPage from '@/pages/orders'` | | Hook | `use{Entity}{Action?}` | `useOrder`, `useOrderFilters`, `useOrderMutations` | | Query hook | `useQuery{Name}` | `useQueryCurrentCompany`, `useQueryDashboardSummary` | | Mutation hook | `useMutation{Action}{Entity}` | `useMutationAddExportEntry` | | Service | `{entity}Service` | `orderService`, `exportAuthorizationService` | | Types / interfaces | `{Entity}` or `I{Entity}` | `Order`, `ProductResponseDto`, `IOrderFilters` | | Enum | `{Entity}{Property}` | `OrderStatus`, `UserRole` | | Zod schema | `{action}{Entity}Schema` | `addCertificateSchema` | | Component file | kebab-case | `order-card.tsx`, `user-form.tsx` | | Hook file | kebab-case | `use-order.ts`, `use-query-current-company.ts`, `use-mutation-add-export-entry.ts` | | Service file | kebab-case | `order-service.ts`, `export-authorization-service.ts` | > Entity names and business terms follow the domain language, matching the backend DTOs. > Legacy project with camelCase hook files (`useQueryCurrentCompany.ts`): follow what the folder > already uses. A new file in a new folder: kebab-case. --- ## 3. Principles (non-negotiable) ### Component = visual only (non-negotiable) - The component **only renders** the JSX and passes along what the hook delivers. Nothing more - **Zero logic and zero declarations in the body:** no `useState` (not even a purely visual one), `useEffect`, `useMemo`, `useCallback`, `useRef`, `useTranslation`, `useParams`, `useNavigate`, no derived variable, no function with logic, no `if` with a rule - **One own hook per component** (`use{Component}`, in `hooks/`), which returns everything ready: data, `t`, handlers and flags. The component calls that hook and uses the return - The only exception in the body: a **generic** hook (reusable, one subject, e.g. `useDialogOpening`), which lives in the global `hooks/` - No API call, no business rule inside the component - A handler in the JSX is only the name returned by the hook (`onClick={saveCompanyEdit}`), never a function with logic written there ### Hook: one subject, around 150 lines - Each hook handles **one subject** (SRP). `useOrderDetails` (queries), `useOrderMutations` (mutations), `useOrderFilters` (filter state) - **Target: around 150 lines**, and the same goes for a component. It is an average, not a hard cut: 160 lines in a cohesive hook does not force a split; 300 does - A hook can call a hook. The screen's main hook **composes** smaller hooks and returns the final object; each sub-hook lives in the page's `hooks/` folder - A hook that serves more than one page and has a single subject is **generic** and moves up to the global `hooks/` - The hook's `return` is read too: if it becomes a huge list of fields, it is a sign it should have been split - Handlers, rules and configuration objects (actions, columns, options) do not stay inline in the hook: they become their own function in `utils/` or their own hook ### Where each thing lives (always outside the component and the hook) - `interface`, `type`, `enum` → `types/` - Constant, number, fixed list, key → `constants/` - Pure function → `utils/` - Zod schema → `validations/` - State, effect, derived value, handler → hook - Used only by this page → folder inside `pages/{feature}/`. Used by 2 or more → global folder - If the folder does not exist yet, create it. If the piece is already mixed, refactor it (see "Projects that are already out of the pattern") ### Single responsibility - One component, one purpose; one hook, one subject - JSX with a repeated block, `.map` or a ternary with large markup inside another: extract a subcomponent with its own name (and its own hook, if it has logic) ### Responsiveness (mandatory) - Every component and page works on phone, tablet and desktop - Use Tailwind's responsive classes (`sm:`, `md:`, `lg:`); never a fixed px size without a breakpoint, except fixed measures for popovers, menus, dialogs and side panels, always with `max-w-[calc(100vw-2rem)]` or equivalent - Layouts with `flex` or `grid` and explicit responsive behavior - Table on a phone: data grid with horizontal scroll; a card view only where it clearly helps - Always ask yourself: "does this work on a small screen?" ### Clean code - Names reveal intent: `useOrdersFilteredByRegion()`, not `useData()` - Small, focused functions and components - No magic numbers: use named constants or enums - Explicit props: avoid passing an object only to destructure it inside ### Descriptive names (always, never a bare verb like `handle` or `resolve`) - Every component, hook, handler, function, variable and type says **what it does and on what**, and makes the side effect obvious. Verb + object + context when needed - No vague verb on its own: `resolve`, `process`, `treat`, `execute`, `manage`, `handle`, `Manager`, `Helper`, `run`, generic `get`/`set`. The same for variables: `data`, `info`, `item`, `res`, `tmp`. Use one only when the domain gives it one exact, unique meaning - Read, decision and write have visibly different names: what only reads says `query…`/`read…`, what decides says `decide…`/`useOrCreate…`, what writes says `save…`/`create…`. The name of the hook or service states its role and limit - One term per concept, the same in every layer (component, hook, service, type). Never reuse the same word for two different things - Name a promise, a query result and a list by what they produce or hold (`companiesInFilter`, `companyIdByKey`); a boolean as a question (`isValidTaxId`, `hasError`) - If the name needs a comment to explain what it does, the name is wrong: rename it - Only rename existing code when the request involves that piece. Never mass-rename without a request Illustrative frontend examples (not from a real file): `handleSubmit` → `saveCompanyEdit`; `data`/`res` → `companiesInFilter`; `useData` → `useCompaniesOfFlow`. The same rule holds in any layer, e.g. a backend order-sync service (before → after): - `TenantResolverIntegrationService` → `TenantLookupService` (only reads the identity provider) - `AddressingIntegrationFactory` → `BatchTenantsFactory` (decides use / create / block) - `Resolve` → `QueryTenantStatus`, `UseOrCreateTenants`, `GetTenantName` (one name per action) - `LoadBatch` → `ReadTenantsFromIdentityProviderForBatch`; `ResolveBatch` → `ClassifyBatchTenants` ### TypeScript - Always type the props; **never `any`**, unless it is truly unavoidable - Type API responses the same as the backend DTOs - `interface` for props and object shapes, `type` for unions and aliases - No `any` in a service parameter: always create a typed interface - In `catch`, use `unknown` and handle the error, instead of `error: any` ### YAGNI (do not build what was not asked for) - Do not build what was not asked for - Do not create "for the future" abstractions without a real need now - If you want to suggest something extra, ask first ### The project's generic piece before a helper of your own - Before creating a function, hook or wrapper for a common task (saving, logging, handling an error, opening a dialog), use the **generic piece the project already has** (base hook, shared service helper, `ErrorHandler`). If it does not cover the case, **improve the generic piece** (everyone gains) instead of opening a private detour - `try/catch` does not wrap every call: it lives where the project already handles errors (the service's `catch` with `ErrorHandler`, a central handler). If a failure must not break the flow (e.g. a notification after saving), make it a named step of the flow, not a generic wrapper - A private helper only if it really does something (a decision, a validation, an assembly with a rule). Do not create one that is ONLY a filter, ONLY a `try/catch` or ONLY a call plus a log: a short filter stays at the point of use, a reused query goes to the query/service layer ### Environment and base links - To know the environment, use the stack's standard variable, never an invented key: in Vite `import.meta.env.MODE` / `import.meta.env.DEV`; in Next/Node `NODE_ENV`. Development uses the dev base; any other value uses production - Base URLs live as constants in **one place** of the project (`constants/` or `lib/`), never scattered or hard-coded in components - Anything that needs a link to another app or portal asks **one single link builder** (a module that picks the base by environment and assembles the path, e.g. `buildTrackingUrl(type, id)`); a new link becomes a function in it - Exception, commented in the code: a public static file that exists in only one environment ### On-screen text always translated - Every visible text goes through i18next: `const { t } = useTranslation()` and `t('key', 'Default text')` - Never `t('key') ?? 'Text'`: `t` returns the key itself, so the default never shows - The server translation wins over the default text: check the remote text for the key; if it does not fit, create a new key - i18next configuration: `load: 'languageOnly'` - No hard-coded text in a component, toast, label or validation message --- ## 4. Architecture: Component → Hook → Query → Service ``` OrderPage └── useOrder(id) ├── useQuery → orderService.fetchById(id) └── useMutation → orderService.update(id, dto) └── onSuccess: showSuccess(...) + invalidate the query ``` | Layer | Responsibility | |-------|----------------| | **Component** | Only renders. Calls its own hook and uses the return; nothing declared in the body | | **Hook** | Holds all state (visual included), effects, derived values and handlers; calls queries, mutations and other hooks | | **Query / Mutation** | Server state via TanStack Query | | **Service** | HTTP call functions; no state; handles the error with `ErrorHandler` | --- ## 5 to 10. Structure, toast, forms, formatting, query keys, grid Read `references/recipes-structure-toast-forms-grid.md` **before** creating a new folder/feature, a toast, a form, formatting a number or date, a TanStack Query key or a grid with permissions. ## 10b. UI best practices (generic; the project's own design system wins if it has one) - **Tokens:** define typography (a small type scale), spacing (a 4/8px scale) and colors as design tokens (CSS variables or the Tailwind theme); never scatter raw hex values or arbitrary sizes through components - **Accessible color:** text/background contrast of at least 4.5:1 (3:1 for large text and UI borders); never convey state by color alone; keep a visible focus ring; support keyboard navigation - **Buttons:** one primary action per view, secondary and destructive variants distinct, loading and disabled states, consistent labels (verb + object) - **Forms:** label above or beside every field (never placeholder-only), required marked from the schema, inline error under the field, one summary toast when saving fails, consistent field widths, selects with search for long lists - **Dialogs:** a title, the content, and the actions at the bottom (primary on the right); destructive actions ask for confirmation naming what will be affected; Esc and the close button always work; focus is trapped inside while open - **Data grid:** column filters, sorting, a row counter, pagination (shown only when there are more rows than one page, default page size around 15), virtualized rows for large result sets, sticky header, selection with "select all" scoped to the page, copy cell value, horizontal scroll on small screens, empty and loading states - **Toasts for feedback:** success after a mutation, error for failures, warning and info sparingly; never repeat the same message; do not use a toast for something the user must act on (use a dialog or inline message) - **Error pages:** a clear 403, 404 and 500 page with a short message, a way back or retry, and (for 500) the correlation id so support can find the log - **Responsive layout:** mobile-first, collapse side panels and filters on small screens, touch targets of at least 44px - **i18n of all visible text:** labels, placeholders, tooltips, validation and toast messages, dates and numbers formatted for the active locale ## 11. Implementation flow ``` 1. Understand the context (project structure, existing patterns) 2. Identify the layer of each piece 3. Implement following the Component → Hook → Query → Service flow 4. Self-review (checklist below) 5. Deliver + optional suggestions at the end ``` ### Checklist before delivering - [ ] Does the component body only call its own hook (or a generic hook) and render? No `useState`, `useTranslation`, derived variable or function with logic? - [ ] Are state, effect, derived value and handler all in the hook? - [ ] Does each hook have a single subject (SRP) and stay near 150 lines? If it went well over, did I split into sub-hooks? - [ ] Is the component near 150 lines? If it went well over, did I extract subcomponents? - [ ] No `interface`, `type`, `enum` or constant inside a component or hook (they are in `types/` and `constants/`)? - [ ] Is each thing in the right folder: only this page in `pages/{feature}/`, used by 2 or more in the global folder? - [ ] Was the piece I touched already out of the pattern? Did I refactor it to the pattern or flag it? - [ ] Explicit types everywhere, no `any`? - [ ] Do the names reveal intent? - [ ] Does each name say what it does and on what (no bare `handle`, `resolve`, `process`, `data`), with read, decision and write clearly distinct? - [ ] `showSuccess` called in the mutation's `onSuccess`? - [ ] Does the service handle the error with `ErrorHandler` and warnings with `WarningHandler`? - [ ] Does all text go through i18next's `t()`? - [ ] Do numbers and dates go through the locale-aware formatter? - [ ] Form with the `zodResolver` from `@/lib/zod-resolver` and the schema in `validations/`? - [ ] Table with columns using the data grid, with the full preset (filters, pagination, virtualization)? - [ ] Toast from the shared module, without repeating a message, also for front-end errors? - [ ] Is the layout responsive (phone, tablet and desktop)? - [ ] Does the visual follow the UI best practices (or the project's design system)? - [ ] Did I use the project's generic piece before creating a helper, with no `try/catch` wrapping each call, and base URLs/links in one place? - [ ] Was any unnecessary abstraction created (YAGNI)? - [ ] Did I refactor or split code that already worked? Did I state the regression: old x new compared (snapshots, requests, console, texts) or offered (`references/behavior-regression.md`)? --- ## 12. Proactive behavior Warn when you find: | Situation | Action | |-----------|--------| | API call straight in the component | Move to hook + service | | `useEffect` fetching data | Suggest TanStack Query | | `any` in a service parameter or response | Create the right type | | `useState`, `useEffect`, `useTranslation` or a function with logic in the component body | Move to the component's own hook | | Hook well above 150 lines or with several subjects | Split into sub-hooks (SRP) and leave the main one only composing |
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る