원클릭으로
component-rules
// Rules and conventions for building React components in this repo (composition pattern, no logic in component body, styling tokens, file layout, etc.). Read this before writing or editing any component.
// Rules and conventions for building React components in this repo (composition pattern, no logic in component body, styling tokens, file layout, etc.). Read this before writing or editing any component.
Stage files, generate a commit message in this repo's Conventional Commits style, and commit after user approval.
Port a component from the HTML reference (digital-go-jp/design-system-example-components-html) to this React + Storybook project.
Run the pre-completion checks before declaring a task done. Executes lint, markup lint, build (tsc), tests, and reminds the user to verify Storybook visually.
Review changed code in this repo against the project's review checklist (readability, maintainability, performance, security, consistency).
Author component documentation in this project. Docs live inside the component's *.stories.tsx (Storybook autodocs), translated from the HTML reference's MDX structure into JSX, in Japanese.
Write tests for components in this project. Covers Storybook-based UI tests (composeStories + vitest-browser-react), unit tests for hooks/utils, and the conventions that keep tests deterministic and meaningful.
| name | component-rules |
| description | Rules and conventions for building React components in this repo (composition pattern, no logic in component body, styling tokens, file layout, etc.). Read this before writing or editing any component. |
These are the project-wide rules for component code. Workflow skills (port-html-to-react, write-tests, write-component-docs) build on top of these — they don't restate them.
The codebase is the source of truth for style and structure. Read it before adding or changing components.
src/components/ to see what is already there and how directories are organized..tsx, .stories.tsx, .test.tsx, plus any split-out types.ts / hooks. Match their patterns rather than inventing new ones.tailwind.config.js and the @digital-go-jp/tailwind-theme-plugin tokens so styles use existing tokens, not stock Tailwind values.port-html-to-react).src/components/<Name>/ first — including stories, tests, and any split-out hooks/utils. Subtle conventions live in those siblings.Use React children composition. Keep props and state minimal; let consumers assemble the structure.
// Good: consumers control the structure
<Dialog>
<DialogBody>
<h2>...</h2>
<p>...</p>
</DialogBody>
</Dialog>
// Bad: structure locked inside props
<Dialog body={<><h2></h2></>} />
Base props on native element props. Add custom props only when necessary.
type ButtonProps = ComponentProps<'button'>;
// When extension is necessary:
type ButtonProps = ComponentProps<'button'> & {
size: Size;
variant: Variant;
};
BaseProps, CommonProps, BaseButton, BaseCard.useState, useRef, useEffect, or other React hooks in the component body (keeps React Server Component compatibility).useFooAnnouncer.ts) — imported when the consumer needs it.src/components/FileUpload/FileUpload.test.tsx for the pattern (Story reuse via composeStories + vitest-browser-react).Components must be self-contained. Do not rely on:
tailwind.config.js (e.g. extending theme.keyframes or colors for a single component)global.css or other shared stylesheetsIf you need custom CSS (e.g. @keyframes), put it in a .css file inside the component directory and import it from the component's .tsx. A consumer should be able to copy the component folder and have it work.
Per component:
src/components/Foo/
├── Foo.tsx # Main implementation (sub-components can live here too)
├── Foo.stories.tsx # Storybook
├── Foo.test.tsx # Vitest (when hooks/utils are split out, or for UI behavior)
├── types.ts # Shared types (size, variant, etc.)
├── useFooSomething.ts # Component-specific hook (when needed)
└── index.ts # Public exports
Foo.tsx.Foo.tsx exceeds roughly 300 lines, propose a split to the user; do not do it unprompted.@digital-go-jp/tailwind-theme-plugin (https://github.com/digital-go-jp/tailwind-theme-plugin).text-blue-500 etc.). text-white and text-black are the only exceptions.text-xs, text-lg, etc.). Text styles are covered by the text-std-*, text-dns-*, text-oln-*, text-mono-* token sets — these set font-size, weight, line-height, and letter-spacing together, so a single token usually replaces multiple classes (text-base leading-[1.3] tracking-normal → text-dns-16N-130).leading-100 … leading-175 over arbitrary values like leading-[1.3].[margin-left:calc(...)] → ml-[calc(...)], [padding-left:1rem] → pl-4, etc. Reserve [property:value] for properties Tailwind has no shorthand for.prefers-reduced-motion: reduce and forced-colors handling.focus-visible, hover, and other interaction states), grep for the same selector elsewhere in src/components/ and follow that form. Example: focus-visible outline + ring is expressed with focus-visible:outline focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-* focus-visible:ring-yellow-300 — not as [box-shadow:...] arbitrary values.The HTML reference is built without a CSS reset, so it explicitly normalizes <button>, <ul>, <a>, etc. (e.g. border: 0; background: transparent; padding: 0; margin: 0; list-style: none; font: inherit; text-decoration: none; color: inherit; box-sizing: border-box). This project uses Tailwind's Preflight, which already applies those resets globally.
Do not copy those reset declarations into the React port. Only add classes that change behavior beyond Preflight's defaults. Common offenders to omit:
m-0, p-0, pl-0 on <ul>/<ol> (Preflight handles list margins/padding)list-none on a <ul> that has no markersborder-0, bg-transparent on <button>text-inherit, [font:inherit], [letter-spacing:inherit] on form/interactive elementsno-underline on <a> when the design wants the defaultbox-sizing: border-box (Preflight sets it)data-* + group-data-[...] patternRepresent state via data-* on the root; children react via group-data-[...]/name. Avoid branching className in JS.
<div
className='group/progress-indicator'
data-type={type}
data-indeterminate={isIndeterminate ? '' : undefined}
>
<svg className='group-data-[indeterminate]/progress-indicator:[animation:spin_2s_linear_infinite]' />
</div>
console.log or debug output in committed code.Playground Story. Tests reuse it via composeStories.spinner-loop.html → SpinnerLoop, display name in Japanese).tags: ['autodocs'] to the meta.write-component-docs skill.All Vitest tests run in browser mode via Playwright + Chromium (see vite.config.ts). There is no jsdom environment.
composeStories + vitest-browser-react against Stories (storybook project).src/**/*.test.{ts,tsx} (unit project — also browser-mode, but DOM globals are simply unused for pure logic).write-tests skill. Reference implementation: src/components/FileUpload/FileUpload.test.tsx.