Sorb — Figma-to-React design-token bridge
A design-token bridge between Figma and React: a Figma plugin plus a multi-tenant SaaS control plane that previews proposed tokens in a running app before merge. Published @sorb npm packages, a no-install interactive tour at try.sorbcloud.com, and MCP servers so agents can drive it.
What I built
A design-token bridge between Figma and React: a Figma plugin, a React SDK, a bridge server, and a capture/resolver layer on a shared contract, plus a hosted, multi-tenant control plane. Edit a token in Figma and see it live in your running React app before you merge.
It runs two ways from one codebase: a free, local, single-tenant loop (`sorb dev`) and a paid, hosted, multi-tenant platform. The same binary flips modes.
- The connectivity layer: framework adapters for six UI stacks, one-click browser pairing for the plugin, live diagnostics that name the failing side, one-click GitHub pull requests, and hosted + local MCP servers for agents.
Where it came from
Sorb wasn't a cold start. It's the productized version of a pattern I'd already proven and a pain I'd already lived at BuzzFeed.
- The architecture, proven: for the Callout platform I designed a JSON contract where one data structure served four jobs: form generation, validation, live preview over browser events, and the production render. A teammate built the renderer's first pass against that contract, and it held across the team boundary. Sorb generalizes the same shape: a design surface pushes changes over an event channel, and you preview them in the real renderer.
- The gap, seen formally: I worked with our VP of technology, a senior engineer, and design leadership to evaluate how Figma could stay connected to our codebase, and we found the existing tooling lacking core features. On my own time I started building and evaluating ways to keep the two in sync. That work became Sorb.
- The pain, felt personally: shipping a shared-component change meant a version bump, a publish, then updating and deploying up to three consuming services just to see it (Foundation UI has the full story). Sorb's core promise erases my own old bottleneck: see a proposed token change in the running app with no publish and deploy cycle.
How it works
1. A designer opens the Sorb plugin in Figma and clicks sign in. The browser opens, a device pairing hands the plugin a scoped key, and it's connected. No servers, URLs, or keys to paste. (The free local bridge still works with zero account for self-hosted setups.)
2. The designer edits a token variable and clicks Preview. The plugin posts the proposed tokens to the bridge.
3. The React SDK's provider fetches them, applies them as CSS custom properties on `:root`, and live-polls for updates, so the running app re-themes in place. Framework adapters carry the same tokens into Tailwind, MUI, Mantine, PrimeVue, and Angular Material apps.
4. If the bridge isn't reachable it falls back silently to the committed tokens: preview never breaks production. And when a preview fails, diagnostics name which side owns it. The SDK answers a ping with a fingerprint, and a diagnosis panel points at plugin, bridge, app, or cloud.
5. A token version ships as a GitHub pull request with one click.

How I built it
Polyrepo, by design. A tree lifecycle (Seed, Juice, Leaf, Canopy) plus a shared Core contract and a Cloud control plane, each its own repo so any package can be public or private independently. JavaScript only (types are JSDoc typedefs), built with esbuild. Never TypeScript, with one deliberate exception: the hosted Cloud plane is a Next.js app.
- Juice is the bridge, a Hono server (bin `sorb`, port 7777) that's a dev-dependency only and never touches production. The same binary turns on auth, entitlements, and scoped CORS the moment a database URL is set.
- Seed captures Storybook via Playwright and resolves tokens through Style Dictionary (DTCG).
- Canopy is the Figma plugin (sandboxed `code.js` + iframe `ui.html`); Leaf is the React SDK (provider + hooks).
- MCP servers expose the whole thing so agents can drive it.

Questions this project answers
You describe yourself as working at the seam where design meets engineering. What does that look like in practice? Sorb is that seam made into a product. The problem it solves is that a token approved in Figma gets re-typed by hand in code and quietly drifts, and nobody notices until users do. So instead of anchoring on the design file, I anchored on the running app: a designer edits a token in Figma, the plugin posts it to a local bridge, and the React SDK applies it as CSS custom properties so the real components re-skin live before anyone merges. It gives design and engineering one shared source of truth they can both see moving.
When AI proposes a change, how do you tell a good one from a merely plausible one? By checking it against something that can't lie. In Sorb every AI feature rides the live binding graph and is verified against the actual running app, so a proposed token change is judged by what it does on real components, not by the model's confidence. For the first write feature, a contrast fixer, the judge is the deterministic WCAG contrast math, not the LLM. The running app and the math are the oracle; the model only proposes.
What does responsible AI look like when the model is confidently wrong? The loop is AI proposes, a human reviews, and the live app verifies, with no auto-apply anywhere. I was also careful not to overstate the guarantee: verify is a check-then-merge habit, not an enforced CI gate, and I keep the product copy honest about that distinction. On the reliability side the preview fails safe by design. If the bridge isn't running, the SDK silently falls back to the committed tokens, so an experiment can never break production.
Is Sorb actually shipped, or still a prototype? The core is shipped: four @sorb packages published to npm through OIDC trusted publishing, a React SDK and Figma plugin in real use, and a multi-tenant hosted platform running live with a public demo on self-hosted infrastructure. I'm honest about what's still branch-local: the deeper AI write features are built and gated, not merged, because I won't market a capability the product doesn't actually run yet. The bridge, the SDK, the plugin, and the hosted plane are real and deployed today.
Results
- Published to npm: @sorb/core, @sorb/seed, @sorb/leaf, @sorb/juice (via OIDC trusted publishing).
- A live multi-tenant hosted platform (app.sorbcloud.com) with a Token Explorer workbench, version compare, and token-version-to-PR shipping.
- try.sorbcloud.com: a no-install interactive tour with the real plugin and a real storefront, the bridge played by a service worker. Ninety seconds, no account.
- Six live demo storefronts (Bootstrap, Tailwind, MUI, Mantine, PrimeVue, Angular Material) re-themed by one token set.
- A hosted MCP server at mcp.sorbcloud.com so agents can read and propose tokens.
- A defined category and positioning distinct from Figma Code Connect: a 100% free, self-hostable core with a hosted moat.
Skills
AI / Agentic
| MCP control surface | Built an MCP read-model over the resolved tokens and their component bindings (listTokens, getToken, bindings) so an agent reads the same ground truth the app renders from. |
|---|---|
| Running-app as the oracle | Designed every AI feature to ride the live binding graph and verify against the actual running app, so a proposed change is checked against reality rather than trusted from the model. |
| AI-proposes, human-verifies loop | Scoped the AI roadmap so the model only proposes token changes; a person reviews and the live app confirms them, with no auto-apply path. |
| Deterministic oracle for writes | Gated the first verifiable AI write (a contrast/a11y fixer) on WCAG contrast math as the judge, keeping the correctness check deterministic instead of leaning on the LLM's word. |
| Agent-built polyrepo | Built the whole toolchain through the studio's spec-first agent pods, down a cost-tiered model ladder with a human owning every merge. |
Frontend
| React SDK | Wrote the Leaf SDK (SorbProvider, PreviewBanner, useToken/useIsPreview hooks) that applies proposed tokens as CSS custom properties on :root and live-polls for updates. |
|---|---|
| Figma plugin | Built Canopy across the Figma sandbox split: code.js reads variables with no network, ui.html fetches over the bridge, the two talking only over postMessage. |
| CSS custom properties | Made var(--token, fallback) the runtime carrier so a token edit re-skins real components in place, and falls back silently to committed values when the bridge is off. |
| Design tokens (DTCG / Style Dictionary) | Modeled tokens in DTCG across primitive/semantic/component tiers and resolved them through a thin Style Dictionary wrapper into a stable {id, cssVar, value, tier, type} shape. |
| Storybook capture (Playwright) | Drove Storybook headlessly with Playwright to capture component artifacts the plugin can re-materialize as real Figma nodes. |
| Auto-binding engine | Wrote a property-affinity binder (fill to bg, stroke to border, text to text, radius to radius) so tokens attach to components with zero hand-authored mapping code. |
| Next.js (Cloud) | Built the hosted control-plane UI in Next.js 15 for orgs, projects, API keys, and billing. |
Backend / Infra
| Hono bridge server | Built Juice, the Hono server on :7777 (bin sorb) that serves preview, verify, and token endpoints and is a dev-dependency that never touches production. |
|---|---|
| One binary, two modes | Wrote the bridge so setting a database URL flips it from local single-tenant to hosted mode (auth, entitlements, scoped CORS) without changing a single endpoint path. |
| JavaScript + esbuild | Kept the four public packages JavaScript-only with JSDoc typedefs, built with esbuild, and pinned a shared @sorb/core contract so independently versioned repos cannot drift. |
| PostgreSQL + Redis | Ran Postgres 16 as the system of record (orgs, keys, entitlements, audit) and Redis 7 for preview payloads and verify reports with tiered TTLs. |
| Clerk + Stripe | Wired Clerk for auth/orgs and Stripe for per-editor billing into the hosted plane. |
| Coolify / Traefik on Hetzner | Deployed the bridge, control plane, marketing site, and live demo on a self-hosted Coolify/Traefik stack on a Hetzner VPS. |
| npm publishing (OIDC) | Shipped @sorb/core, seed, leaf, and juice to npm through GitHub Actions OIDC trusted publishing, no long-lived tokens. |
Leadership
| Founder & sole engineer | Designed, built, and shipped the entire toolchain, plugin, SDK, and hosted platform solo. |
|---|---|
| Category & positioning | Defined the 'design-token bridge for the running app' category against Code Connect, choosing a claim that is falsifiable in the product's favor and honest about being React-only and narrower-but-deeper. |
| OSS + SaaS dual model | Shaped a fully free, offline, self-hostable local loop with a paid hosted moat, one codebase serving both. |
| Honest guardrails | Held the copy to what the product actually does, never claiming an enforced merge gate when verify is a check-then-merge habit. |