uSpec Design System Documentation Tool

repository·main·Indexed 42 days ago

https://github.com/redongreen/uspec

uSpec bridges Figma designs with AI agents like Cursor, Claude Code, and Codex to generate design system documentation. It uses the uSpec Extract Figma plugin to produce `_base.json` files, which are then processed via the `uspec-skills` CLI to create canonical component contracts and implementation Markdown guides covering API, color, structure, and accessibility specs.

Tokens
211.1K
Snippets
307
Records
670
Agent score
50%

What's inside uSpec

  1. Scope and limitations of the extract-color skill

    main

    The extract-color skill has specific boundaries regarding what it can and cannot perform. It is important to understand these limits to avoid attempting unsupported workflows.

    Out of Scope

    • Figma Operations: Direct extraction from Figma is not supported, except via the Step 3-delta escape hatch. It cannot import or detach Figma templates.
    • UI Rendering: The skill does not render variant sections, tables, header fields, or preview instances.
    • Figma API Calls: It does not call setExplicitVariableModeForCollection on preview wrappers.
    • Validation: It does not provide visual validation screenshots.
    • Configuration: It does not re-read uspecs.config.json; instead, the orchestrator passes the mcpProvider directly to the skill.
  2. Scope and limitations of the Extract API Data skill

    main

    The extract-api skill is responsible for the interpretation half of the create-component-md pipeline for API data. It processes data to produce structured JSON representations of APIs.

    Out of Scope

    • Extracting data from Figma (except via the Step 3-delta escape hatch).
    • Importing or detaching Figma templates.
    • Rendering tables or previews within Figma.
    • Visual validation screenshots and iterative fix loops.
    • Re-reading uspecs.config.json (the orchestrator passes mcpProvider directly to the skill).

    Troubleshooting Data Quality

    If the produced JSON is missing properties, has incorrect ownership, or violates the schema, check the following:

    1. Reasoning Errors: The logic in this skill or create-api may be flawed.
    2. Source Data Errors: The _base.json provided by the uSpec Extract plugin may be incomplete. Note: You should fix the plugin's phase code rather than duplicating extraction logic within this skill.
  3. Scope and limitations of the extract-voice skill

    main

    When using the extract-voice skill, be aware of the following out-of-scope operations. These actions are not performed by the skill and should not be expected in the output:

    • Figma Operations: The skill does not extract from Figma beyond the Step 3-delta escape hatch. It also does not import or detach Figma templates, nor does it render Focus Order artwork or per-state platform sections in Figma.
    • Slot Insertions: The skill does not apply SLOT_INSERTIONS to live preview instances. Instead, slot insertion plans are recorded for future consumers.
    • Visuals: The skill does not produce visual validation screenshots.
    • Configuration: The skill does not re-read uspecs.config.json; instead, the orchestrator passes the mcpProvider directly to the skill.
  4. Invariants and constraints for the Create Component Markdown orchestrator

    main

    The create-component-md orchestrator follows a strict set of operational invariants to ensure data integrity and batch-mode execution. Developers implementing or extending this workflow must adhere to these rules:

    Data Integrity & Mutation

    • Figma Writing: The uSpec Extract plugin is the only Figma-writer. All interpretation skills are read-only. Any script in an interpretation skill must be < 50 lines; larger scripts require widening the _base.json schema.
    • _base.json Immutability: This file is immutable after the plugin finishes, with one exception: the orchestrator's Step 4.5 may rewrite _base.json._childComposition to override classifications and resolve ambiguousChildren[].
    • Mutation Safety: All Figma mutations must occur on a temporary instance and must be followed by a .remove() call on all code paths. Component sets and shipped variants must never be mutated.
    • Cache Files: The .uspec-cache/ directory is gitignored. Only the sibling .json contract and .md implementation guide should be version controlled.

    Execution Model

    • Batch Mode: The orchestrator runs in strict batch mode from Step 1 to Step 10. Never call AskQuestion or pause for user input. If data is missing, log a _deltaExtractions[] entry (for interpretation skills) or emit a row with provenance: "not-measured" (for structure) and continue.
    • Parallelism: Do not run interpretation skills in parallel within the parent's context. Instead, run them as parallel subagents (one per specialist) after extract-api completes. Each subagent must hold its own _base.json + dictionary read and write to a distinct cache file.
    • Skill Hierarchy: Never call a create-* skill from this orchestrator; they are designed for Figma rendering, whereas this orchestrator targets Markdown.

    Error Handling & Recovery

    • Interruption Recovery: If interrupted between Step 4 and Step 9, existing JSON cache files remain valid. Re-running any single phase is safe.
    • Missing Content: If rendered .md is missing content, check the four domain JSONs, then _base.json. Gaps usually reside in an interpretation skill or _base.json rather than the renderer.
  5. Handle edge cases in screen reader accessibility specifications

    main

    When documenting accessibility for components, follow these specific rules for complex scenarios:

    • Merged Labels/Hints: If a label merges into an input, do NOT list it as a separate focus order entry. Instead, document it as a property on the input's platform table using accessibilityLabel (iOS), contentDescription (Android), or <label for> (Web).
    • Live Regions: Do NOT include live regions in focusOrder as they are not focus stops. Document them using liveRegion or aria-live in the platform tables.
    • Decorative Elements: Exclude decorative elements from both focusOrder and platform tables.
    • SLOT Nodes: Treat a SLOT as a transparent wrapper (similar to an auto-layout FRAME). Focus stops are the children of the SLOT, not the SLOT itself. If a SLOT has a boolean visibility binding (componentPropertyReferences.visible), ensure the focus order accounts for children appearing/disappearing between states.
    • Disabled States: If a component is in a disabled/non-focusable state, the focus stop count is 0. Remove it from the focus order entirely, but document the state's platform properties (e.g., .notEnabled trait, disabled(), or aria-disabled).
    • Merged Parents with Breakout Children: If a container uses mergeDescendants but contains one independently interactive child, list only that interactive child as a focus stop.
    • Ambiguous Merges: If platform behaviors differ (e.g., iOS merges parts but Web keeps them separate), document the superset in focusOrder and explain the differences in the guidelines.
  6. Required Markdown (.md) structure for Create API

    main

    The create-api skill consumes a specific Markdown structure produced by create-component-md. It does not perform extraction; it only renders what is already authored in the following sections:

    • ## API Section: Contains the source of truth for the property contract.

      • General notes: A blockquote (> ...) immediately under the confidence header.
      • Properties table: A table with columns Property | Type | Values | Default | Notes.
      • Sub-component tables: Sections starting with ### {name} containing their own property tables.
      • Configuration examples: Sections starting with ### {name} containing a description and a fenced code block.
      • Referenced components: A ### Referenced components section containing #### {displayName} headers and tables mapping props passed to the child.
    • render-meta block: A fenced JSON block between <!-- render-meta:start v=1 --> and <!-- render-meta:end --> containing:

      • component.compSetNodeId: The target for live previews.
      • propertyDefs: A dictionary mapping raw component-property keys (e.g., "clear button#10225:1") to their definitions.
      • booleanDefs[]: Definitions for boolean properties.
      • slotContents[]: Information for resolving configuration-example slot insertions.
      • fileKey and nodeId: For completion links.
      • sourceHash: For drift detection.
  7. Map Figma variant axes to runtime API properties

    main

    When a Figma variant axis (like state) is decomposed into multiple runtime properties (like validationState and isDisabled), you must provide a mapping. This prevents leaking Figma-specific names into the API.

    Each mapping entry must include:

    • figmaValue: The exact Figma axis value (e.g., "active").
    • apiAssignments: The resulting property values (e.g., { "validationState": "none", "isDisabled": false }).
    • runtimeCondition: A prose description of the condition (e.g., "focused").

    Note on States:

    • Transient interactive states (hovered, pressed) are handled by the platform and should not be documented as API properties.
    • Persistent states (disabled, selected, loading) are API properties and should be documented as booleans.
  8. How the uSpec pipeline works

    main

    uSpec connects AI agents and Figma through a three-stage pipeline that transforms Figma component data into machine-readable contracts and human-readable documentation, which can then be rendered back into Figma as annotations.

    The Pipeline Flow

    1. Extraction: The uSpec Extract Figma plugin captures component data (variants, token bindings, etc.) and produces a _base.json file.
    2. Compilation: The create-component-md tool processes _base.json to compile a canonical .json component contract and a concise .md Markdown guide.
    3. Rendering: create-* render skills read the .md file and its associated render-meta to draw documentation frames (annotations) directly in Figma via the Figma MCP.

    Key Artifacts

    • Contract + Markdown: The canonical JSON serves as the machine source of truth for automation, while the Markdown provides context for people, LLMs, code reviews, and design iterations.
    • Figma Annotations: Rendered frames placed next to components in Figma, ideal for design reviews and spec handoff.
    flowchart LR
        Plugin["uSpec Extract plugin (Figma Community)"] -->|"_base.json"| C["create-component-md"]
        C -->|"canonical .json"| Contract["component contract"]
        Contract -->|"concise .md"| R["create-* render skills"]
        R -->|"annotation frames"| F["Figma"]
  9. Understand Simple vs. Compound component accessibility output

    main

    The create-voice skill categorizes components based on their focus behavior to determine how documentation is rendered:

    Simple Components (1 focus stop)

    Components where all visual parts merge into a single focusable element.

    • Examples: Button, Checkbox with label, Switch, Toggle.
    • Output: Documents one focus stop per state, including platform-specific properties.

    Compound Components (2+ focus stops)

    Components with multiple independently focusable elements.

    • Examples: Text field with trailing clear button, Chip with dismiss action, Tab bar.
    • Output: Includes a focus order section showing the traversal sequence, plus per-state documentation for each individual focus stop.
  10. Repository structure and folder map

    main

    The uSpec repository is organized into source directories and build artifacts:

    Source of Truth (Do not edit build outputs)

    • skills/<name>/SKILL.md: Platform-neutral skill definitions using {{skill:name}} and {{ref:area/file.md}} tokens.
    • references/<area>/*.md: Shared agent instructions and reference material.

    CLI Package (packages/cli/)

    • package.json: Defines the package name uspec-skills and its version.
    • src/: TypeScript source code for the CLI.
    • scripts/build.mjs: The build script that bundles src/ into dist/ and copies skills/ and references/ into templates/.
    • dist/: The bundled CLI entrypoint (build output).
    • templates/: The shipped version of skills and references (build output).

    Other Components

    • figma-plugin/: The Figma Desktop plugin.
    • docs/: Documentation site source.
  11. Choose a Figma MCP provider

    main

    uSpec uses the Model Context Protocol (MCP) as the render channel to move data from an AI agent into Figma. You must choose one of two providers based on your environment:

    1. Figma Console MCP (by Southleft):

      • Mechanism: Connects via a Desktop Bridge plugin running inside Figma Desktop using WebSockets.
      • Capabilities: Exposes 59+ tools for design creation and variable management.
      • Use case: Best for users utilizing the Desktop Bridge workflow.
    2. Native Figma MCP (by Figma):

      • Mechanism: Connects directly to Figma's API.
      • Capabilities: Provides read and write access without requiring a Desktop Bridge plugin.
      • Use case: Best for direct API-based automation.

    Regardless of the provider, the agent uses the .md file as the source of truth for component data, tokens, and styles, using the MCP primarily to render annotation frames and perform small whitelisted reads for verification.

  12. How to resolve tokens and handle composite colors

    main

    When building the working evidence set, tokens must be resolved following a specific priority:

    1. Style Name: If styleId resolves via styles.resolvedStyles[styleId].name.
    2. Variable Name: If boundVariableId resolves via variables.resolvedVariables[boundVariableId], use codeSyntax (WEB preferred) or name.
    3. Hard-coded: If neither is present, token is null (must be flagged in notes).

    Composite Colors: When a node has multiple visible layers (e.g., a gradient or layered fill), a fill-composite entry is used to build compositeDetail. This includes a layers[] array containing:

    • Solid: { type: 'solid', hex, token, blendMode, opacity }
    • Gradient: { type: 'gradient', gradientType, angleDegrees, stops: [{ position, color, token }], blendMode, opacity }
    • Image: { type: 'image', blendMode, opacity }
    // Example resolved colorEntry shape
    colorEntry = {
      element: "Button container fill",
      path: "/fills/0",
      property: "fill",
      hex: "#FFFFFF",
      token: "token.color.white",
      opacity: 100,
      subComponentName: "Button",
      compositeDetail: {
        styleName: "Linear Gradient",
        layers: [
          { type: 'solid', hex: '#FFFFFF', token: 'token.white', blendMode: 'normal', opacity: 100 },
          { type: 'gradient', gradientType: 'linear', angleDegrees: 90, stops: [...], blendMode: 'normal', opacity: 100 }
        ]
      }
    }