iPhone
A device frame drawn from the real measurements, with a screen you put anything in.
Installation
npx shadcn@latest add @loomui/iphoneUsage
import { IPhone } from "@/components/ui/iphone"<IPhone />On its own it is a blank device with a grey screen. Fill it with a screenshot:
<IPhone src="/screenshot.png" alt="The app's home screen" />Or with live markup, which is the whole reason to build the frame out of elements instead of using a picture of one.
<IPhone width={280}>
<AppShell />
</IPhone>Anything inside is clipped to the screen's radius and sits under the island.
One number, and it shrinks
<IPhone width={360} />width is the only measurement you set, and it is a maximum rather than a
fixed size. The frame fills the space it is given and stops there, so it never
pushes a narrow screen sideways.
Everything else is a real device measurement expressed as a share of the frame's
own width, in container units. The rim, both corner radii, the island, the four
buttons. That keeps the proportions exact at any width. A percentage border-radius would go elliptical on a box this tall. A scale
factor worked out in JavaScript breaks the moment the frame has to shrink.
The rim is a gradient rather than a flat fill. A solid band the same colour all the way round reads as a border. A gradient reads as metal catching light on one side.
<IPhone island={false} buttons={false} />Turn either off for a cleaner shape, useful when the frame is small enough that the detail becomes noise.
The component is not client-side. There is no state and no listener, so it renders on the server and costs nothing on the client.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
width | number | 300 | Largest width the device is drawn at. |
src | string | none | Screenshot to fill the screen with. |
alt | string | "" | Description of the screenshot. |
island | boolean | true | Draw the island at the top of the screen. |
buttons | boolean | true | Draw the buttons down the sides. |
className | string | none | Merged onto the frame. |
Any other <div> prop is forwarded. children fills the screen when there is
no src.
Accessibility
The rim, the island and the buttons are all aria-hidden. They are a picture
of a phone, not content. Give src a real alt describing what is on the
screen, or leave it empty when the screenshot is decoration beside copy that
already says the same thing.