Phone framePro
A current flagship phone mockup with a pill cutout, status bar, home indicator and safe area insets, in portrait and landscape.
On this page
Installation
Usage
import { PhoneFrame } from "@/components/ui/chatcn/pro/phone-frame"
<PhoneFrame width={320}>
<div className="h-full pt-(--chatcn-safe-top) pb-(--chatcn-safe-bottom)">
<YourChat />
</div>
</PhoneFrame>Children render at a real 393 x 852 viewport and scale with the frame, so a chat sits at the density it has on the device and fills the glass at any width. The hardware is generic: no brand shapes or marks.
Every finish is its own material: a metal ramp lit from the top left, thin bands lit the way a studio key light wraps around a frame, a specular top edge, near black cover glass with a faint reflection (light glass on white), a hairline where the screen meets the glass, and buttons and camera lenses shaded as solids. Fine detail (the polished chamfer, antenna breaks) only draws once it is a few px wide, so small frames stay crisp instead of stacking hairlines. It is all gradients, inset shadows and SVG: no images, no glare. On a dark page the device gets a light hairline edge instead of a heavier shadow.
The screen exposes its safe area as --chatcn-safe-top, --chatcn-safe-right, --chatcn-safe-bottom and --chatcn-safe-left, in viewport px, the way env(safe-area-inset-*) reads on the device. Pad with pt-(--chatcn-safe-top) and friends.
Examples
Orientation
Changing orientation turns the phone a quarter turn into its new layout. In landscape the status bar hides, the cutout moves to the side and the safe area follows it.
Finishes and status bar
Silver, black with glare, white and a bare frame. Pass a PhoneStatusBar with your own time, signal, Wi-Fi and battery. Under 20% the battery turns red; charging turns it green with a bolt, so the state never rests on color alone.
Props
| Prop | Type | Notes |
|---|---|---|
width | number | Rendered width in px. Everything scales from it. Capped at the container |
finish | "auto" | "silver" | "graphite" | "black" | "white" | Hardware material. auto is silver in light, graphite in dark. light and dark still work as aliases for silver and graphite |
shading | boolean | Metal sheen, specular edges, chamfer and glass reflection. false draws flat tones. Default true |
shadow | boolean | Layered contact shadow, plus a floor shadow under upright devices. Default true |
glare | boolean | A soft diagonal reflection on the screen glass. Default false |
viewport | number | CSS width the content renders at before it scales. Smaller makes the UI larger |
screenClassName | string | Class names for the screen, for example dark |
orientation | "portrait" | "landscape" | Default portrait |
statusBar | boolean | ReactNode | Default bar, your own PhoneStatusBar, or none |
homeIndicator | boolean | Default true |
cutout | boolean | The pill camera cutout. Default true |
buttons | boolean | Side buttons. Default true |
PhoneStatusBar
| Prop | Type | Notes |
|---|---|---|
time | string | Default 10:24 |
signal | number | null | Bars, 0 to 4. null hides it |
wifi | number | null | Strength, 0 to 3. null hides it |
battery | number | 0 to 100 |
charging | boolean | Green fill and a bolt |
layout | "split" | "edges" | Around a cutout, or pushed to the ends |
Keyboard
The frame adds nothing to the tab order. Everything inside the screen keeps its own keyboard behavior.
Accessibility
- Hardware, the status bar and the home indicator are
aria-hidden. Only your content is read. - Pass
aria-labelor wrap the frame in afigurewith a caption when the mockup carries meaning.
Motion
- Orientation changes rotate the frame a quarter turn over 300 ms with the default ease out, from the old footprint to the new one.
- Under reduced motion the change is a 150 ms opacity fade with no movement.
- Frames are static. Nothing animates on hover.
- Under
forced-colors: active, the shell and the screen get a system color outline, so the silhouette survives without fills.