Xray Lens

A loupe over a polished UI. Inside the glass the surface peels away to the layer underneath, such as model tokens, raw JSON or latency telemetry, lined up with the parts it explains. A switch floods it over the whole card.

  • Hover
  • Interaction
  • Developer

Uses motion, clsx, tailwind-merge

Open in ChatGPTOpen in Claude
MCMaya Chenmaya@northwind.io
2 min ago
Billing
Frustrated
Urgent

I was charged twice for Pro this month, once on Oct 2 and again on Oct 3. Could you refund the extra $49.00?

Suggested reply94% confident

Hi Maya, sorry about that. I've refunded the duplicate $49.00 charge, so it should be back on your card in 5 to 7 days.

Point at the ticket to see what the model read and returned, or flip the whole card.

Installation

Add the component on its own with the CLI, or copy the file in by hand.

The CLI writes components/ui/xray-lens.tsx and installs motion, clsx and tailwind-merge. It needs the @keyframeui registry in components.json; add it once per project.

npx shadcn@latest add @keyframeui/xray-lens

Examples

Finished sections built with Xray Lens. Each one installs whole, with the component included.

Blueprint

A studio's booking card over its own blueprint: component names, type and spacing tokens on wireframe bounds. `mode="drag"` with more `zoom`; pick the lens up and it lifts. The Design/Blueprint switch sits over the photo.

Design systems

Built from parts, not pixels.

Every screen we ship is made of named components and tokens your team can reuse. Pick up the loupe and drag it across the card to see the system underneath.

A low timber and glass house lit up at dusk

Timber House

Lake Bohinj, Slovenia

4.92
Sauna
Lake view
Sleeps 4
€184 night

Request Trace

An AI search answer over the live trace that produced it: retrieval, generation and citation spans on the request's timeline, and the answer recolored by when each word streamed. `autoPlay` tours the spans on desktop too until the pointer takes over, and `rest={false}` keeps the lens closed before the tour starts.

Observability

Every answer, traced to the millisecond.

How do I rotate API keys without downtime?
Answer

Create the new key and deploy it alongside the old one. Once traffic has moved over, revoke the old key1. Both keys stay valid during the overlap, so no request fails2.

Sources

docs.acme.dev

Rotating API keys

acme.dev/blog

Zero-downtime deploys

status.acme.dev

Incident 4127

The lens tours each span. Point at any part of the answer to take over.

Props

surfaceRequired

TypeReact.ReactNode

The polished UI. It sets the frame's size. Mark parts of it with XrayLensSpot to give them an xray of their own.

xray

TypeReact.ReactNode

What's underneath, laid over the same box and shown only inside the lens: a grid, a code dump, a schema. Spots draw on top of it.

mode

Type"hover" | "drag"Default"hover"

hover: the lens flies to the pointer and trails it. drag: it stays put until it's picked up, and a click elsewhere glides it there. Touch screens always drag.

size

Typenumber | stringDefault200

Diameter of the lens: px, or a percentage of the frame's width like "36%", so it shrinks with the card on phones. It never grows past 90% of the frame's shorter side.

zoom

TypenumberDefault1.06

Magnification inside the lens, around its center, so whatever sits under the crosshair stays lined up. 1 for none.

rest

Type{ x: number; y: number } | falseDefault{ x: 0.5, y: 0.5 }

Where the lens waits, as fractions of the frame. It opens there when the frame scrolls into view and glides back when the pointer leaves. false keeps it closed until the pointer arrives.

autoPlay

Typeboolean | "touch"Default"touch"

Let the lens tour the labeled spots by itself while the frame is in view, pausing on each so its label shows: "touch" on touch screens only, true everywhere, false never. A finger, the pointer or focus takes over; the tour picks up again from the nearest spot. Never with reduced motion.

toggle

TypebooleanDefaulttrue

Show a two-way switch that floods the xray over the whole frame, from the lens or from the switch, so the layer underneath never depends on hover. The magnification settles to 1 under the flood, so everything lines up.

labels

Type[string, string]Default["Surface", "X-ray"]

The switch's labels: the surface first, then the xray.

toggleClassName

Typestring

Classes for the switch. It sits in the bottom-right corner; on a dense card, move it under the frame with top-full right-auto bottom-auto left-1/2 mt-4 -translate-x-1/2.

revealed

Typeboolean

Floods the xray over the whole frame. Leave unset to let the switch manage it.

defaultRevealed

TypebooleanDefaultfalse

Whether the xray starts out covering the frame.

onRevealedChange

Type(revealed: boolean) => void

Called when the switch flips the layers.

reticle

TypebooleanDefaulttrue

Draw a crosshair and rim ticks on the glass.

lensLabel

TypestringDefault"Inspection lens"

Accessible name of the lens. It's focusable: arrow keys move it (Shift for bigger steps) and Home sends it back to rest.

className

Typestring

Classes for the frame. Put the card's radius here so the xray layer follows it. The frame sets --lens-x, --lens-y and --lens-r (px) every frame, so content in either layer can react to the lens.

surfaceClassName

Typestring

Classes for the wrapper around surface.

xrayClassName

Typestring

Classes for the xray layer. It defaults to bg-zinc-950 text-zinc-100, so the surface never shows through.

lensClassName

Typestring

Classes for the glass, e.g. a different ring-* color for the rim.

labelClassName

Typestring

Classes for the label that names the spot under the lens.

spot.xrayRequired

TypeReact.ReactNode

XrayLensSpot: what this element looks like under the lens. It's drawn in the xray layer in a box with the element's exact position, size and corner radius, and follows it through layout changes.

spot.label

Typestring

XrayLensSpot: named under the glass while the lens is centered on this element. The smallest spot under the center wins.

spot.xrayClassName

Typestring

XrayLensSpot: classes for the spot's box in the xray layer, e.g. a dashed outline.

spot.as

Type"div" | "span"Default"div"

XrayLensSpot: the wrapper element. Use span (with inline-block) to mark words inside a paragraph.

spot.className

Typestring

XrayLensSpot: classes for the wrapper in the surface.

text.text

Typestring

XrayLensText: the text as the surface shows it. Under the lens it turns into its tokens in place: the copy keeps the text's classes, so it wraps exactly the same way.

text.tokens

Type(string | { text: string; p?: number; label?: string; className?: string })[]

XrayLensText: your model's tokens. With p (probability, 0–1) each token is tinted by confidence: green, amber below 0.85, red below 0.5. A label makes the token a spot. If they don't spell out text, the text is split roughly instead.

text.entities

Type{ text: string; label: string }[]

XrayLensText: phrases to outline and label, such as entities the model extracted. Used when the text is split here rather than given as tokens.

text.label

Typestring

XrayLensText: named under the glass. Defaults to the token count.

text.as

Type"p" | "span" | "div" | "li" | "blockquote"Default"p"

XrayLensText: the text's element. Its className styles both the text and its token copy, so keep colors that differ out of it or let the tokens override them.