← Blog

Share the IR, not the pixels: a strategy for multi-ecosystem UI prototypes

For interaction-heavy prototypes, share meaning and behavior — domain IR plus a headless controller — and hand-write thin views per ecosystem. Do not aim to auto-convert full UI.

Most “write once, render everywhere” pitches fail because they target the wrong layer.

They try to export pixels — layout trees, component skins, framework-specific event wiring — across React, Solid, Vue, and Web Components. That layer is where ecosystems diverge fastest. It is also where accessibility, theme contracts, and host conventions live. Auto-converting it produces uncanny UI: technically mounted, cognitively wrong.

HCI Nerdz prototypes should share something narrower and more durable:

Do not aim to auto-convert full UI across ecosystems. Share the IR and the controller. Write thin views.

This is a reusable strategy template for interaction-heavy HCI Nerdz work — not a prescription only for pipeline-composer.

The four layers

1. Core — domain IR

The core owns what the system is, independent of any UI framework:

For a processing map, the core answers: “Is this composition legal?” and “What happens to this request?” without knowing whether the user clicked a button or pressed j.

2. Controller — headless interaction state machine

The controller owns how the user moves through the domain:

Think Zag.js or XState shape: explicit states, guarded transitions, effects that mutate the IR through the core API. The controller is framework-agnostic TypeScript (or similar). It emits intent and view-model slices, not DOM.

3. Renderers — pick the surface for the job

Renderers translate view-model + controller events into a concrete surface:

The same IR and controller can back both. The renderer swap is intentional, not a compile target fantasy.

4. Thin framework wrappers

Each host gets a small adapter:

Wrappers stay thin. If logic creeps into the wrapper, it belongs in core or controller.

What not to promise

Do not promise universal UI export. Historical UI languages — UIML, XUL, QML — mostly succeeded as runtimes, not as exporters that faithfully translated arbitrary designs across toolchains. They defined a world and rendered inside it. That is a different ambition from “compile this Figma tree to five frameworks.”

Prior art tackles parts, not the whole picture:

LayerExamplesWhat they share
Headless behaviorZag.js, Ark UIState machines for widgets; host binds DOM
Multi-target componentsMitosis, StencilStructural JSX → several outputs; still not full semantic portability
EncapsulationWeb ComponentsStable element contract; you still author behavior and a11y
OrchestrationXStateExplicit machines; you still build views
Node editorsXYFlow, Rete, JointJS, GoJSCanvas graphs; domain IR often bolted on later
Declarative UI runtimesQML, XULRuntime + language; not open portable cores

HCI Nerdz can compose these parts intentionally rather than chase a mega-spec. See also Fundamental contexts for portable UI for the broader framing.

Product proof without a portable core

Microsoft Power Automate (and Azure Logic Apps, ADF-style designers, and kin) prove enterprise appetite for visual pipelines. Millions of users compose ordered flows in a canvas. That is product proof that processing maps matter — not an open, portable IR you can drop into arbitrary apps.

The lesson is demand, not format:

Our open prototypes should still export — but export meaning (IR + evaluation), not a pixel-perfect clone of Microsoft’s chrome.

Same IR, two renderers

pipeline-composer is the first proving ground:

Both read the same core. Both talk to the same controller. The user skill transfer is in the map semantics, not in whether a step is a <li> or a rounded rect.

That split is the strategy in one sentence: share the IR, not the pixels.

When to use this template

Reach for core → controller → renderers → thin wrappers when a prototype is:

Skip the split for static marketing pages and one-off demos with no reuse story.