GENERATIVE UI · The future of sales chatbots

Conversations
that sell.

Vitrine is a white-label chat widget where the AI picks from typed, trusted components — live inventory, financing, scheduling — and never writes a line of markup. Questions become cards. Cards become appointments.

0runtime dependencies
7 files≈3,700 lines, no build step
WCAG AAenforced at runtime
76 KBsix font weights, subset

Live demo — not a video

Deterministic planner, no network — it runs entirely in this page. Type or tap. Switching tenants re-brands this page too: the same five tokens.

The registry pattern

The model chooses.
It never draws.

Most “AI on your website” is a prompt away from an incident. Vitrine is built so the unsafe thing cannot be drawn at all.

01 — Plan

A typed registry

The planner reads your inventory and the buyer’s message, then picks components from a registry of JSON Schemas — the same schemas it sees as tool definitions. One source of truth, no drift.

02 — Validate

Schemas say no

Every block is checked before it renders. A finance card without apr and termMonths is refused by the schema. A hallucination isn’t an injection — it’s one dropped card.

03 — Render

Trusted client

A renderer you own turns specs into DOM. Nothing from the model ever reaches innerHTML — not sanitized out, structurally absent. The model decides what to show; you decide how it looks.

This is the entire contract.

A component name and a payload. No HTML, no JSX, no CSS — so your brand, your compliance and your layout can’t be improvised away by a language model having a creative day.

Swap the registry and the same system sells hotels, books clinic appointments, or leases apartments.

// what the planner emitted, last turn
[
  { "component": "unit_carousel",
    "props": { "title": "Under $12,000",
               "units": ["mt09", "z900", "v7"] } },
  { "component": "choice_chips",
    "props": { "options": ["Compare these",
                          "Monthly payments",
                          "Book a test ride"] } }
]

On the wire — exactly what crosses from model to client.

One conversation

From hello to receipt
in eight turns.

Every screen below is a real capture of the widget — the same one running at the top of this page.

White-label

One registry.
Every brand.

Tenants own five tokens — accent, ink, radius, typeface, logo. The host keeps layout, spacing, hierarchy, depth and motion. That split is how fifty brands share one codebase without fifty forks.

The widget re-skinned for an RV dealership: violet accent, dark mode, marine and RV inventory.

Every accent runs a contrast gate. The default warm yellow measures 1.47:1 on white — invisible as text. So the gate darkens it in 4% steps until it passes AA: fills keep the real brand colour #E8D83F, text gets #886C2C at 4.97:1. Measured, never assumed.

$ npm run audit
  PASS  TARGETS   44×44pt minimum, everything labelled
  PASS  TYPE      type scale only, no fractional sizes
  PASS  SPACING   4pt grid
  PASS  CONTRAST  all text ≥ 4.5:1 against its background
  PASS  RUNTIME   no console errors during the funnel

The details

Premium is a hundred
small refusals.

Springs, not transitions.

Motion starts from the current on-screen value and inherits velocity when you reverse it. Feedback lands on pointerdown, never on click. Cards materialise — blur and scale off one spring — instead of fading in.

Layout never jumps.

Every component declares its height, so the loading skeleton occupies exactly the space the finished card will. That single property is most of what separates “premium” from “widget”.

Compliance lives in the renderer.

The finance slider auto-attaches a Regulation Z disclosure, recomputed on every input. Prompt instructions get ignored; a renderer that can’t draw the unsafe thing does not.

The inventory carousel on an iPhone: typed unit cards with real photography, prices and monthly estimates.

Real capture

Typed live,
frame by frame.

Sixty real browser frames, straight out of the widget: a customer types a question, taps a bike, and keeps going. This is the Pale tenant in dark mode — the theme whose accent is deliberately terrible, so you can watch the contrast gate carry it.

From the project README — captured with Playwright, no mockups.

Animated capture: a customer types a question, taps a bike card, and continues the conversation — shown in the Pale tenant theme in dark mode.

Google Research measured an 83–97% preference for generated UI over markdown answers. An ACL 2026 study found ~70% across 76 participants. And nobody has published a controlled A/B inside a production chat widget — we’d rather run yours.

Google Research, 2025
“Generative Interfaces for Language Models”, ACL 2026 Findings

Pricing

Two honest ways
to run it.

The code is MIT either way. Pay for the part that takes taste and time: making it feel native to your brand and your floor.

Self-hosted

$0MIT, forever

seven files · no build step, no dependencies

  • The full widget: registry, renderer, springs, contrast gate
  • Works from file:// — open the demo and it runs
  • Bring your own planner, model and inventory
  • The same audit tooling: npm run audit
Read the source

Either way you keep everything: the widget ships under the MIT license, and the managed plan builds on exactly the code you can read on GitHub today.

Start your setup

Tell us where the widget is going and we'll get back within a day.

Sending opens your email app with everything filled in — or write us directly at rafael@flow-labs.io.