Skip to content
iPhone Duo SupportGet help

Capacitor, Cordova and web views on iPhone Duo

Ingenious Techlab Team

Engineering analysis, not vendor guidance

Capacitor / Ionic has not shipped iPhone Duo support or published guidance for it. Everything below is our analysis of how Capacitor / Ionic behaves against the APIs and behaviours Apple has documented. Treat the Apple-side facts as authoritative and the Capacitor / Ionic-side conclusions as reasoned inference to verify against your own app.

Ionic has published no iPhone Duo guidance. This is our analysis of how a WKWebView-based iOS app behaves against the APIs and behaviours Apple documented on 9 September 2026.

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:

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.

A pragmatic checklist

  1. Confirm viewport-fit=cover is present
  2. grep -rn "safe-area-inset" and split every shorthand into left and right
  3. Replace user-agent layout branching with media and container queries
  4. Swap 100vh for 100dvh
  5. Replace cached window.innerWidth with ResizeObserver
  6. Test the Split View divider through its full range
  7. Consider a native plugin only if your app draws a full-bleed surface where the fold matters

Steps 1 and 2 take an afternoon and account for nearly all of the visible difference.

Related

Want this done for you?

We add iPhone Duo and adaptive-layout support to existing iOS apps, native and cross-platform.