crucıble

Handset

Live 3D device mockup: a studio-lit beveled phone whose screen renders your real React children as DOM. Springs between present, landscape, exploded-stack and back poses (or scroll-scrubs via progress), drag to spin, rises in with a soft contact shadow. Offscreen-paused, reduced-motion-safe, CSS-frame fallback.

r3fpro

Installation

CLI

npx shadcn@latest add @crucible/handset

Manual — install dependencies, then copy the source

npm install three @react-three/fiber @react-three/drei

Props

PropTypeDefaultDescription
childrenReact.ReactNodeLive React content rendered ON the screen as real DOM (focusable, clickable), laid out in a screenWidth-px-wide portrait viewport. Note: it mounts in a separate React root, so app-level context providers do not reach it — pass data in as props.
screenSrcstringImage URL shown on the screen when no children are given (cover-fit).
poseHandsetPose"present". Overridden by progress when setPreset pose.
progressnumberScroll-scrub control, 0–1. Overrides pose: 0 → present, .33 → landscape, .66 → explode, 1 → back, interpolated in between.
finishHandsetFinish"metal" (brushed titanium-silver)Body material.
interactivebooleantrueDrag (or focus + Left/Right arrows) to spin; springs back to the current pose.
pausedbooleanFreeze all motion: no intro, no idle drift, pose changes are instant and drag is disabled. Reduced motion behaves the same (drag stays, damped).
screenWidthnumber300CSS width (px) of the screen's layout viewport.
labelstring"Device mockup"Accessible name for the device region.
classNamestringrelative aspect-[4/5] w-fullExtra classes for the wrapper.
refReact.Ref<HandsetHandle>Imperative handle: setPose(), replay().

Honors prefers-reduced-motion with a designed static fallback, pauses offscreen and on hidden tabs, and passes className through.