Black Box

3D Product Explainer Box for React

A glossy black box that shows its work. Each face is one part of your product, and they open in turn: the panel lifts off its socket, light leaks out round it, and a hairline draws out to a label saying what that part did. Point at a face, tap it or tab to it to open it yourself. In dark mode the box turns to glass, a wireframe with a small lit core that turns inside.

  • 3D
  • Hero
  • AI
  • Diagram

Uses motion, clsx, tailwind-merge

Open in ChatGPTOpen in Claude
material
introtrue(replays the demo)
lift0.15
liftDuration0.42s
liftBounce0.16
<BlackBox />

replays the demo when you let go.

MWhy did order #4471 get a refund?
Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Black Box: A glossy black box that shows its work. Each face is one part of your product, and they open in turn: the panel lifts off its socket, light leaks out round it, and a hairline draws out to a label saying what that part did. Point at a face, tap it or tab to it to open it yourself. In dark mode the box turns to glass, a wireframe with a small lit core that turns inside.
This demo, Agent Decision: A support agent's refund, opened up: what it read, how it reasoned and what it did, one face at a time.

1. Install it:
   npx shadcn@latest add @keyframeui/black-box-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/black-box-demo.json instead.)
   It adds the demo, components/black-box-demo.tsx, and the component, components/ui/black-box.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <BlackBox> on its own.

3. These props set its feel. Change them before you change the file:
   - material (default "auto"): What the box is made of: glossy black, a glass wireframe, or `auto`, black in light mode and glass in dark.
   - intro (default true): Fly the panels in and shut the box the first time it scrolls into view.
   - lift (default 0.15): How far an open panel lifts off its socket, as a share of the box's edge.
   - liftDuration (default 0.42): How long an opening panel takes to lift, in seconds (its spring's visual duration).
   - liftBounce (default 0.16): How much an opening panel overshoots before it settles, from 0 (not at all) to 1.

4. Don't change, in components/ui/black-box.tsx:
   - Panels, lights, lines and labels animate only transform and opacity, through `animate()` on the nodes kept in `parts`, each on its own layer, so nothing repaints while a panel flies.
   - Requests for a face resolve in one order: hover, then keyboard focus, then a pin, then autoplay. A controlled `value` overrides them all.
   - A mouse opens a face only after resting on it for the hover intent, and the face stays open while the pointer is anywhere over the box; leaving waits before it closes.
   - Autoplay holds while the pointer is over the box, focus is inside it, the tab is hidden or the box is under 40% in view, then picks up from the face left open. A click or Esc stops it for good.
   - Reduced motion has no fly-in, lift, turn or autoplay: every change fades, and the first face rests open once hydrated, since `useReducedMotion` is already true in the hydration render.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/black-box.md

Installation

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

The CLI writes components/ui/black-box.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/black-box

Examples

Finished sections built with Black Box. Each one installs whole, with the component included.

Stack

A backend platform's three parts as a list beside the box. Point at a row and its face opens; leave the list and the box walks it again.

Lattice platform

Your whole backend, in one box.
Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Black Box: A glossy black box that shows its work. Each face is one part of your product, and they open in turn: the panel lifts off its socket, light leaks out round it, and a hairline draws out to a label saying what that part did. Point at a face, tap it or tab to it to open it yourself. In dark mode the box turns to glass, a wireframe with a small lit core that turns inside.
This demo, Stack: A backend platform's three parts as a list beside the box. Point at a row and its face opens; leave the list and the box walks it again.

1. Install it:
   npx shadcn@latest add @keyframeui/black-box-stack-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/black-box-stack-demo.json instead.)
   It adds the demo, components/black-box-stack-demo.tsx, and the component, components/ui/black-box.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <BlackBox> on its own.

3. These props set its feel. Change them before you change the file:
   - material (default "auto"): What the box is made of: glossy black, a glass wireframe, or `auto`, black in light mode and glass in dark.
   - intro (default true): Fly the panels in and shut the box the first time it scrolls into view.
   - lift (default 0.15): How far an open panel lifts off its socket, as a share of the box's edge.
   - liftDuration (default 0.42): How long an opening panel takes to lift, in seconds (its spring's visual duration).
   - liftBounce (default 0.16): How much an opening panel overshoots before it settles, from 0 (not at all) to 1.

4. Don't change, in components/ui/black-box.tsx:
   - Panels, lights, lines and labels animate only transform and opacity, through `animate()` on the nodes kept in `parts`, each on its own layer, so nothing repaints while a panel flies.
   - Requests for a face resolve in one order: hover, then keyboard focus, then a pin, then autoplay. A controlled `value` overrides them all.
   - A mouse opens a face only after resting on it for the hover intent, and the face stays open while the pointer is anywhere over the box; leaving waits before it closes.
   - Autoplay holds while the pointer is over the box, focus is inside it, the tab is hidden or the box is under 40% in view, then picks up from the face left open. A click or Esc stops it for good.
   - Reduced motion has no fly-in, lift, turn or autoplay: every change fades, and the first face rests open once hydrated, since `useReducedMotion` is already true in the hydration render.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/black-box.md

X-ray

A secrets vault for AI agents, with a switch that turns the sealed box to glass so you can see the core inside.

Shut to your agents. Glass to you.

Keel keeps every credential your agents use in one sealed box, and lets you see straight through it.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Black Box: A glossy black box that shows its work. Each face is one part of your product, and they open in turn: the panel lifts off its socket, light leaks out round it, and a hairline draws out to a label saying what that part did. Point at a face, tap it or tab to it to open it yourself. In dark mode the box turns to glass, a wireframe with a small lit core that turns inside.
This demo, X-ray: A secrets vault for AI agents, with a switch that turns the sealed box to glass so you can see the core inside.

1. Install it:
   npx shadcn@latest add @keyframeui/black-box-xray-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/black-box-xray-demo.json instead.)
   It adds the demo, components/black-box-xray-demo.tsx, and the component, components/ui/black-box.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <BlackBox> on its own.

3. These props set its feel. Change them before you change the file:
   - material (default "auto"): What the box is made of: glossy black, a glass wireframe, or `auto`, black in light mode and glass in dark.
   - intro (default true): Fly the panels in and shut the box the first time it scrolls into view.
   - lift (default 0.15): How far an open panel lifts off its socket, as a share of the box's edge.
   - liftDuration (default 0.42): How long an opening panel takes to lift, in seconds (its spring's visual duration).
   - liftBounce (default 0.16): How much an opening panel overshoots before it settles, from 0 (not at all) to 1.

4. Don't change, in components/ui/black-box.tsx:
   - Panels, lights, lines and labels animate only transform and opacity, through `animate()` on the nodes kept in `parts`, each on its own layer, so nothing repaints while a panel flies.
   - Requests for a face resolve in one order: hover, then keyboard focus, then a pin, then autoplay. A controlled `value` overrides them all.
   - A mouse opens a face only after resting on it for the hover intent, and the face stays open while the pointer is anywhere over the box; leaving waits before it closes.
   - Autoplay holds while the pointer is over the box, focus is inside it, the tab is hidden or the box is under 40% in view, then picks up from the face left open. A click or Esc stops it for good.
   - Reduced motion has no fly-in, lift, turn or autoplay: every change fades, and the first face rests open once hydrated, since `useReducedMotion` is already true in the hydration render.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/black-box.md

Props

faces Required

TypeBlackBoxFace[]

Up to three faces, in the order autoplay opens them: the top, then the left side, then the right. Each is { id, label, detail?, icon? }: label titles the label and names the face for screen readers, detail is a line or two under it, and icon is engraved in the middle of the panel and lights up when it opens.

value

Typestring | null

The open face's id, or null for none, when you control it.

defaultValue

Typestring | nullDefaultnull

The face that's open at first. None by default.

onValueChange

Type(id: string | null, reason: "autoplay" | "preview" | "pick") => void

Called whenever a face opens, or the box shuts (null), with the reason: autoplay, a preview (hover or keyboard focus) or a pick (click, tap, Enter).

autoplay

Typeboolean | numberDefaulttrue

Open each face in turn, 3.2s each, with a short rest shut between rounds, until the visitor picks one. A number sets the time per face in ms.

material

Type"auto" | "solid" | "glass"Default"auto"

What the box is made of: glossy black, a glass wireframe, or auto, black in light mode and glass in dark.

labels

TypebooleanDefaulttrue

Show the open face's label beside the box on a leader line, or under it in narrow containers. Turn it off when your own copy says which part is open.

intro

TypebooleanDefaulttrue

Fly the panels in and shut the box the first time it scrolls into view.

lift

TypenumberDefault0.15

How far an open panel lifts off its socket, as a share of the box's edge.

liftDuration

TypenumberDefault0.42

How long an opening panel takes to lift, in seconds (its spring's visual duration).

liftBounce

TypenumberDefault0.16

How much an opening panel overshoots before it settles, from 0 (not at all) to 1.

className

Typestring

Extra classes for the root. Set the light's color with --black-box-light (default #4f6bff), e.g. [--black-box-light:#ff5a1f]; glass draws its lines in the text color.