Component contract
Define the decisions a component owns. Inspect existing components and callers before proposing a duplicate or a universal abstraction.
Work
- Name purpose, consumers, and ownership: product-agnostic primitive, domain component, or layout. Domain states belong near the feature even when reused across several screens.
- Define necessary inputs, events, controlled/uncontrolled behavior, defaults, and composition slots. Prefer explicit states over combinations of contradictory booleans.
- Specify reachable data and interaction states. Separate component behavior from server authorization and persistence responsibilities.
- Describe native semantics, accessible name, keyboard model, focus entry/return, and feedback. Follow the applicable platform pattern; not every control uses the same keys.
- Define content limits, wrapping, responsive behavior, supported themes, and token usage. Do not truncate essential status labels without an equivalent accessible path.
- For a revision, identify caller migrations and backward compatibility before changing the API.
Deliver
A compact contract: purpose/owner, API, meaningful states, interaction, resilience, acceptance. Include types only when they resolve ambiguity. A simple badge does not need a full component RFC.
## Component specification: [Name]
**Purpose:**
**Type:** primitive | feature | layout
**Used in:**
**Variants:**
**States:**
**Props:** inputs, events, defaults
**Accessibility:** semantics, keyboard, focus
**Tokens / responsive:**
**Acceptance:** [...]
Example: ProjectStatus composes a generic Badge but maps domain status to text in feature code. Its label conveys meaning independently of color; an unknown server value has an intentional fallback.
Verify behavior in its real parent, including long content and keyboard interaction. For implementation requests, build the component and run relevant checks. Use $states for gaps across a larger flow.
Worked example
Component specification: ProjectStatusBadge
Purpose: Show a project's lifecycle status next to its name in lists and headers.
Type: Primitive-level badge with domain variants; kept in primitives until two or more features need it.
Used in: ProjectList, ProjectDetailHeader
Variants: status: active | paused | completed | archived; size: sm | md (default md)
States: Default only; non-interactive with no hover action; label truncates with ellipsis below 320px, never wraps.
Props: status (required), size?, className?; label text derives from status; no arbitrary children slots.
Accessibility: Semantic span; status conveyed by text, color decorative; AA contrast for every pair in both themes.
Tokens / responsive: Uses --color-success, --color-warning, --color-muted, --color-primary pairs; no raw hex.
Acceptance: All four statuses render correct label and colors; passes contrast in both themes; holds layout in a 320px container.
Gotchas
- Do not combine contradictory booleans for state; prefer one explicit state value.
- Do not truncate essential status labels without an equivalent accessible path.
- Do not embed domain status mapping in a primitive; keep it in feature code.
- Do not create a duplicate before inspecting existing components, callers, and system inventory.
- Do not assign server authorization or persistence to the component contract.
Boundaries
- Do not use when the product has no tokens yet — use
$tokens first.
- Do not use when the component already exists — check
$system inventories before duplicating.
1---2name: component3description: Specify or revise a UI component’s API, states, semantics, data ownership, and acceptance behavior. Use when defining props, variants, slots, keyboard behavior, or acceptance criteria before building a component.4---56# Component contract78Define the decisions a component owns. Inspect existing components and callers before proposing a duplicate or a universal abstraction.910## Work1112- Name purpose, consumers, and ownership: product-agnostic primitive, domain component, or layout. Domain states belong near the feature even when reused across several screens.13- Define necessary inputs, events, controlled/uncontrolled behavior, defaults, and composition slots. Prefer explicit states over combinations of contradictory booleans.14- Specify reachable data and interaction states. Separate component behavior from server authorization and persistence responsibilities.15- Describe native semantics, accessible name, keyboard model, focus entry/return, and feedback. Follow the applicable platform pattern; not every control uses the same keys.16- Define content limits, wrapping, responsive behavior, supported themes, and token usage. Do not truncate essential status labels without an equivalent accessible path.17- For a revision, identify caller migrations and backward compatibility before changing the API.1819## Deliver2021A compact contract: **purpose/owner, API, meaningful states, interaction, resilience, acceptance**. Include types only when they resolve ambiguity. A simple badge does not need a full component RFC.2223```markdown24## Component specification: [Name]2526**Purpose:**27**Type:** primitive | feature | layout28**Used in:**29**Variants:**30**States:**31**Props:** inputs, events, defaults32**Accessibility:** semantics, keyboard, focus33**Tokens / responsive:**34**Acceptance:** [...]35```3637Example: ProjectStatus composes a generic Badge but maps domain status to text in feature code. Its label conveys meaning independently of color; an unknown server value has an intentional fallback.3839Verify behavior in its real parent, including long content and keyboard interaction. For implementation requests, build the component and run relevant checks. Use `$states` for gaps across a larger flow.4041## Worked example4243## Component specification: ProjectStatusBadge4445**Purpose:** Show a project's lifecycle status next to its name in lists and headers.46**Type:** Primitive-level badge with domain variants; kept in primitives until two or more features need it.47**Used in:** ProjectList, ProjectDetailHeader48**Variants:** `status: active | paused | completed | archived`; `size: sm | md` (default md)49**States:** Default only; non-interactive with no hover action; label truncates with ellipsis below 320px, never wraps.50**Props:** `status` (required), `size?`, `className?`; label text derives from status; no arbitrary children slots.51**Accessibility:** Semantic `span`; status conveyed by text, color decorative; AA contrast for every pair in both themes.52**Tokens / responsive:** Uses `--color-success`, `--color-warning`, `--color-muted`, `--color-primary` pairs; no raw hex.53**Acceptance:** All four statuses render correct label and colors; passes contrast in both themes; holds layout in a 320px container.5455## Gotchas5657- Do not combine contradictory booleans for state; prefer one explicit state value.58- Do not truncate essential status labels without an equivalent accessible path.59- Do not embed domain status mapping in a primitive; keep it in feature code.60- Do not create a duplicate before inspecting existing components, callers, and system inventory.61- Do not assign server authorization or persistence to the component contract.6263## Boundaries6465- Do not use when the product has no tokens yet — use `$tokens` first.66- Do not use when the component already exists — check `$system` inventories before duplicating.