surface-component-scaffold-gen

SKILL.md

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
Weekly Installs
1
First Seen
7 days ago
Installed on
mcpjam1
claude-code1
replit1
junie1
windsurf1
zencoder1