# Duo Ready

> Make a website look its best on iPhone Duo, Apple's book-style foldable - closed, opened, turned, in Split View and in the in-app browsers people open links from. Use when someone asks to make a site "ready for iPhone Duo", "foldable-ready" or "look great on the new foldable iPhone", to check a landing page, shop or checkout on the opened screen, or asks why taps fail in the middle of a foldable's screen. Reads pages at every pose, keeps every control off the crease with one isolated block of CSS, handles the side Dynamic Island and in-app browsers, and renders device mockups for client reports and posts.

- Skill: `gurovdigital/duo-ready` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add gurovdigital/duo-ready`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gurovdigital/duo-ready/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: gurovdigital (https://skillmd.com/u/gurovdigital)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/gurovdigital/duo-ready

---


# Duo-ready

A book-style foldable opens into a nearly square screen with a crease down the
middle. Taps on the crease register badly, so any small control that straddles
the centre line is a lost click. This skill reads the site at every pose, finds those controls, moves them
with the smallest possible CSS change, proves the fix, and draws the picture
that shows a client why it mattered.

## What is known, and what is not

- **Geometry.** iPhone Duo opened is 951 x 669 points by App Store Connect's
  screenshot size, or 890 x 626 by the spec sheet's panel resolution. Until the
  device settles it, check both. Closed, it is 466 x 678. The crease is the
  horizontal centre of the opened screen.
- **No web API.** As of Safari 27, WebKit ships neither the Viewport Segments API
  nor the Device Posture API. A page cannot ask where the fold is; it has to
  assume the centre. Native apps get `UIViewReservedRegion`; the web gets nothing.
- **Apple's rule.** Scrolling content - articles, feeds, long rows - may cross the
  crease. Controls must not.

Details and sources: `references/duo-geometry.md`.

## Workflow

### 1. Read the site at every pose

```bash
node scripts/duo-check.mjs https://example.com https://example.com/pricing --out duo-report --shots
```

Needs Node 22+ and Google Chrome (`CHROME_PATH` overrides). It opens every URL at
six poses with touch input emulated, scrolls the whole page so lazy sections
load, and writes `duo-report.md`, `duo-report.json` and, with `--shots`, one
screenshot per hit with the centre line dashed in. Exit code 1 means work to do.

Read every template that carries controls: home, a product or pricing page, the
checkout or signup flow, a blog post.

### 2. Triage

Read `duo-report.md` and sort each finding:

| Finding | Action |
|---|---|
| Control narrower than 240px straddling the centre at `open-890` or `open-951` | Must move |
| Row or card wider than 240px crossing the centre | Leave it - Apple allows this |
| Any pose scrolls sideways | Fix regardless of the fold |

### 3. Fix

Put every change in **one block at the end of the stylesheet**, so it touches
nothing else and can be removed in one piece when Safari ships a real API:

```css
/* Opened book-style foldable: the crease runs down the middle of the page.
   No web API reports it yet, so this covers both published iPhone Duo widths. */
@media (min-width: 880px) and (max-width: 962px) and (pointer: coarse) {
  /* fixes go here */
}
```

The range catches no iPad and not the Galaxy Z Fold 7's inner screen (984px). A
few Android tablets held landscape may match; every fix below is harmless there.

Prefer moves that hold at any width over caps tuned to one:

| Pattern on the crease | Width-independent fix |
|---|---|
| Grid with an odd number of columns | Make it even: `grid-template-columns: repeat(4, 1fr)` puts a gutter on the centre |
| Inline row of keys, chips or links | `padding-right: calc(50% + 2.5rem)` on the row keeps its rule full width and wraps the items short of the centre |
| Row of fixed-size icons whose split lands near the centre | Widen the gap until the split between two items sits on the centre, then check at both widths |
| Field with its button beside it | Stack: `flex-direction: column; align-items: flex-start` |
| Two-column card with its button at the far end | One column: `grid-template-columns: minmax(0, 1fr)`, button `justify-self: start` |
| Side-by-side pair of buttons | `flex-direction: column; align-items: flex-start` |
| List row with a side column pushing controls right | Use the row's own narrow-screen stacked form inside the block |

Do not hide content, shrink tap targets, or change anything outside the block.

One rule outside it, for the closed screen: the Dynamic Island stands on one side
there, so left and right safe areas differ. With `viewport-fit=cover`, give
full-bleed fixed bars `padding-left: env(safe-area-inset-left, 0px)` and the
right-hand equivalent, written inline rather than through a custom property.

### 4. Verify

Run the check again. The bar is **zero narrow controls on the crease at both
`open-890` and `open-951`, and no sideways scroll at any pose.** A fix that holds
at one width and not the other is not done.

### 5. Show it

```bash
node scripts/duo-mockup.mjs https://example.com --scroll ".pricing" --label "Before" --out before.png
# apply and deploy the fix, then:
node scripts/duo-mockup.mjs https://example.com --scroll ".pricing" --label "After" --out after.png
```

`--scroll` takes a CSS selector or a pixel offset; `--width 890` renders the
narrower opened size. Capture "Before" before deploying, not after.

### 6. Check in-app browsers too

Links from Instagram, TikTok, Telegram and LinkedIn open in the app's own
WebKit view, not in Safari, and fixed headers behave differently there. Host
`probe/vp.html` and `probe/vp.js` anywhere, open the page from the app, and read
the numbers. The one rule that matters: **never pad a fixed header by
`env(safe-area-inset-top)`** - measured on iOS 26.5 it is 0px in Safari and 62px
in an in-app view whose toolbar already covers that band, so the padding opens a
dead strip above the navigation.

On a real opened Duo, the same probe shows the true viewport width and a dashed
centre line: one photo of the screen settles 890 against 951 and shows whether
the centre and the physical crease coincide.

### 7. Report honestly

State that the checks ran in emulation, which widths were checked, and that the
result should be confirmed on the device. Name the date. When the Viewport
Segments or Device Posture API ships in Safari, replace the width range with a
real query and delete the guesswork.

