-
Notifications
You must be signed in to change notification settings - Fork 28
Component Specification Protocol
New here? Start with Component Lifecycle for the full end-to-end guide. This page covers the specification phase in detail.
Every new Astryx component must go through the full specification protocol before any code is written. The spec is the contract — deviations during build need justification.
- Identify the component gap (from internal usage data, builder feedback, or milestone needs)
- Check if existing components can compose to solve the problem before creating something new
- If the need is domain-specific, it's a recipe/example — not a core component
- Use code search (
tbgs,tbgf,tbcs,zbgs) to study the internal implementation - Gather: current API, prop types, internal architecture, delegation patterns
- Usage analysis: which props are actually used, how frequently, what the common patterns are
- Identify the value to port — not the internal structure. Internal architecture reflects internal constraints that may not apply to OSS
- Compare with equivalent components in other design systems (Radix, shadcn, Ant, MUI, Chakra)
- Note naming conventions, API patterns, slot flexibility
- Identify where the industry has converged vs. where there's meaningful divergence
Before proposing an API, exhaustively list the scenarios it must handle. This prevents designing for the happy path and discovering gaps during build.
Every component should cover at minimum:
| Case | What it tests |
|---|---|
| Simple/default | The 80% use case. Zero config. Does it just work? |
| Configured | Custom options beyond defaults. Does configuration feel natural? |
| Controlled | External state management. Can the consumer own the state? |
| Composed | Inside Dialog, Table, AppShell, Card. Does it play well with siblings? |
| Edge/mixed | Mixed modes, dynamic switching, responsive behavior — the scenario that reveals friction |
| Migration | What does adopting this look like for someone with nothing today? |
Add domain-specific cases as needed. The use case list becomes the test battery for API Arbitration and the acceptance criteria during build.
Write the spec document covering:
- The problem — what gap this fills, with usage data
- Use cases — the full enumeration from Phase 4
- Architecture — how it composes with existing components, delegation hierarchy
- API — props, types, defaults, with rationale for each decision
- Behavior — interaction patterns, keyboard, accessibility, state management
- Key decisions — tradeoffs made and why, with alternatives considered
- Composition stories — how it works inside Dialog, Table, Card, List, AppShell
Before review, map every proposed export against what already exists in Astryx:
- Flag any sub-component that duplicates an existing component (e.g., Separator = Divider)
- Identify anything useful outside this component family as a standalone extraction (e.g., Kbd)
- Challenge thin wrappers — if it's just styling an existing component, use the existing component
Apply the spec review protocol (6 phases):
- Read the full spec
- Identify which component family this belongs to (alerting, input/form, action/menu, navigation/data, layout)
- Compare prop names against the RIGHT component family (not random components)
- Check existing Astryx naming conventions:
| Pattern | Convention | Examples |
|---|---|---|
| Primary callback | onChange |
TabList, Selector, Switch |
| Visual mode | variant |
Button, Badge, Banner |
| Accessible name | label |
Button, Popover, MoreMenu |
| Flexible end slot | endContent |
ListItem, SideNavItem, TopNav |
| Input validation | status: {type, message} |
Field, TextInput |
| Async action | onChangeAction |
Selector, Switch |
- Do prop names match industry conventions?
- Any naming collisions with natural language priors?
- What do Radix, shadcn, Ant, MUI, Chakra call the equivalent?
-
Positional > semantic for flexible slots (
endContentnotaction) - Relax slots for OSS — don't restrict to string when ReactNode works
- No premature flexibility — drop props with 0 usage and no external precedent
- Skip sub-components that duplicate existing ones
- Name for humans, describe for machines — human-friendly name, keyword-rich description
- Don't fight natural language priors
- How does this compose with Dialog, Table, Card, List, AppShell?
- Are defaults right? (Override rate = 0% means right default)
- Flag anything uncertain for vibe test
- Don't debate in the abstract — build it, test it, feel the friction
API shape is determined through API Arbitration — the standard process, not an optional step for contentious cases.
- Explore candidate API shapes (Phase 1 of arbitration)
- Enumerate all use cases the API must handle (Phase 2)
- Test candidates with naive prompts against skill docs (Phase 3)
- Iterate until a clear winner emerges or the team makes a call (Phase 4)
- Measure: override rate (0% = right default), correctness rate, escape hatches, hallucinations
Contributors are expected to drive this work. The team collaborates in the issue thread and applies system-level context. See API Arbitration for the full process and sample prompt.
- Update spec with review feedback and vibe test results
- Create or update tracking issue
- Spec is now the source of truth for the build loop → proceed to Component Build Protocol
- Compose, don't rebuild — use existing Astryx components, don't reimplement
- Family alignment > global consistency — compare within the right component family
- Positional prop names > semantic for flexible slots
- Natural language priors are incredibly strong — work with them, not against them
- Vibe test when unsure — override rate is the cleanest signal
- Internal architecture ≠ OSS architecture — port the value, not the structure
- Relax slots for OSS — ReactNode where possible
- The override rate is the best signal — 0% = right default, 67% = wrong default