# Contributing

Thanks for helping with this kit. It is a **library**, not an app, and that
changes what "done" means: every change here lands in someone else's build, so
the bar is a bit higher than usual and the checks are correspondingly noisy.

## Getting set up

```bash
git init             # only if this is a fresh scaffold — see "Git hooks" below
npm install          # or pnpm install
npm run dev          # the showcase, on http://localhost:5175
```

`npm run dev` opens the **showcase** — the gallery that documents every
component as a variant grid. That is the primary surface for developing against.
Storybook is optional and may not be present at all; see below.

## Git hooks

Three hooks run locally, installed by husky from the `prepare` script. They are
a faster echo of CI, never a replacement for it.

| Hook | What it does | Cost |
|---|---|---|
| `pre-commit` | Branch-name gate, then ESLint on the **staged files only** | ~2-5s |
| `commit-msg` | The subject line must be `<prefix>[(scope)][!]: <text>` | instant |
| `pre-push` | Full `lint`, `typecheck` and `test:run` | ~70s |

`build`, `verify`, `size` and `audit:shipped` are in **no** hook: they need
`dist/` built first, and a hook that takes minutes gets bypassed within days.

Both the branch name and the commit subject take the same prefixes —
`chore feat hotfix bugfix reconcile fix docs refactor test perf ci` — so a
branch is `feat/progress-circle` and its commits are `feat: …`. `main`, `master`
and a detached `HEAD` (rebase, bisect) are exempt from the branch gate.

Run them by hand with `npm run check:staged` and `npm run check`. Bypass with
`HUSKY=0 git commit` or `git commit --no-verify` — and say so in the PR if you
did.

**They activate on install, but only in a git repository.** A freshly scaffolded
kit is not one yet, so `npm install` prints `.git can't be found` and does
nothing. Run `git init` first, or `npx husky` afterwards.

## The one-command gate

```bash
npm run lint && npm run typecheck && npm run test:run && npm run build && npm run verify && npm run size
```

That is exactly what CI runs. Run it before opening a PR — not because CI will
not catch it, but because `verify` failures are much easier to read locally.

If a step fails in a way that looks unrelated to your change, check
`docs/instructions.md` §9 first: several failure modes here are environmental
(a blocked native binding, a package manager that skipped an install script)
rather than defects.

## Adding a component

There is a checklist, and skipping any line of it produces a component that
looks finished and is not:

1. `src/components/<name>/` with `<name>.tsx`, `<name>.test.tsx`, `index.ts`
   (and `<name>.stories.tsx` if this kit kept Storybook).
2. `'use client'` at the top of **both** `<name>.tsx` and `index.ts`.
3. A case in `test/cases.tsx` — that one table feeds both the accessibility
   sweep and the server-rendering sweep, so a component missing from it is
   untested in two ways at once.
4. A showcase entry in `showcase/src/registry/<name>.tsx`, and add it to
   `showcase/src/registry/index.ts`. **Without this the component does not
   exist as far as the documentation is concerned.**
5. `npm run exports:gen` — this writes the `exports` map. Never edit that map
   by hand.
6. `npm run props:gen` and `npm run tokens:gen` if you touched props or tokens.
7. `npx changeset` to describe the change (see below).

If you use Claude Code, `/ui-kit-component` does all of the above.

## The rules that are enforced, and why

`npm run lint` fails on each of these. They are not style preferences:

- **No raw colours in `src/`.** No `#fff`, no `rgb()`, no `oklch()`, and no
  `--ui-color-*` primitive. Components use the semantic layer
  (`bg-background`, `text-muted-foreground`). That indirection is the only
  reason the whole kit can be rebranded by rewriting one file.
- **No `radix-ui` barrel import.** Use the per-primitive subpath
  (`import * as Dialog from 'radix-ui/dialog'`). The barrel is one module
  reachable from every Radix-using component, so importing it defeats code
  splitting — measured, it took Button from 10 kB to 434 kB.
- **No new runtime dependencies.** `dependencies` is empty and that is a
  feature: Radix is compiled into `dist`, `tailwind-merge` is vendored into
  `src`, and everything else is local code. A package added here lands in every
  consumer's lock file. Write it locally, or make the case in the PR.
- **No `clsx`, `class-variance-authority`, `lucide-react`, `cmdk`, `sonner`,
  `vaul`, `rc-*` or a date library.** The kit already has all of them as local
  code under `src/lib/` and `src/icons/`.

## Changesets

Every change a consumer can observe needs one:

```bash
npx changeset
```

Pick `patch` for a fix, `minor` for a new component or prop, `major` for
anything that changes an existing shape. Dev-only changes (a test, a script, CI)
need no changeset.

`npm run release` refuses to publish while changesets are pending, because a
pending changeset means the version was never bumped.

## Storybook is optional

This kit may have been scaffolded without it. If `.storybook/` is missing, that
is deliberate — the showcase covers the same ground and is the surface that gets
published. Do not add stories to a kit that has no Storybook; add a showcase
registry entry instead.

## Pull requests

- One logical change per PR.
- Say what a consumer would notice, in one sentence. If the answer is "nothing",
  say that too — it tells the reviewer no changeset is needed.
- Paste the real output of the gate above. If something failed and you believe
  it is unrelated, say so explicitly rather than leaving it out.
