surface-component-scaffold-gen
SurfaceComponentScaffoldGen
Scaffold a Clef Surface headless component $ARGUMENTS with .widget spec (anatomy, FSM states, accessibility, affordance, props, connect, compose), anatomy parts, machine implementation, and suite manifest.
When to use: Use when creating a new Clef Surface headless component. Generates a .widget spec (anatomy, states with transitions, accessibility, affordance, props, connect, compose, invariant), anatomy definition, machine implementation, and suite manifest.
Design Principles
- Behavior-Rendering Separation: Widget specs define behavior (states, transitions, guards). Rendering is handled by framework adapters. They agree only on part names (anatomy).
- Finite State Machine Discipline: Every component is a finite state machine with explicit states, events, transitions, and guards. No implicit state.
- Anatomy Contract: The anatomy defines named parts (root, trigger, content, etc.) that both the machine and renderer reference. This is the only coupling point.
- Props API via connect(): The machine's connect() action transforms internal state into framework-neutral props objects — one per anatomy part.
Step-by-Step Process
Step 1: Register Generator
Self-register with PluginRegistry so KindSystem can track ComponentConfig → SurfaceComponent transformations. Registration is also handled automatically by register-generator-kinds.sync.
Examples: Register the component scaffold generator
const result = await surfaceComponentScaffoldGenHandler.register({}, storage);
Step 2: Preview Changes
Dry-run the generation using Emitter content-addressing to classify each output file as new, changed, or unchanged. No files are written.
Arguments: $0 name (string), $1 parts (string[]), $2 states (string[]), $3 events (string[]), $4 role (string?), $5 requires (requiresconfig?), $6 affordance (affordanceconfig?), $7 props (propdef[]), $8 compose (string[])
Step 3: Generate Clef Surface Component
Generate a complete Clef Surface component scaffold with widget spec ( . widget file including purpose , anatomy , states , accessibility , affordance , props , connect , compose , and invariant blocks ) , anatomy concept , machine implementation , and suite manifest .
Arguments: $0 name (string), $1 parts (string[]), $2 states (string[]), $3 events (string[]), $4 role (string?), $5 requires (requiresconfig?), $6 affordance (affordanceconfig?), $7 props (propdef[]), $8 compose (string[])
Checklist:
- Component name is PascalCase?
- Parts list defines all structural elements?
- States define all machine states?
- Events define all transitions?
- Anatomy lists all parts and slots?
- Machine implementation has spawn, send, connect, destroy actions?
- Suite manifest declares dependencies on surface-core and surface-component?
- All files written through Emitter (not directly to disk)?
- Source provenance attached to each file?
- Generation step recorded in GenerationPlan?
- Has purpose block?
- State machine has an [initial] state?
- All states are reachable?
- ARIA role is specified?
- Keyboard bindings cover Enter, Escape, Arrow keys?
- Focus management (trap, roving, initial) is defined?
- Props have types and defaults?
Examples: Generate a dialog component
clef scaffold component --name Dialog --parts root,trigger,content --states closed,open
Generate a tabs component
clef scaffold component --name Tabs --parts root,list,trigger,content,indicator --states idle,focused,selected --events focus,select,blur
Step 4: Edit the Widget Specification
Open the generated .widget file and refine each section: 1. Write a purpose block describing what the widget does. 2. Define anatomy parts with semantic roles (root, trigger, content, etc.). 3. Design the state machine: states, events, transitions, entry/exit actions. 4. Add accessibility: ARIA role, keyboard bindings (Enter, Escape, Arrow keys), focus management. 5. Define affordance bindings: serves purpose, specificity, when conditions. 6. Add props with types and defaults. 7. Wire connect block: map anatomy parts to attribute bindings. 8. Add compose block for nested widgets. 9. Write invariants describing behavioral guarantees.
References
Supporting Materials
Quick Reference
| Input | Type | Purpose |
|---|---|---|
| name | String | PascalCase component name |
| parts | list String | Anatomy part names (root, trigger, content, etc.) |
| slots | list String | Named slot insertion points |
| states | list String | FSM state names |
| events | list String | FSM event names |
| a11y | { role, ariaProps } | Accessibility configuration |
Output Files:
| File | Purpose |
|---|---|
{name}-widget.concept |
Widget FSM specification |
{name}-anatomy.concept |
Parts contract definition |
suite.yaml |
Suite manifest with dependencies |
{name}-machine.handler.ts |
Machine handler implementation |
Anti-Patterns
Rendering logic in widget spec
Widget spec includes CSS, HTML, or framework-specific code — violates behavior-rendering separation.
Bad:
widget Dialog {
render {
<div class="dialog-overlay"> # HTML in spec!
<div class="dialog-content">...</div>
</div>
}
}
Good:
widget Dialog {
anatomy {
part root # Just names — rendering
part backdrop # is the adapter's job
part content
}
}
Implicit state transitions
Component changes state without explicit events — makes behavior unpredictable.
Bad:
machine {
state open {
# Implicitly closes after 5 seconds — not declarative!
after 5000ms -> closed
}
}
Good:
machine {
state open {
on close -> closed
on timeout -> closed # Explicit event
}
}
Validation
Generate a Clef Surface component scaffold:
npx tsx cli/src/index.ts scaffold component --name Dialog --parts root,trigger,content --states closed,open
Run scaffold generator tests:
npx vitest run tests/scaffold-generators.test.ts
Related Skills
| Skill | When to Use |
|---|---|
/create-theme |
Generate themes to style the component |
/create-concept |
Generate concept specs for custom component concepts |
/create-suite |
Generate suite manifests for component libraries |