{"id":51231191,"url":"https://github.com/opensesh/canvas-to-code","last_synced_at":"2026-06-28T16:30:52.033Z","repository":{"id":357485031,"uuid":"1237168386","full_name":"opensesh/canvas-to-code","owner":"opensesh","description":"Translate design exports (HTML + screenshot) into design-system-aligned code through ten explicit gates. A Claude Code plugin.","archived":false,"fork":false,"pushed_at":"2026-06-06T22:56:35.000Z","size":1842,"stargazers_count":5,"open_issues_count":1,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T00:16:55.315Z","etag":null,"topics":["ai-tools","claude-code","claude-design","claude-plugin","design-handoff","design-systems","design-to-code","designer-tools","developer-tools","figma-to-code","open-session"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/opensesh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-13T00:08:44.000Z","updated_at":"2026-06-06T22:56:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/opensesh/canvas-to-code","commit_stats":null,"previous_names":["opensesh/design-to-code-bridge","opensesh/canvas-to-code"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/opensesh/canvas-to-code","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensesh%2Fcanvas-to-code","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensesh%2Fcanvas-to-code/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensesh%2Fcanvas-to-code/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensesh%2Fcanvas-to-code/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/opensesh","download_url":"https://codeload.github.com/opensesh/canvas-to-code/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensesh%2Fcanvas-to-code/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34896652,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-tools","claude-code","claude-design","claude-plugin","design-handoff","design-systems","design-to-code","designer-tools","developer-tools","figma-to-code","open-session"],"created_at":"2026-06-28T16:30:51.462Z","updated_at":"2026-06-28T16:30:52.019Z","avatar_url":"https://github.com/opensesh.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Canvas-to-Code\n\n\u003e Open Session's internal bridge from a design export to BOS-aligned production code. Eleven gates catch off-brand drift before it ships and turn every handoff into a tracked dashboard entry.\n\n[![tests](https://github.com/opensesh/canvas-to-code/actions/workflows/test.yml/badge.svg)](https://github.com/opensesh/canvas-to-code/actions/workflows/test.yml)\n[![version](https://img.shields.io/badge/version-0.5.0-blue.svg)](./CHANGELOG.md)\n[![Claude Code plugin](https://img.shields.io/badge/Claude%20Code-plugin-orange.svg)](https://docs.claude.com/claude-code)\n\n```\n┌──────────────────────┐     ┌──────────────────────────────┐     ┌─────────────────────────┐\n│   Design             │     │   Conversion                 │     │   Codebase              │\n│   ─────────────      │     │   ───────────────            │     │   ─────────────         │\n│   Paper (preferred)  │     │   0. Intake                  │     │   app/\u003croute\u003e/          │\n│   Claude Design      │     │   1. Materials               │     │   ├─ DS-aligned JSX     │\n│   Figma + UUI    ────┼────▶│   2. DS alignment            │────▶│   └─ token-mapped CSS   │\n│   V0 / Lovable       │     │   3. Target audit            │     │                         │\n│   Webflow            │     │   4. Scope                   │     │   components/           │\n│   Screenshot-only    │     │ ◆ 5. Component mapping       │     │   ├─ base/  (UUI reuse) │\n│                      │     │ ◇ 6. Data binding + schema   │     │   ├─ ds/    (wrappers)  │\n│   review.html        │     │   7. Slice plan              │     │   └─ custom/ (new)      │\n│   + screenshot(s)    │     │   8. Pre-slice               │     │                         │\n│   or an iter folder  │     │   9. Pre-swap                │     │   data/\u003cpage\u003e/\u003csub\u003e/    │\n│   from /paper-design │     │  10. Pre-retro               │     │   ├─ *.schema.json      │\n│                      │     │                              │     │   ├─ *.mock.json        │\n│                      │     │                              │     │   └─ types/             │\n└──────────────────────┘     └──────────────────────────────┘     └─────────────────────────┘\n                                ◆ keystone   ◇ contract for the production DB\n\n                                Every run logged in /canvas-to-code:dashboard\n                                with PR links and a reuse-vs-net-new diff.\n```\n\n---\n\n## What it does\n\nEvery design export from every tool is **off-brand in the same ways** — raw hex colors, off-brand fonts, pixel-literal spacing, `ring-2` instead of `shadow-focus-ring`, no token mapping. Manually fixing the same things on every handoff is slow and lossy.\n\nCanvas-to-Code makes the translation deterministic. The PM agent walks each handoff through eleven gates, refuses to advance when one fails, and writes everything back to `.canvas-to-code/state/\u003cfeature\u003e/status.json` so you can resume any time.\n\nTwo gates do the heavy lifting:\n\n- **Gate 5 — Component mapping (keystone).** Every visual unit in the design gets a tier (`base` / `ds` / `custom-shared` / `custom-page` / `net-new`), a target file path, a reused-or-new flag, and a confidence score. Low-confidence rows block until you acknowledge them. The mapper pushes every unit as high up the reuse stack as it can — reusing a UUI primitive is always cheaper than building a new custom component.\n- **Gate 6 — Data binding + schema.** Each unit gets a data source: `backend` (wire to an existing service in `lib/services/`), `mock` (emit a JSON Schema + mock JSON + TypeScript interface under `data/\u003cpage\u003e/\u003csubpage\u003e/`), or `none` (decorative). The schemas emitted here are the **durable contract** between design and engineering — they become the basis for the production database schema. Designers iterate against the mock; engineers wire the real hook later without touching layout.\n\nThe slice plan that comes out the other side ships in 4–6 small PRs (scaffold → header \u0026 nav → cards → wire data → secondary → swap), and `/canvas-to-code:dashboard` aggregates every feature you've ever bridged into one view, with a component +/− diff showing whether reuse is trending up over time.\n\n---\n\n## Who uses it inside BOS\n\n- **Designers** kicking off a handoff. Drop a Paper iter (or an HTML export + screenshot) under `.claude-design/\u003cfeature\u003e/`, run `/canvas-to-code:start`, and the PM agent walks you through Gate 0 in plain English. No engineering knowledge required to begin.\n- **Engineers** implementing the handoff. `/canvas-to-code:start --feature \u003cname\u003e` auto-advances through Gates 3–7 to produce the component-mapping table, the data-binding plan, and the slice plan. `/canvas-to-code:start --gate 8` runs slice preflight + drafts the PR body.\n- **Reviewers.** `/canvas-to-code:start --pr \u003cnum\u003e` validates a slice PR against the plan and returns PASS/REVISE with `file:line` citations.\n- **Anyone curious about the system's health.** `/canvas-to-code:dashboard` shows every bridged feature, its PRs, its LOC, and the reuse vs. net-new component diff.\n\n---\n\n## The eleven gates\n\n| Gate | Name | Failure mode |\n|---|---|---|\n| 0 | **Intake** | Five-question conversation with the PM. Saves and exits cleanly if materials aren't ready. |\n| 1 | **Materials present** | HTML export + ≥1 screenshot required (or a v2-compliant iter folder). PM walks you through capture if missing. |\n| 2 | **DS-alignment of source** | Records translation-cost warning for non-canonical sources (Paper passes; generic HTML doesn't). Warn-only. |\n| 3 | **Target surface audit** | Auditor agent reports current route status, behaviors to port, sub-routes. |\n| 4 | **Scope confirmation** | Routes added/dropped, behaviors to port, header \u0026 nav decisions — all explicit. |\n| 5 | **Component mapping** ◆ | The keystone. Every visual unit categorized base/ds/custom-shared/custom-page/net-new with a target path and confidence. Low-confidence rows must be acknowledged. |\n| 6 | **Data binding** ◇ | `backend` / `mock` / `none` for every unit. Emits JSON Schema + mock JSON + TS interface under `data/\u003cpage\u003e/\u003csubpage\u003e/`. This is the spec the production DB schema is built from. |\n| 7 | **Slice plan** | Default rhythm: scaffold → header \u0026 nav → cards → wire data → secondary → swap. 150–550 LOC per slice. Mock/schema/type files allocated to the slice that consumes them. |\n| 8 | **Pre-slice** | Branch identity, working-tree cleanliness, prior-slice-merged checks before each slice PR. |\n| 9 | **Pre-swap** | Behaviour-port checklist verified, parallel route returns 200s, dropped routes deletion-ready. |\n| 10 | **Pre-retro** | Swap merged, typecheck + build green on main. |\n\n---\n\n## Quick start\n\nAlready installed in BOS. To bring it into another Open Session repo:\n\n```\n/plugin marketplace add opensesh/canvas-to-code\n/plugin install canvas-to-code@canvas-to-code-marketplace\n/canvas-to-code:start\n```\n\nThe PM agent takes you through Gate 0 and writes everything else.\n\n### Updating\n\nRun the bundled command from any consumer repo — it updates by git SHA, so it works even when a release didn't bump the version field:\n\n```\n/canvas-to-code:update          # check + update to latest main\n/canvas-to-code:update --check  # dry run: report version/SHA + commits behind\n```\n\nRestart the Claude Code session afterward so the refreshed agents/skills load.\n\nThe built-in path still works, but `/plugin update` keys off the `version` field and **silently no-ops** when a release ships without a bump (tracked in [#2](https://github.com/opensesh/canvas-to-code/issues/2)):\n\n```\n/plugin marketplace update canvas-to-code-marketplace\n/plugin update canvas-to-code\n```\n\nEvery release bumps `.claude-plugin/plugin.json`'s `version` field, enforced by [`version-check.yml`](./.github/workflows/version-check.yml). `/canvas-to-code:update` doesn't depend on that discipline — it's the reliable path.\n\n---\n\n## What lives in the repo\n\nCanvas-to-Code reads and writes a few well-defined paths inside BOS. None of these need manual editing day-to-day — the PM agent fills them in.\n\n### Design sources — `.claude-design/\u003cfeature\u003e/`\n\nOne folder per feature, committed. Either:\n\n- **Iter shape** (preferred — emitted by `/paper-design`): `\u003csubpage\u003e/paper/iter-NN-\u003cslug\u003e/` with `source-meta.yaml`, `jsx/\u003cpage\u003e.tsx`, and `screenshots/`. The iter IS the handoff; no manual promotion step.\n- **Flat shape** (any other tool): `review.html` + `screenshots/` + an optional `source-meta.yaml` declaring `source:`.\n\n### Per-feature state — `.canvas-to-code/state/\u003cfeature\u003e/status.json`\n\nGitignored. Holds `phase`, `gateLog[]`, `componentMap`, `dataBindings`, `slices[]`, and lifecycle timestamps. Single source of truth for the dashboard. Forward-compatible — older files backfill silently on first read.\n\n### Emitted code — three places in BOS\n\n- `app/\u003croute\u003e/` — DS-aligned JSX + Tailwind classes (token-mapped, focus-ring fixed, no raw hex).\n- `components/base/` · `components/ds/` · `components/custom/` — slotted by the mapper based on tier.\n- `data/\u003cpage\u003e/\u003csubpage\u003e/` — the Gate 6 triple per mocked unit: `\u003cunit\u003e.schema.json` (the contract), `\u003cunit\u003e.mock.json` (designer-iterable data), `types/\u003cunit\u003e.ts` (typed interface). When the real backend lands, the schema migrates to the production DB layer and the mock is deleted.\n\n### Project config — `.canvas-to-code/`\n\nThe BOS instance is already configured. `config.yaml` holds gate severities, slice LOC budget, branch patterns, dashboard output path. `token-map.yaml` holds the hex → semantic-token mappings, font substitutions, and focus-ring pattern that make BOS-aligned output BOS-aligned. Both files are seeded from the templates in this repo and live in the BOS repo, not here.\n\n---\n\n## Command reference\n\nThree commands. Five flags. The PM agent does everything else.\n\n| Command | What it does | Flags |\n|---|---|---|\n| `/canvas-to-code:start` | Universal workflow. With no flags, scans `.claude-design/` and presents a guided menu (resume an active feature, ingest an iter, or start fresh). With flags, dispatches to a specific mode. | `--feature \u003cname\u003e` · `--gate \u003c0-10\u003e` · `--prep` · `--pr \u003cnum\u003e` |\n| `/canvas-to-code:dashboard` | Work timeline: what's done, what's in progress, when it happened, PR + doc links, reuse vs. net-new component diff. Cross-feature by default. | `--feature \u003cname\u003e` · `--json` |\n| `/canvas-to-code:assets` | File inventory under `.claude-design/\u003cfeature\u003e/`: counts by type, paths, presence of standard artifacts. | `--feature \u003cname\u003e` · `--json` |\n\n### Flag cheat sheet\n\n| Flag | Lives on | Purpose |\n|---|---|---|\n| `--feature \u003cname\u003e` | `start`, `dashboard`, `assets` | Scope to one feature. On `start`, resumes from the next pending gate. |\n| `--gate \u003c0-10\u003e` | `start` | Jump to or re-run a specific gate. `--gate 5` re-validates the component mapping; `--gate 6` re-runs data binding; `--gate 8/9/10` runs slice / swap / retro preflight. |\n| `--prep` | `start` | Scaffold `.claude-design/\u003cfeature\u003e/` only — no conversational intake. |\n| `--pr \u003cnum\u003e` | `start` | Validate a slice PR against the plan; spawns the reviewer agent. |\n| `--json` | `dashboard`, `assets` | Emit machine-readable output. |\n\n---\n\n## Architecture\n\n| Surface | Count | Notes |\n|---|---|---|\n| **Commands** | 3 | `commands/`: `start`, `dashboard`, `assets`. |\n| **Subagents** | 7 | `canvas-to-code-pm` (Opus, the orchestrator) · `-extractor` (Opus) · `-auditor` (Sonnet) · `-mapper` (Opus, keystone) · `-data-binder` (Opus) · `-planner` (Opus) · `-reviewer` (Sonnet). |\n| **Skills** | 2 | `canvas-to-code-guardrails` (auto-activates in `.claude-design/` projects) · `canvas-to-code-vocabulary` (tier model + slice rhythm). |\n| **Hooks** | 2 | `pre-commit-guardrails.sh` (opt-in hard gate) · `post-merge-cleanup.sh` (opt-in slice-branch + worktree cleanup). |\n| **Templates** | 7 + 10 | Plan, slice/swap/retro PR bodies, designer handoff, spike, dashboard; plus 10 per-gate failure messages. |\n\nThe PM agent **never writes feature code**. It owns the conversation and the gates; engineers own the production code.\n\n---\n\n## Source shapes\n\nThe bridge accepts two source shapes. The Gate 0 picker treats them as peers.\n\n### Iter shape (preferred, emitted by `/paper-design`)\n\nA subfolder ending in `iter-NN-\u003cslug\u003e` with a v2-compliant `source-meta.yaml`. The producer skill emits this shape directly — the iter IS the handoff.\n\n```\n.claude-design/\u003cfeature\u003e/\u003csubpage\u003e/\u003ctool\u003e/iter-NN-\u003cslug\u003e/\n  source-meta.yaml         ← metaVersion: 2 + the seven required fields\n  notes.md\n  jsx/\u003cpage\u003e.tsx           ← pre-extracted JSX\n  screenshots/01-*.png\n```\n\nWhen you pick an iter at Gate 0, the bridge snapshots `source-meta.yaml`, the JSX, and the screenshots into `.canvas-to-code/state/\u003cfeature\u003e/source-snapshot/` (Gate 1). Gate 5a then short-circuits the HTML extractor and copies the snapshot JSX straight to `/tmp/\u003cfeature\u003e-template.tsx` — the lossiest gate in the pipeline becomes a no-op.\n\n#### source-meta v2 schema\n\nThe canonical spec lives at [`.claude/skills/paper-design/SKILL.md`](https://github.com/open-session/BOS-3.0/blob/feat/frontend-only-rewrite/.claude/skills/paper-design/SKILL.md) in the BOS repo. Seven required fields:\n\n```yaml\nmetaVersion: 2\nsource: paper          # paper | claude-design | figma | v0 | lovable | webflow | screenshot-only | generic-html\nfeature: brand-hub     # URL path segment 1\nsubpage: colors        # URL path segment 2, or 'home' if URL is the feature index\ntargetRoute: /brand-hub/colors\njsxPath: jsx/brand-hub-colors.tsx       # relative to the iter folder\nprimaryScreenshot: screenshots/01-baseline.png\n```\n\nIters missing `metaVersion: 2` fail Gate 1 loudly and point at the producer skill for backfill.\n\n### Flat shape (any other tool)\n\nA feature subfolder with:\n\n```\n.claude-design/\u003cfeature\u003e/\n  review.html              ← design export (Claude Design, Figma, V0, Lovable, Webflow, generic HTML)\n  screenshots/             ← one or more PNGs\n  source-meta.yaml         ← declares `source:` when auto-detect is ambiguous\n```\n\nThe discriminator reads the first 2 KB of `review.html`, returns `{ type, shape: \"flat\" }`, and the extractor parses HTML → JSX.\n\n### Per-source signatures (flat shape only)\n\n| Source | Discriminator signature | Decoder | Day-1 |\n|---|---|---|---|\n| Paper | (iter-only — no flat signature) | Iter short-circuit at Gate 5a | ✅ |\n| Claude Design | `__bundler/template` script tag with embedded char-dictionary | Bundle decode → flat JSX | ✅ |\n| Figma Export-to-Code | `data-figma-*` attrs OR `figma.com` in `\u003cmeta\u003e` | Flatten inline styles → JSX, strip Figma class names | ⏳ |\n| V0 / Lovable | `.tsx`/`.jsx` extension OR `v0.dev`/`lovable.dev` comment | Pass-through, sanitize imports | ⏳ |\n| Webflow | `\u003clink href=\".webflow.css\"\u003e` or `data-wf-*` | Convert HTML+CSS → JSX+Tailwind | ⏳ |\n| Screenshot-only | No `review.html`, PNGs in `screenshots/` | No decode — extractor returns \"visual-reference only\" | ✅ |\n| Generic HTML | Default fallback | Treat as V0 case | ✅ |\n\n---\n\n## Status\n\n**v0.4.0 — Iter folders as first-class sources.** The Gate 0 picker scans `.claude-design/` for v2-compliant `iter-*` folders alongside active features and loose materials, then presents one unified menu. Selecting an iter auto-fills Gate 0 from `source-meta.yaml`. Gate 1 snapshots the iter; Gate 5a short-circuits the HTML extractor. `status.json` gains `subpage`, `sourceShape`, and `sourceIterPath` — additive only. Adds `paper` to the source enum (DS-aligned pass at Gate 2). Consumer-repo state directory renamed to `.canvas-to-code/`; PM auto-migrates on first spawn. Flat shape still works unchanged.\n\n**v0.3.0 — Three commands, five flags.** Surface collapsed from 10 commands to 3 (`start`, `dashboard`, `assets`), with five flags (`--feature`, `--gate`, `--prep`, `--pr`, `--json`). `start` opens with a guided discovery scan so the first thing you see is what already exists. `status.json` gains lifecycle timestamps; the PM backfills silently on older files.\n\nCanonical spec: [BOS-3.0 code-bridge spike](https://github.com/opensesh/BOS-3.0/blob/main/docs/spikes/design-system/2026-05/2026-05-12-code-bridge-standardization.md).\n\n---\n\n## Contributing\n\nThis plugin is maintained inside Open Session for the BOS codebase. Issues and PRs welcome from the team. The test suite (`node --test tests/*.test.mjs`) must stay green.\n\n---\n\n## Acknowledgements\n\n- [KARIMO](https://github.com/opensesh/KARIMO) — PRD-driven autonomous development; this plugin lifts its PM-agent + status.json + worktree-cleanup patterns.\n- [Untitled UI](https://www.untitledui.com/), [React Aria](https://react-spectrum.adobe.com/react-aria/) — the primitive layer that makes BOS's `components/base/` viable.\n- [Claude Design](https://www.claude.com/product/claude-design), [Anthropic](https://www.anthropic.com).\n\n---\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopensesh%2Fcanvas-to-code","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopensesh%2Fcanvas-to-code","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopensesh%2Fcanvas-to-code/lists"}