# Smart UI: answers that show your interface

Your assistant answers with your own interface: a flight list, a fare comparison, a receipt. Try the live demo, then see what is checked and what is tested.

Smart UI makes your assistant answer by rendering your own components instead of plain text. Ask it for flights and get a list you can book, drawn with your tokens and checked before it shows. It comes with OpenDesigner, so there's nothing separate to install. It stays off until you ask for it, or until OpenDesigner sees an assistant in your product, offers it once, and you say yes.

The page has a live demo: a travel assistant with recorded answers, drawn by the real renderer, in 3 design systems. The broken answer it can send is refused by the validator.

In numbers, read from the repository: 26 parts in 6 jobs (answer, data, choose, act, confirm, media), plus 1 made for one kind of product; 69 validator test cases that the Python and browser validators must answer the same way.

## How it works

1. **Scope.** It reads your repo: what the product is, who uses it, where the assistant lives, your framework, your tokens, and the actions your API can take. It proposes what the assistant should answer with (for a travel app: flight results, a fare comparison, a booking confirmation) and you confirm the list. What it can't find says "not known".
2. **Catalog.** Your confirmed parts become a catalog the model reads. It holds words and numbers, and looks only from your token names.
3. **Select.** For each message, only the two or three parts that fit go to the model. A short question gets a text answer.
4. **Check.** The answer is validated before it renders. A mistake comes back to the model as an error it can fix.
5. **Show.** `<od-surface>`, a plain HTML element, draws the part from your library and your `tokens.css`. It works in React, Vue, Svelte or plain HTML.
6. **Audit.** Every part is rendered in every state at phone and desktop sizes, at 200% text, in dark mode and right to left, and checked against the accessibility floors.

## The parts

- **answer** (Say something with its evidence): [Answer with sources](https://opendesigner.dev/library/answer-sources/), [Key facts](https://opendesigner.dev/library/key-facts/), [Summary card](https://opendesigner.dev/library/summary-card/), [Citation list](https://opendesigner.dev/library/citation-list/)
- **data** (Show numbers): [Data table](https://opendesigner.dev/library/data-table/), [Monthly spend chart](https://opendesigner.dev/library/bar-chart/), [Line chart](https://opendesigner.dev/library/line-chart/), [Spend breakdown donut](https://opendesigner.dev/library/donut-breakdown/), [Stat strip](https://opendesigner.dev/library/kpi-strip/), [Progress status](https://opendesigner.dev/library/progress-status/)
- **choose** (Help the person pick): [Comparison](https://opendesigner.dev/library/comparison/), [Result list](https://opendesigner.dev/library/result-list/), [Product card](https://opendesigner.dev/library/product-card/), [Quick replies](https://opendesigner.dev/library/quick-replies/)
- **act** (Collect input or ask before acting): [Form fields](https://opendesigner.dev/library/form-fields/), [Setup checklist](https://opendesigner.dev/library/setup-steps/), [Time slots](https://opendesigner.dev/library/time-slots/), [Approval prompt](https://opendesigner.dev/library/approval-prompt/)
- **confirm** (Say what happened): [Receipt](https://opendesigner.dev/library/receipt/), [Error with retry](https://opendesigner.dev/library/error-retry/), [Empty state](https://opendesigner.dev/library/empty-state/)
- **media** (Show a picture, a place, a file or code): [Image card](https://opendesigner.dev/library/image-card/), [Location card](https://opendesigner.dev/library/location-card/), [File card](https://opendesigner.dev/library/file-card/), [Code block](https://opendesigner.dev/library/code-block/)
- **domain** (Parts made for one kind of product; offered only when the scope step picks them): [Flight results](https://opendesigner.dev/library/flight-results/)

## The rules

- **Text when text is enough**: "When does check-in open?" gets a sentence, not a card.
- **One part per answer**: Unless the person asked for more.
- **Changes wait for a tap**: Book, pay, send and delete are buttons. Pressing one asks first. The model can't press it.
- **Nothing off-system**: No colours, sizes, styles, HTML or links outside your allow-list. Unknown parts are refused, not dropped.
- **Accessibility floors**: AA contrast, 24 px targets (44 px for actions that change data), visible focus, one polite announcement, reduced motion, and no two buttons with the same name.
- **Honest numbers**: A number shown must be in the data the assistant was given. Missing stays missing.
- **Your look wins**: Inside Claude or ChatGPT, the host's look applies only if you turn it on.

## Where it runs

- **Your own product, any framework**: `<od-surface>`, a plain custom element. Tested: Yes, in Chromium, in two systems
- **Agents that speak A2UI v1.0**: The catalog and the renderer. Tested: Messages and catalog pass A2UI's own schemas. Not tried with another A2UI renderer yet
- **Claude, ChatGPT and other MCP Apps hosts**: `show_ui` in the OpenDesigner MCP server. Tested: Against a simulated host. Not tried in Claude or ChatGPT yet
- **Anywhere with no interface**: The same answer as plain text. Tested: Yes

Not tried yet:

- Claude Desktop, claude.ai and ChatGPT with the MCP server. The view passes a simulated host; real hosts are known to differ.
- Other A2UI renderers (the Lit, React and Flutter ones) and CopilotKit.
- A real screen reader. The audit checks names, roles and the live region, which is what one reads, but not how it sounds.

Read more: https://opendesigner.dev/docs/smart-ui/ · Quickstart: https://opendesigner.dev/docs/smart-ui-quickstart/ · Adapters: https://opendesigner.dev/docs/smart-ui-adapters/
