Ouro HX + Ouro AX
Feels right to the people using it. Obvious to the agents building it.

Ouro HX is a React component library you copy into your app and own. Its taps, motion and screen-reader announcements are tested in real browsers.
npx shadcn@latest add ourostack/ ouro-ui/ checklist#v1.3.10Adds Checklist. Any other part installs the same way.
v1.3.10 · latest releasePick a coat of paint. The whole page changes with it.
Live Follows your sky, from first light to moonrisePearl Clean white, soft glass and one clear blueAbyss Pale aqua, then a cyan glow in the darkBrass Ivory, walnut and polished brassChart Parchment and ink, like a sea chartCoral Warm sand, kelp and coralCookbook Spoonjoy's own look, synced from the app
Captain Nemo: Welcome aboard my secret sub! Fill three tanks to dive.
Ouro UI has a half for people and a half for agents
Showcases
Two ships, same parts.
Two of the many in the harbour. One is a story we made up to show the parts off; the other is a real recipe app you can open and X-ray.
The Nautilus: built for the story.
Captain Nemo is building a submarine in secret. Help him get her ready to dive, with controls built from Ouro HX.
Spoonjoy: built for dinner.
A recipe app with a look of its own, already cooking in production. Open its Test Kitchen, then turn on X-ray to see the Ouro HX parts inside.


Ouro UI is one corner of Ouro's meadow. There's a desk, a toy factory and a picnic going on just over the hill.

Repaint the whole ship
The parts read a handful of shared colours, so one change repaints the ship: this page, and your app.
Pearl: clean white, soft glass and one clear blue. Copy theme brings :root, .dark and @theme inline, plus the fonts.
- Headings
- GeistGeistSpace GroteskCormorant GaramondChart FellNunitoFraunces
- Corners
- 0.75rem0.75rem1rem0.25rem0.125rem1.25rem0.375rem
- Colours
- Background, primary, accent, highlight and border
3 left before we dive.
Or mix your own paint
:root {
--primary: #0066cc;
--primary-foreground: #ffffff;
--ring: #0066cc;
--radius: 0.5rem;
}
.dark {
--primary: #2997ff;
--primary-foreground: #00172e;
--ring: #2997ff;
}
For agents
Agents read the types
An agent that learned on shadcn/ui writes what it knows. Ouro HX answers where the agent already looks: the type check, the source it just installed, and the console. Every message here is real output from the code, checked against the code on every CI run.
The agent writes
shadcn's destructive variant
<Button variant="destructive">Delete</Button>The type check
error TS2322: Type '"destructive"' is not assignable to type '"solid" | "soft" | "outline" | "plain" | (string & { 'From shadcn? Use tone="destructive" or tone="secondary". default is "solid"; ghost and link are "plain"': never; }) | undefined'.
The installed source components/ouro/button/button.tsx
/** Visual weight: `solid` for the primary action, `soft` for secondary actions, `outline` for a quiet bordered control, `plain` for a borderless one. shadcn's `default` is `solid` (the default), `ghost` and `link` are `plain`, and `destructive` and `secondary` are tones: `tone="destructive"`. Any other string is a type error whose message names the fix. Variant is visual only. For a toggle, pass `aria-pressed`; for the current nav item, `aria-current="page"`. */variant?: | "solid" | "soft" | "outline" | "plain" | (string & { 'From shadcn? Use tone="destructive" or tone="secondary". default is "solid"; ghost and link are "plain"': never }) | undefined;
The console, in development
[ouro-ui] Button: variant="destructive" is not a variant. Fix: <Button tone="destructive">.
The agent writes
A loading flag named the usual way
<Button isLoading>Save</Button>The type check
error TS2322: Type 'boolean' is not assignable to type '{ 'Use <Button loading>': never; }'.
The installed source components/ouro/button/button.tsx
/** Not a prop. Ouro UI uses `loading`: `<Button loading>`. */isLoading?: { 'Use <Button loading>': never };
The console, in development
[ouro-ui] Button: isLoading is not a prop. Fix: <Button loading>.
The agent writes
Radix's asChild for a link
<Button asChild> <a href="/recipes">Recipes</a> </Button>The type check
error TS2322: Type 'boolean' is not assignable to type '{ 'Use <Button href="/path">': never; }'.
The installed source components/ouro/button/button.tsx
/** Not a prop. To render a link, pass `href`: `<Button href="/recipes">`. For other elements, use parts. */asChild?: { 'Use <Button href="/path">': never };
The console, in development
[ouro-ui] Button: asChild is not a prop. Fix: <Button href="/path">.
Rules an agent doesn't have to learn
Props first
A component takes props that describe the whole thing, and building-block parts wait in a separate file for when props can't say what you need.
Required means required
What a component needs, like a sheet's
title, is a required prop, so leaving it out is a type error.One state, one prop
loadinghandles the spinner,aria-busy, the screen reader announcement and the layout, so nothing else needs setting.Named appearance
tonevalues are the theme's token names (primaryreads--primary), andvariantandsizeare named options, so the props speak the theme's language instead of color names.className is for layout
Margins and widths go in
className, and an appearance class gets a one-time warning that names the prop to use.The guessed prop exists
When an agent guesses a common name, it either works or fails the type check with an error that names the real prop, and hovering it in the editor shows the same note.
Ouro AX
Obvious to the agents using it, too
A standard of checkable claims, a kit that meets them and an X-ray that checks any command line, HTTP API or MCP server against them.
Here is one moment, seen from both sides: what you see, and what your agent sees.
You: Tell Design sync I'm running late.
Your agent: Posted in Design sync on your word. Ana and Kim can see it.
notes post abc123 "Running late, start without me"notes: post: other people will see this as the person's, so it needs --why: who asked you, and what they said. Fix: notes post abc123 "Running late, start without me" --why "<who asked you, and what they said>".notes post abc123 "Running late, start without me" --why "Jo Park asked: tell Design sync I'm running late"Posted to "Design sync". Everyone in it can see it.Checked: read the room's last message back.Receipt: m1Next: notes watch abc123 (wait for a reply)
The agent's first try is refused in one line that names the fix, and nothing reaches the room until it says who asked.
Written from where agents went wrong
- 0 of 12
Rules written from principles alone matched none of the 12 wrong turns real agents made.
- from 5 to 10
Two rules written from those wrong turns took the same agent from 5 of 10 runs to 10 of 10.
- from 1 to 2
A wait on the conversation the agent already knew took “tell me when someone replies” from 1 of 2 runs to 2 of 2, in about half the calls.
Install
Copy it in, then make it yours
Designed down to the last tap. And down to the last prop.
Yours to keep
The shadcn CLI copies a component into
components/ouro/with the helpers it imports, and your app’s own Tailwind CSS compiles it.Keeps your theme
Components read shadcn’s theme variables, such as
--primary, and install beside yourcomponents/ui, so you can switch one component at a time.Phones get sheets
A dialog sits centered on a wide screen and turns into a bottom sheet you can drag away on a phone.
Motion, minus the luggage
Motion is CSS, reduced motion included, so your components travel light.
Tests, if you want them
Its tests and stories are an optional
-testsitem that never changes your test tools' versions.Agents read the manual too
llms.txt lists every item and the commands that add it, so your coding agent installs without guessing.
Set up shadcn once
In an app with Tailwind CSS v4 and an
@/*path alias.--base basebecause Ouro HX is built on Base UI; skip-tin an existing app.npx shadcn@latest init --base base --preset nova --yes --no-monorepoThen add the base tokens, once per app. From v0.8.0 it is the only add that writes to your stylesheet, so later adds leave it alone.
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.3.10Add a component from GitHub
npx shadcn@latest add ourostack/ouro-ui/sheet#v1.3.10The tag pins the newest release. Without it the install fails: main has no registry.json.
Or add the @ouro namespace once
It serves the newest release. Put this in
components.json:{ "registries": { "@ouro": "https://ui.ouroboros.bot/r/{name}.json" } }npx shadcn@latest add @ouro/ouro-base @ouro/sheet