Die Oberfläche ist lokalisiert; Skill-Beschreibungen und -Dokumente werden per KI automatisch in Ihre Sprache übersetzt. Code, Befehle, Pfade und Fachbegriffe bleiben im Original, maßgeblich ist das Dokument des Autors.
Skills entdecken
Design und Kreativität · SKILL.md

skeleton-extractor

skeleton-extractor

Use when transforming an HTML mockup into a draft design-skeleton.v1 YAML. Walks the DOM via puppeteer-core at 3 breakpoints, extracts bboxes + computed styles (resolved back to declared tokens) + wired interaction handlers, and emits one draft ready for visual-architect review. Runs the Node subprocess under trusted_runner discipline (CB3-compliant: truste…

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill-Dokumentation

GitHub

name: skeleton-extractor description: "Use when transforming an HTML mockup into a draft design-skeleton.v1 YAML. Walks the DOM via puppeteer-core at 3 breakpoints, extracts bboxes + computed styles (resolved back to declared tokens) + wired interaction handlers, and emits one draft ready for visual-architect review. Runs the Node subprocess under trusted_runner discipline (CB3-compliant: trusted runner owns execution, skill only generates draft). Invoked by visual-architect at design-phase freeze; NEVER writes the frozen skeleton itself." disambiguation: Turns an HTML mockup into a DRAFT skeleton by measuring the DOM. Reviewing and FREEZING that skeleton at design time is visual-architect.

skeleton-extractor

Transforms an HTML mockup into a pre-freeze draft design-skeleton.v1 YAML. One skill, one operation (extract), one subprocess, one stdout JSON blob.

When to invoke

Called by visual-architect at design-phase freeze (see design doc §2.5), after the user has approved an HTML mockup. The draft it emits is reviewed by the user, amended (binds_to URIs, bbox confirmations, unresolved-token decisions), then HMAC-signed and written to .design-ledger/skeletons/<screen>.yaml. Skeleton-extractor does NOT freeze — that is visual-architect's job.

When NOT to invoke

  • After the skeleton is frozen — verification is visual-arbiter's job (pure-Python verdict on stdout, CB4).
  • For micro-drift re-approval — that is design-drift-arbiter's job.
  • As a runtime live probe on the built product — subprocess targets only the approved mockup.

Public API

A single op.

extract

python3 skills/skeleton-extractor/scripts/extract.py \
  --mockup /abs/path/to/mockup.html \
  --out    /abs/path/out.draft.yaml \
  [--breakpoints 420,700,1280] \
  [--tokens-path /abs/path/to/index.yaml]

Arguments:

FlagRequiredDefaultMeaning
--mockupyes—Absolute path to the HTML mockup (file read via file:// URL)
--outyes—Where the draft YAML is written (trusted_runner.atomic_write_bytes)
--breakpointsno420,700,1280Comma-list of viewport widths; one extraction pass per width
--tokens-pathnononeOptional path to index.yaml with declared tokens: block, used for getComputedStyle back-resolution
--timeoutno120Node subprocess timeout seconds

Output shape

Draft YAML conforming to design-skeleton.v1 (§2.3):

schema: design-skeleton.v1
draft: true
generated_by: skeleton-extractor@1.0.0
generated_at: "2026-04-24T00:00:00Z"
breakpoints: [420, 700, 1280]
fonts_loaded: true
fonts_ready_max_ms: 412
elements:
  - id: grid_card_nth_of_type_1
    selector: "#grid .card:nth-of-type(1)"
    role: article
    kind: element
    bbox:
      mobile:  {x:16, y:96,  w:388, h:260}
      tablet:  {x:24, y:116, w:320, h:240}
      desktop: {x:48, y:136, w:384, h:220}
    tokens_used:
      background_color: "token://color.paper"
      border_color:     "token://color.ink"
    tokens_raw_per_bp: { ... }           # preserved for the arbiter's audit trail
    interactions:
      - {event: click, binds_to: null}   # user fills during visual-architect freeze
    shadow_dom_opaque: false
unresolved_tokens_report:
  - {selector: ".banner", field: color, computed: "#ac3b3b", breakpoint: desktop}
concerns: []

Flow (HTML mockup path → subprocess → draft YAML)

  1. Python wrapper (scripts/extract.py) parses args, loads declared tokens, checks that /bin/google-chrome and the Node binary exist.
  2. Wrapper spawns node skills/_meta/skeleton_extractor.mjs with a sanitized env (mirrors trusted_runner._run_pytest), a 120s timeout, and a single JSON stdin payload {mockupHtml, breakpoints, tokens}.
  3. Node subprocess (_meta/skeleton_extractor.mjs, ~300 LOC) launches puppeteer-core pointed at /bin/google-chrome. For each breakpoint: setViewport before page.goto(file://mockup, {waitUntil: "networkidle0"}), then await document.fonts.ready + 300ms settle (font-race guard, §2.10). Walks visible elements with role/data-*/event-handler, emits per-element {selector, role, bbox, tokens_raw, interactions, shadow_dom_opaque}.
  4. Node resolves computed tokens_raw back to declared tokens (§2.4 step 4). var(--accent-sun) chains resolve through :root. First exact match wins; unresolved values are surfaced in unresolved_tokens_report — never auto-added to index.yaml (D2 strict).
  5. Node merges per-breakpoint elements by stable selector into one record with a bbox: {mobile, tablet, desktop} map.
  6. Node emits ONE JSON blob on stdout; exit 0 on success.
  7. Python wrapper parses the JSON, serializes to YAML, and writes via trusted_runner.atomic_write_bytes. No partial writes, no stream output, no ledger writes from inside the subprocess.

CB3 compliance

The subprocess is CB3-compliant: trusted_runner (invoked from the Python wrapper) owns subprocess execution and captures stdout; the skill/subprocess only generates a draft. The final persisted file goes through atomic_write_bytes (same helper bob uses for bundles and claims).

Edge cases (§2.10)

Edge caseHandling
Font-loading racefonts.ready + 300ms settle; >2s = warning concern; >5s = blocker concern + fonts_loaded: false
Viewport-unit CSS (vw/vh)setViewport always called BEFORE goto() — deterministic per breakpoint
Pseudo-elementsWalked by DOM iteration; current version records host element tokens only; ::before/::after explicit slots are v2
Shadow DOMOpen roots recursed; closed roots → shadow_dom_opaque: true marker on host (arbiter treats as black box)
Responsive accident vs intentbbox deltas between neighboring breakpoints are recorded but NOT auto-flagged; visual-architect reviews
Animations / transitionsStatic after-settle state only; must_satisfy.known_deferred: [animations] is visual-architect's concern

Failure surface

ConditionExitObservation emitted (category, fingerprint)
/bin/google-chrome missing3external_tool_fail, chrome_absent
node binary missing2external_tool_fail, node_absent
Subprocess non-zero exit2external_tool_fail, exit-<N>
Subprocess exceeds 120s2external_tool_slow, timeout-120s
Malformed stdout JSON2external_tool_fail, malformed_output

All observations are fail-open: claude_observe import errors, runtime errors inside the observation writer, or env-adoption gaps never block the caller. The Python exception still propagates so bob/visual-architect sees the authoritative failure.

Determinism / caveats

  • The extractor output is deterministic given identical inputs (same mockup bytes, same Chrome version, same declared tokens). Networked mockups are out of scope — waitUntil: "networkidle0" is for same-origin static assets only.
  • The draft is unsigned. Only visual-architect signs after user review.
  • Token resolution uses first-match-wins on the flattened token index; declared aliases that collide are a user error surfaced at freeze time, not extraction time.

References

  • Design doc: /path/to/project/docs/plans/2026-04-23-ecosystem-keystone-design.md (§2.3, §2.4, §2.10)
  • Contract map: progress/contract-map.yaml skeleton-extractor component (TS-SE-01..04)
  • Precedent: /path/to/projects/test_flow/test-driver.mjs (puppeteer-core invocation pattern)
  • Subprocess: ~/.claude/skills/_meta/skeleton_extractor.mjs
  • Tests: ~/.claude/skills/skeleton-extractor/tests/
Gespeicherte Skills