Open-source web component
Unfold
An iPhone Duo you can put on any web page. It opens, folds, turns and stands up like the real one, and shows your own screens.
Drag to turn it, click to flip it, or drop a screenshot on it.Drag sideways to turn it, tap to flip it.
Every way it stands.
Seven postures, one attribute each. Every phone here turns and flips too.
Closed
Landscape
Landscape ajar
Portrait
Portrait ajar
Laptop
Tent
The back
Measured, not guessed.
The body follows Apple's published dimensions. Everything else is measured from Apple's own 3D model of the phone and its spec drawings, down to the speaker holes.
- Open
- 164.6 × 117.8 × 5.2 mm
- Closed
- 84.1 × 117.8 × 11.3 mmTwo 5.2 mm halves with a 0.9 mm gap, and the hinge cover standing 1.8 mm out of the side.
- Camera
- A 55.8 × 21.2 mm plateau, 3.6 mm proudTwo lenses in 16.3 mm polished rings that rise another 1.9 mm, with the mic and flash at the inner end.
- Band
- Rounded titanium, 0.7 mm round the inner screenIt rolls 1.3 mm into the back glass and the cover glass, with antenna lines, speaker holes and USB-C where they really are.
- Buttons
- Volume on top, Touch ID and Camera Control on the sideVolume 21 to 45 mm from the corner, Touch ID 34 to 52 mm and Camera Control 77 to 94 mm down from the top.
- Hinge
- No line at the foldThe screen wakes dark and soft as it opens and clears from the side that was hidden, like the real one.
- Finishes
- Star White and Night Sky
Add it to a page.
It is one ES module with no build step and no dependencies. Load it from here, or download unfold.js and host it yourself. The source is on GitHub.
Load the script
<script type="module" src="https://unfold.foo/unfold.js"></script>
Add a phone
Give each screen a picture or a video. The inner screen spans both halves, and a portrait shot on it turns to stand upright when the phone does.
<unfold-device inner="open.png" cover="closed.png" posture="landscape-ajar" finish="night-sky" interactive> </unfold-device>
Move it
Every method returns a promise, so moves can follow one another.
const phone = document.querySelector('unfold-device');
await phone.setPosture('closed');
await phone.setPosture('laptop');
await phone.animateTo({ yaw: 160, pitch: 14 }, { duration: 1200 });
phone.addEventListener('change', e => console.log(e.detail.fold));Reference.
Attributes
| Name | Values | What it does |
|---|---|---|
posture | closed, landscape, landscape-ajar, portrait, portrait-ajar, laptop, tent | How it is held. Changing it animates to the new posture. Default landscape. |
inner | URL | Image or video on the big inside screen. |
cover | URL | Image or video on the outside screen. |
finish | star-white, night-sky | The colour of the frame, back and hinge. |
fold | 0 to 180 | Angle between the two halves of the inner screen. 0 is shut, 180 is flat. |
yaw | degrees | Turns it left or right. |
pitch | -90 to 90 | How far above it you look from. Below zero looks up at it. |
roll | degrees | Turns it on the page. |
interactive | present or not | Drag to turn, click to flip, arrow keys to turn, F to fold. |
click | flip, fold, none | What a click does when it is interactive. Default flip. |
logo | URL | A logo in the middle of the back. None by default. |
zoom | number | Scales the phone within the element. Default 1. |
shadow | none | Hides the shadow underneath. |
label | text | What screen readers call it. Default "iPhone Duo". |
Methods and properties
| Name | What it does |
|---|---|
setPosture(name, options) | Moves to a posture. Same as setting the attribute, but returns a promise. |
open(options) | Unfolds it flat. |
close(options) | Folds it shut. |
toggle(options) | Opens it if it is mostly closed, closes it otherwise. |
flip(options) | Turns it around to show the other side. |
spin(turns, options) | Spins it one full turn, or turns turns. Negative spins the other way. |
animateTo(target, options) | Animates any of fold, yaw, pitch and roll. Options are duration in ms, easing (a CSS easing name or cubic-bezier()) and delay. Resolves true when it finishes, or false if something else moved it first. |
stop() | Stops every animation where it is. |
setScreen(name, src) | Puts a URL, File or Blob on inner or cover. Handy for a file picker or a drop. null clears it. |
fold, yaw, pitch, roll | Read or set the pose right away, with no animation. |
posture, state | The last posture asked for, and the current pose as one object. |
Animations respect reduced motion: they jump straight to the end unless you pass force: true.
Events
| Name | When |
|---|---|
change | Every time it moves. event.detail is the pose: { fold, yaw, pitch, roll, posture }. |
activate | On a click, Enter or Space when it is interactive. event.detail.action says what it did. |
Styling
| Name | What it does |
|---|---|
width, height | The element is a block, 16:10 by default. Size it however you like and the phone scales to fit, in every posture. |
--unfold-metal | Colour of the frame and buttons. Overrides the finish. |
--unfold-back | Colour of the back glass and camera plateau. |
--unfold-hinge, --unfold-antenna | Colours of the hinge cover and the antenna lines. |
--unfold-shadow | How dark the shadow is, 0 to 1. Default 0.3. |
::part() | stage, shadow, screen, back-glass and camera. |
Your own finishes and postures
Both are plain objects. Add one, then use it by name.
import { addFinish, POSTURES } from 'https://unfold.foo/unfold.js';
addFinish('sage', { metal: '#a9b7a5', back: '#c3cdbf', hinge: '#97a393', antenna: 'rgba(60, 70, 58, .4)' });
POSTURES.reading = { fold: 120, roll: 0, anchor: 0.5, lean: 0, yaw: -20, pitch: 10 };<unfold-device finish="sage" posture="reading"></unfold-device>
Notes.
- It is all plain elements and CSS 3D transforms, one matrix each, worked out in JavaScript as it moves. There is no canvas and no WebGL, so screens stay sharp at any size and videos play as they are.
- Each phone builds itself when it comes near the screen and stops drawing when it scrolls away, so a page full of them stays quick. It runs at 60 frames a second in Chrome and Safari.
- Tested in Chrome and Safari, on desktop and on phones. On a touchscreen a sideways drag turns it and a vertical swipe still scrolls the page.
- Free to use under the MIT license. Not affiliated with Apple. iPhone and iPhone Duo are trademarks of Apple Inc.