Skip to main content

Ouro HX + Ouro AX

Feels right to the people using it. Obvious to the agents building it.

A woman and a little white robot build a big green switch panel on a wooden workbench at dawn, while Ouro, a smiling green snake curled into an O, watches from the glowing trail.

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.10

Adds Checklist. Any other part installs the same way.

v1.3.10 · latest release

Pick 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.

Sunlit water0 fathoms
Speed
Ballast tanks
Fill all three to dive.
Every control here is a real Ouro HX componentSource (opens in a new tab)

Ouro UI has a half for people and a half for agents

Ouro HX · human experience
For the screens people use, like this one.
Ouro AX · agent experience
For the tools agents use, like a CLI or an MCP server.

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.

Spoonjoy's Crispy Lemon Chickpeas recipe with X-ray on: its parts, such as the number field and the Start cooking button, are outlined and labeled with their Ouro HX component names.Spoonjoy's Crispy Lemon Chickpeas recipe in dark mode with X-ray on: its parts, such as the number field and the Start cooking button, are outlined and labeled with their Ouro HX component names.
The Test Kitchen with X-ray on: the outlined parts wear their Ouro HX name tags. Real app, real recipe, real chickpeas.

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.

Meet the neighbours

Ouro's whole world at dawn: Ouro curled on the glowing trail, a toy factory puffing steam, a desk under a blossom tree, a reading lectern, a green card on an easel, two friends and two robots waving from a picnic blanket, and robot sheep grazing by a shepherd's crook.

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.

before-we-dive.tsxLivePearlAbyssBrassChartCoralCookbook
Before we dive
Tick them off. The captain is watching.
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

Corners
: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.

  1. 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">.

  2. 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>.

  3. 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

  1. 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.

  2. Required means required

    What a component needs, like a sheet's title, is a required prop, so leaving it out is a type error.

  3. One state, one prop

    loading handles the spinner, aria-busy, the screen reader announcement and the layout, so nothing else needs setting.

  4. Named appearance

    tone values are the theme's token names (primary reads --primary), and variant and size are named options, so the props speak the theme's language instead of color names.

  5. 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.

  6. 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.

Design syncYou Running late, start without meSent by your agent, on your word
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 your components/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 -tests item 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.

  1. Set up shadcn once

    In an app with Tailwind CSS v4 and an @/* path alias. --base base because Ouro HX is built on Base UI; skip -t in an existing app.

    npx shadcn@latest init --base base --preset nova --yes --no-monorepo

    Then 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.10
  2. Add a component from GitHub

    npx shadcn@latest add ourostack/ouro-ui/sheet#v1.3.10

    The tag pins the newest release. Without it the install fails: main has no registry.json.

  3. 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