- name
- daub-ui
- description
- Build, preview and verify UI with DAUB (daub.dev), a drop-in CSS + JS component library with 91 components and 21 theme families. Covers three paths: plain HTML with db-* classes, json-render or OpenUI Lang specs rendered by daub-render.js, and the hosted DAUB MCP server (generate_ui, validate_spec, render_spec, parse_openui, get_block_library). Includes the rules that prevent broken renders, 266 ready-made blocks, playground share links, a verify loop, and Jev recipes for picking components, blocks and themes. Use when the user mentions DAUB, daub.dev, daub-ui, db- classes, the DAUB playground or MCP, json-render or OpenUI specs, or asks for a themed dashboard, landing page, form or settings screen without a build step.
- license
- MIT
- compatibility
- Any agent that reads files and runs shell or HTTP. The MCP path needs network access to daub.dev. Jev recipes need an OpenRouter API key.
- metadata
- {"daub-version":"3.20.6","homepage":"https://daub.dev"}
# DAUB UI
DAUB is a drop-in CSS + JS component library. `daub.css` styles 91 components through `db-*` classes, `daub.js` wires the interactive ones (tabs, overlays, switches, dropdowns), and 21 theme families give 42 light and dark themes. No build step and no framework.
| Path | You write | Good for |
|---|---|---|
| 1. Plain HTML | `<button class="db-btn db-btn--primary">` | Existing HTML, JSX or template code, static sites |
| 2. Specs | json-render JSON or OpenUI Lang, rendered by `daub-render.js` | LLM output, streaming, UI as data, playground previews |
| 3. Hosted MCP | Tool calls to `https://daub.dev/api/mcp` | Drafting from a prompt, validating and rendering without local tooling |
## Pick a path
- The user has HTML, JSX, Vue or template files: path 1. Look up each component in `references/components.md` (HTML classes) or `https://daub.dev/llms.txt` before you write markup.
- You generate UI from a prompt, or a program consumes the UI: path 2. Write OpenUI Lang when a model writes the spec (about 67% fewer tokens than JSON). Write JSON when code edits the spec.
- You want a server to draft or check a spec: path 3, then edit the returned spec under path 2 rules.
- The user wants React: `npm install daub-react daub-ui` with React and React DOM 19 or later (React section of llms.txt). The React package depends on `@shadcn/react` 0.3.1; build the app with its bundler.
## Path 1: plain HTML
```html
<link rel="stylesheet" href="https://daub.dev/daub.css">
<script src="https://daub.dev/daub.js"></script>
<main class="db-container db-container--narrow">
<div class="db-card">
<div class="db-card__header">
<h3 class="db-card__title">Notifications</h3>
<p class="db-card__desc">Choose what reaches you.</p>
</div>
<div class="db-flex db-flex--col db-gap-4">
<div class="db-field">
<label class="db-field__label" for="email">Email</label>
<input class="db-field__input" id="email" type="email" placeholder="you@acme.com">
<span class="db-field__helper">We send one digest a week.</span>
</div>
<div class="db-switch" role="switch" tabindex="0" aria-checked="true">
<span class="db-switch__track"><span class="db-switch__thumb"></span></span>
Mentions
</div>
</div>
<div class="db-card__footer">
<button class="db-btn db-btn--primary" data-db-modal-trigger="saved">Save</button>
</div>
</div>
</main>
<div class="db-modal-overlay" id="saved" aria-hidden="true">
<div class="db-modal" role="dialog" aria-modal="true">
<div class="db-modal__header">
<h2 class="db-modal__title">Saved</h2>
<button class="db-modal__close" aria-label="Close">×</button>
</div>
<div class="db-modal__body">Your preferences are live.</div>
</div>
</div>
```
- Set the theme on the root: `<html data-theme="nord-light">`, or call `DAUB.setFamily('nord')` and `DAUB.setScheme('dark')`.
- `daub.js` runs `DAUB.init()` on load. After you insert markup later, call `DAUB.init(container)`.
- Overlays (`db-modal-overlay`, `db-alert-dialog`, `db-sheet`, `db-drawer`, `db-command`) need an `id`. Open them with `DAUB.openModal('id')` and friends, or a `data-db-modal-trigger="id"` button.
- Toasts are JS only: `DAUB.toast({ type: 'success', title: 'Saved', message: 'Changes are live.' })`.
- CDN copies: `cdn.jsdelivr.net/npm/daub-ui@latest/daub.css` and `/daub.js`. npm can lag the site; `https://daub.dev/daub.css?v=3.20.6` is the current build.
## Conversation components
The HTML catalog has 91 components; the browser/OpenUI/MCP registry has 89 renderer types. Compose MessageScroller, Message, Bubble, Attachment, Marker, and ChangeSummary with ChatComposer. ChatComposer adds local attachments, dictation, model controls, queue and steer events; your app owns message data, AI transport, approval enforcement, uploads, persistence, and reaction state. See `https://daub.dev/chat-demo.html` and the Chat Components section of `https://daub.dev/llms.txt`.
ChangeSummary is static markup with host-provided `files: [{path, additions?, deletions?, status?}]`, optional title/description, and explicit action children. It needs `daub.css` without `daub.js`. Render paths as plain text and counts with accessible addition/deletion labels. Counts floor finite numbers and clamp rows/totals to `0..Number.MAX_SAFE_INTEGER`; other counts become zero. Empty files default to `No files changed`. Label demo data `Prepared 2 demo files` / `Demo changes`; omit actions unless the host wires them. React `onUndo` / `onViewChanges` render buttons only when supplied; `undoLabel` / `undoDisabled` configure the callback-backed Undo button. DAUB performs no file operations.
- Use stable `data-db-message-id` rows in `db-message-scroller__item`. The native root accepts `data-db-auto-scroll`, `data-db-scroll-position="start|end|last-anchor"`, and `data-db-scroll-peek`.
- `DAUB.createMessageScroller(root, {autoScroll, scrollPosition, scrollPeek})` returns a handle with `scrollToStart`, `scrollToEnd`, `scrollToMessage`, `getState`, and `destroy`. Renderer options use `defaultScrollPosition` and `peek`.
- Chat OpenUI signatures take children first; pass metadata as named props. Attachment children represent actions. Bubble and Marker content accept plain text. Keep image and link URLs safe.
## Path 2: specs
A spec is a flat element map. json-render JSON:
```json
{
"theme": "github",
"root": "page",
"elements": {
"page": { "type": "Stack", "props": { "direction": "vertical", "gap": 5, "container": "wide" }, "children": ["header", "kpis", "revenue"] },
"header": { "type": "Stack", "props": { "direction": "horizontal", "justify": "between", "align": "center" }, "children": ["title", "export"] },
"title": { "type": "Text", "props": { "tag": "h1", "content": "Revenue" } },
"export": { "type": "Button", "props": { "label": "Export CSV", "variant": "secondary", "icon": "download" } },
"kpis": { "type": "Grid", "props": { "columns": 3, "gap": 4 }, "children": ["mrr", "churn", "arpu"] },
"mrr": { "type": "StatCard", "props": { "label": "MRR", "value": "$48,210", "trend": "up", "trendValue": "+6.2%", "icon": "dollar-sign" } },
"churn": { "type": "StatCard", "props": { "label": "Churn", "value": "2.1%", "trend": "down", "trendValue": "-0.4 pts", "icon": "user-minus" } },
"arpu": { "type": "StatCard", "props": { "label": "ARPU", "value": "$38.40", "trend": "up", "trendValue": "+$1.10", "icon": "wallet" } },
"revenue": { "type": "ChartCard", "props": { "title": "Monthly revenue ($k)" }, "children": ["revenue-chart"] },
"revenue-chart": { "type": "Chart", "props": { "bars": [
{ "label": "Apr", "value": 39 }, { "label": "May", "value": 41 }, { "label": "Jun", "value": 44 },
{ "label": "Jul", "value": 43 }, { "label": "Aug", "value": 46 }, { "label": "Sep", "value": 48 }
] } }
}
}
```
A settings page in OpenUI Lang, one statement per line:
```openui
__theme = "nord-light"
root = Stack([header, prefs, account, confirm], "vertical", 5, container: "narrow")
header = Stack([Text("Settings", "h1"), Text("Manage how Acme reaches you.", "p")], "vertical", 1)
prefs = Card([mentions, push, digest], "Notifications", "Choose what reaches you and where.")
mentions = Switch("Email me when a teammate mentions me", true)
push = Switch("Push notifications on this device", false)
digest = Select("Weekly digest", [{label: "Monday 9:00", value: "mon"}, {label: "Friday 16:00", value: "fri"}, {label: "Never", value: "off"}], "mon")
account = Card([deleteBtn], "Delete account", "Removes all 14 projects and their files.")
deleteBtn = Button("Delete account", "icon-danger", icon: "trash-2", trigger: "confirm-delete")
confirm = AlertDialog("confirm-delete", "Delete your account?", "You cannot undo this.", footer: [keepBtn, confirmBtn])
keepBtn = Button("Keep account", "secondary")
confirmBtn = Button("Delete account", "primary")
```
Render either one in any page. The parser and renderer ship only on daub.dev (the npm package has neither):
```html
<link rel="stylesheet" href="https://daub.dev/daub.css?v=3.20.6">
<script src="https://cdn.jsdelivr.net/npm/lucide@0.576.0/dist/umd/lucide.min.js" integrity="sha384-b05ba3pt6xaC7F4r130arhf8cF18GH/gKu9JDz/NMf+BhLlBVwIWUdAZSpf1IWRZ" crossorigin="anonymous"></script>
<div id="app" style="padding-block: 24px"></div>
<script src="https://daub.dev/daub.js?v=3.20.6"></script>
<script src="https://daub.dev/daub-render.js?v=3.20.6"></script>
<script src="https://daub.dev/daub-openui-parser.js?v=3.20.6"></script>
<script>
const spec = DaubOpenUI.openUItoSpec(openuiText); // or JSON.parse(jsonText)
document.documentElement.dataset.theme = spec.theme || 'light';
const app = document.getElementById('app');
app.appendChild(renderElement(spec.elements, spec.root, 0));
for (const id in spec.elements) // elements the tree never reached render after it
if (!app.querySelector('[data-spec-id="' + id + '"]')) app.appendChild(renderElement(spec.elements, id, 0));
DAUB.init(); lucide.createIcons();
</script>
```
Full formats: `references/json-render.md` (state, actions, visibility) and `references/openui.md` (syntax, every signature).
## Path 3: hosted MCP
- Claude Code: `claude mcp add daub --transport http https://daub.dev/api/mcp`
- Cursor and other Streamable HTTP clients: `{ "mcpServers": { "daub": { "url": "https://daub.dev/api/mcp" } } }`
- stdio-only clients: `npx -y mcp-remote https://daub.dev/api/mcp`
- No MCP client (pi, scripts): POST JSON-RPC `tools/call` to the same URL (curl recipe in `references/mcp.md`).
Tools: `generate_ui` (prompt to spec + HTML), `get_component_catalog`, `validate_spec`, `render_spec`, `parse_openui`, `get_block_library`. `validate_spec` and `render_spec` take the spec as a JSON string or an object. `generate_ui` picks components with Jev first; `routing.picked_components` shows the pick. Rate limit: 60 requests per minute per IP.
## Golden rules
These rules prevent the failures seen most in generated DAUB UIs.
1. OpenUI arguments are positional in parser order. Check the signature in `references/openui.md`, or pass named args: `Button(label: "Save", variant: "primary")`.
View on GitHub