Files
ai-for-dummies/.agents/rules/componentization.md

55 lines
1.9 KiB
Markdown

# Rule: componentization
## When to make a component
Extract when the same markup appears **three times**, or when a block has a name
a person would use out loud ("the eyebrow", "the route card", "the phase
panel").
Do not extract on the second occurrence. Two similar blocks often diverge; the
premature abstraction costs more than the duplication.
## Sizes
- A component that exceeds ~120 lines of markup is doing two jobs. Split it.
- A page that is a bare list of components with no markup of its own has been
over-split. Pages are allowed to contain layout.
## Boundaries
```
src/components/
primitives/ Eyebrow, Rule, Callout, CodeBlock — no domain knowledge
blocks/ RouteCard, PhasePanel, HandoffTable, SkillPackage — composed, page-agnostic
islands/ interactive only; each one justified per rules/astro.md
```
- Primitives never import blocks.
- Blocks never import page-specific data; they take props.
- Islands are leaves. An island must not wrap static children that could have
been server-rendered.
## Props
- Typed `interface Props`, every field. No `any`, no untyped rest spread.
- Required by default. Optional props need a default and a reason.
- Pass data, not markup. If you find yourself passing an HTML string, you want a
`<slot>`.
## Named exports, no barrels
Import the file you need. Barrel `index.ts` files break tree-shaking and create
import cycles; bulletproof-react advises against them and so do we.
## The catalog is data, not components
The 24 review-desk entries are content, not 24 components. One
`SkillReviewCard.astro` iterating a typed collection. If you are writing the
25th near-identical component, stop and model the data.
## Do not componentize
`hands-on/starter/` and `hands-on/rules/` are lab fixtures. Their whole value is
being flat, dependency-free files an attendee hands to an agent. They ship from
`public/` unchanged.