App setup
Inspector
X-ray for a page built with Ouro HX: outlines every Ouro component on the page with its name, linked to its docs, and lists them all for keyboard and screen reader users. Each tag names the component's registry item; the bar at the bottom opens the list (a Sheet), every component in document order with its "Docs" and, from sourceHref, "Source" links. Outlines follow their components through scrolling, resizing, overlays and changes to the page, measured once a frame at most; they never take a pointer, so the page stays usable underneath, and only the tags do. X-ray shows only what a person can see and reach: an outline is cut to the part a scrolling box lets show, a tag hides while a menu or a list lies over it, the page behind an open modal dialog is left alone, and a part (a Radio) counts with its component (its RadioGroup). X-ray never draws over the page's text: an outline's edge breaks where a line of text crosses it, and a tag sits where it covers no text and no other tag, or hides. Its bar is a landmark named by label. Escape or Close turns it off, once Escape has closed any other overlay. It renders nothing on the server.
In Spoonjoy
Spoonjoy, a recipe app, is built with Ouro HX (how).
In its source: app/routes/test-kitchen.tsx
Install
Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):
npx shadcn@latest init --base base --preset nova --yes --no-monoreponpx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):
npx shadcn@latest add ourostack/ouro-ui/inspector#v1.2.13Or, with the @ouro namespace set up:
npx shadcn@latest add @ouro/ouro-base @ouro/inspectorIts tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):
npx shadcn@latest add ourostack/ouro-ui/inspector#v1.2.13 ourostack/ouro-ui/inspector-tests#v1.2.13import { Inspector } from "@/components/ouro/inspector/inspector";Paths use shadcn's default @/ alias; the CLI rewrites them to yours.
Example
const [xray, setXray] = useState(false);
<Switch label="X-ray" checked={xray} onCheckedChange={setXray} />
<Inspector open={xray} onOpenChange={setXray} />
<Inspector open={xray} onOpenChange={setXray} sourceHref={(item) => sourceOf(item)} />Use instead when
- Use your browser's developer tools instead when you need more than which component is where; the
data-ouro,data-ouro-controlanddata-ouro-partattributes X-ray reads are there too.
Props
Type: InspectorProps.
docsHref- Type:
((item: string) => string) - The address of an item's docs page, from its registry name, where each tag and the list link. Without it they link to Ouro's public docs,
https://ui.ouro.bot/docs/<item>/. label- Type:
string - Default:
"X-ray" - What X-ray is called, on its bar and as its list's title. Translate it here.
onOpenChange(required)- Type:
(open: boolean) => void - Called with
falsewhen the person closes X-ray (its Close button, or Escape). open(required)- Type:
boolean - Whether X-ray is on: outlines over every Ouro component, and the bar with the list. Pair it with
onOpenChange. sourceHref- Type:
((item: string, element: Element) => string | undefined) - The address of the source that renders a component, if you publish it: the list shows a Source link where this returns one.
Don't use
These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.
isOpen: Not a prop. Ouro UI usesopenwithonOpenChange:<Inspector open={xray} onOpenChange={setXray}>.onClose: Not a prop.onOpenChangegetsfalsewhen X-ray closes:onOpenChange={setXray}.