COMPONENTS.md — component-first forms (the thin-shell standard)
Rule (AGENTS.md mandate 12): a route +page.svelte is a thin shell. It
loads data, then composes small components. It must not grow into a monolith.
Small, well-named components keep each correction scoped to its own files,
so a row session touches little and reviews stay short.
Why
If ten rows all edit one big page, every one of them wades through the same monolith. Small, well-named components keep each correction in its own file.
A file topping that list is the next thing to split.
Layout
src/routes/<area>/
+page.server.ts # data load + form actions (unchanged contract)
+page.svelte # THIN shell: composes components, holds page state
new/+page.svelte # thin shell for the new-record wizard
edit/+page.svelte # thin shell
src/lib/components/<area>/
Wizard.svelte # the entry wizard (steps)
TermsPopup.svelte # one concern per component
WeightsPanel.svelte
ListRows.svelte
...
Rules
- One concern per component. If a component does two unrelated things, split it. Target: each component under ~300 lines; each route page under ~150 lines.
- Never change the contract while splitting. Form
action=,method, POST field names, share/print output and server validation stay identical. A split is a pure refactor — the full test suite must pass unchanged. - State: keep page-level state in the shell; pass props down, emit events
up. Shared stores (
src/lib/*.svelte.ts) are fine for cross-cutting state (toasts, drafts) — don't invent new globals per form. - Tests travel with the component. Split the test file the same way; keep the existing pins green.
- Split on demand, not everywhere. Only split a form when it becomes a
hotspot (
--hotspots). New forms are born split; old ones are retrofitted one hotspot at a time. Reference: C88 (the quote flow).
New-form checklist
-
+page.server.tsowns load + actions (no UI). -
+page.svelteis a thin shell composing<area>components. - Each wizard step / popup / panel is its own component file.
-
docs/OWNERSHIP.mdlists the new components under the area lane.