Hybrid apps built on WKWebView have the easiest iPhone Duo migration of any cross-platform stack. The failures are concentrated in a few places, and the checklist below covers them, with each item linked to the detail.
Capacitor iPhone Duo checklist
- Add
viewport-fit=cover. Without it,env(safe-area-inset-*)resolves to zero and nothing below works. - Split every safe-area shorthand into separate left and right values; the two differ on iPhone Duo.
- Replace user-agent layout branching with media and container queries.
- Swap
100vhfor100dvh, which tracks a viewport that changes mid-session. - Replace a cached
window.innerWidthwithResizeObserver. - Test the Split View divider through its full range.
- Design an HTML tab bar on purpose. Native bars move to the side on iPhone Duo; one drawn in HTML stays where your CSS puts it.
- Build with Xcode 27.1. Only the iOS 27.1 SDK reaches the screen edge.
- Treat App Store screenshots as optional for now. Apple lists iPhone Duo sizes, but uploads open later this year.
- Build a native plugin only if the fold matters, for a full-bleed surface such as a map or canvas.
Steps 1 and 2 take an afternoon and account for nearly all of the visible difference. Ionic has published no iPhone Duo guidance, so the Capacitor side of this is our analysis.
If you ship a plain website rather than a packaged app, Websites on iPhone Duo covers Safari specifically — including why it cannot detect the fold at all.
Hybrid apps have the easiest migration of any cross-platform stack, for a reason that has nothing to do with iPhone Duo: the web has assumed a variable viewport since the beginning. A well-built responsive layout adapts to a 626-point display without changes, because it has always had to adapt to everything else.
The failures are concentrated in a handful of places where web code borrowed a native assumption.
Opt into the display
Environment variables report nothing useful until the web view claims the full display:
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
Without viewport-fit=cover, env(safe-area-inset-*) resolves to zero and your layout is inset by the system with no way to draw underneath. This one line is a prerequisite for everything below.
The one real break: symmetric insets
If you fix nothing else, fix this.
/* Wrong on iPhone Duo — one value applied to both edges */
.container {
padding: 0 env(safe-area-inset-left);
}
Apple states safe areas are asymmetric on this device. Applying the left inset to both sides over-pads one edge, and content drifts visibly off-centre on a display wide enough that people notice.
.container {
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
The shorthand form was harmless for a decade because the two values were always equal. Search your stylesheets for it specifically — it is the single highest-yield grep in a hybrid codebase:
grep -rn "safe-area-inset" src/
User-agent sniffing serves the wrong layout
Any code branching on a phone user agent to pick a mobile layout will serve that layout on a regular-width, 626-point display with room for a sidebar.
// Wrong — iPhone Duo is an iPhone with a tablet-sized inner display
const isMobile = /iPhone/.test(navigator.userAgent);
Media and container queries describe the space you actually have:
@media (min-width: 600px) {
.layout { display: grid; grid-template-columns: 260px 1fr; }
}
Container queries are better still where a component’s own width matters more than the window’s — which is often the case in Split View, where the window is smaller than the display:
@container (min-width: 480px) {
.card { flex-direction: row; }
}
Viewport units
100vh has always been awkward on mobile. On iPhone Duo it gets worse, because the viewport changes while the app runs — folding, unfolding, and Split View resizing all change it without a navigation.
/* Fragile */
.screen { height: 100vh; }
/* Tracks the dynamic viewport */
.screen { height: 100dvh; }
dvh, svh, and lvh all behave sensibly here. vh does not.
JavaScript that measured once
CSS reflows on its own. JavaScript that captured a dimension does not.
// Wrong — captured once
const width = window.innerWidth;
// Right
const observer = new ResizeObserver(([entry]) => {
layout(entry.contentRect.width);
});
observer.observe(document.body);
window.visualViewport and its resize event are the other reliable signal, particularly when the keyboard is involved. A window.resize listener works too, but ResizeObserver is cheaper and fires for container changes that window.resize misses.
This matters more than usual on iPhone Duo because resizes are frequent and user-initiated rather than rare and orientation-driven.
What Capacitor gives you, and what it does not
Capacitor’s status bar and keyboard plugins behave normally. Nothing in the standard plugin set needs changing for this device.
What has no web API at all:
- Reserved regions — where the hinge divides the display
- Hinge angle
- Scene accessories — content on the outer display
- The vertical toolbar region
In practice, the missing toolbar region means a tab bar drawn in HTML stays wherever your CSS puts it, while native bars move to a side column on the closed outer display and on the inner display in landscape. That is fine, but design the bar deliberately for the short, wide outer display rather than inheriting a layout made for a tall phone.
A Capacitor plugin wrapping the Swift APIs is entirely possible, and reserved regions would be the one worth building: a plugin that reports division-region frames as CSS custom properties would let a web layout avoid the fold with a grid-template-columns rule. Nobody has shipped this that we are aware of.
For a typical content app, none of that is needed. The CSS-level adaptation above is the whole job.
Aspect ratio
The inner display is roughly 1.42 — close to √2, the A-series paper proportion. This is unusually good for the kind of layout hybrid apps tend to have: documents, feeds, lists, forms, article views. Two-column reading layouts fit it naturally.
Embedded 16:9 video letterboxes, as it does everywhere else on this device. The web idiom of a fixed-aspect embed with content beneath it is already the pattern Apple recommends for native, so most hybrid apps need no change here.