El problema real: viewport ≠ pantalla física
Las pulgadas no son píxeles CSS. Entre el device pixel ratio, la interfaz del navegador, la orientación y Split View, no existe una ecuación honesta del estilo «iPhone Duo abierto = exactamente X píxeles CSS». Midewindow.innerWidth y window.innerHeight en el contexto real; no tomes una captura de marketing como especificación técnica.
El siguiente snippet reporta dimensiones del viewport y cambios de tamaño. Úsalo mientras cambias el tamaño de la ventana o alternas modos de multitarea. El marco de abajo es esa misma página, metida en un iframe: arrastra la anchura y el número cambia con el viewport del ejemplo.
<output id="viewport-size" aria-live="polite"></output>
<script>
const output = document.querySelector('#viewport-size');
const render = () => {
output.textContent = `${window.innerWidth} × ${window.innerHeight} CSS px`;
};
window.addEventListener('resize', render, { passive: true });
render();
</script>Las dimensiones se actualizan cuando cambia el viewport. No infiere si el Duo está plegado: si solo necesitas layout, esa información no te hace falta.
Container Queries: el componente manda, no el teléfono
Con el iPhone Duo abierto puedes tener una sola página amplia o Safari compartiendo espacio con otra app. Una media query que inspecciona el viewport global no sabe cuánto espacio le queda realmente a tu tarjeta. Una container query sí.
<section class="product-region">
<article class="product-card">
<div class="product-art" aria-hidden="true">⌘</div>
<div class="product-copy">
<h3>Plan Developer</h3>
<p>Deploys, métricas y café de dudosa procedencia.</p>
<a href="#comprar">Ver detalles</a>
</div>
</article>
</section>.product-region {
container: product / inline-size;
}
.product-card {
display: grid;
gap: 1rem;
padding: 1rem;
border: 1px solid #d4d4d8;
border-radius: 1rem;
}
.product-art {
min-height: 7rem;
display: grid;
place-items: center;
background: #e4e4e7;
}
@container product (min-width: 480px) {
.product-card {
grid-template-columns: minmax(120px, 35%) 1fr;
align-items: center;
}
}La tarjeta apila sus elementos con un contenedor angosto y crea dos columnas al superar 480 px de ancho del contenedor. El @container afecta a los descendientes, no al propio .product-region.
CSS Grid sin una lista de breakpoints escrita con lágrimas
Para catálogos y dashboards, antes de escribir cuatro media queries, prueba repeat(auto-fit, minmax()). min(100%, 220px) evita el overflow en contenedores estrechos. Sí, el detalle importa. Alguien olvidará ese min() y luego culpará a Safari.
<div class="service-grid">
<article>Diseño web</article>
<article>Software a medida</article>
<article>Soporte técnico</article>
<article>Automatización</article>
</div>.service-grid {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(min(100%, 220px), 1fr));
gap: clamp(12px, 2vw, 24px);
}
.service-grid > article {
min-width: 0;
padding: 1.25rem;
border: 1px solid #d4d4d8;
border-radius: 12px;
}Una, dos o más columnas según el ancho disponible, sin conocer qué iPhone tiene el usuario.
Safe areas: un botón debajo de la interfaz del sistema no cuenta
Las esquinas redondeadas, recortes y controles del sistema pueden reducir el espacio seguro. Para experiencias edge-to-edge, declara el viewport correspondiente y utiliza env(safe-area-inset-*). Los valores pueden ser cero; eso es perfectamente normal.
El código que copias usa las variables reales. En un navegador de escritorio esas variables valen cero, así que el marco pinta una franja naranja artificial para que se vea el margen. No es un valor de iOS.
<meta name="viewport"
content="width=device-width, initial-scale=1, viewport-fit=cover">
<main class="app-content">Contenido</main>
<nav class="bottom-nav" aria-label="Navegación principal">
<a href="#inicio">Inicio</a>
<a href="#buscar">Buscar</a>
<a href="#perfil">Perfil</a>
</nav>:root { --nav-height: 56px; }
.app-content {
padding-inline:
max(16px, env(safe-area-inset-left, 0px))
max(16px, env(safe-area-inset-right, 0px));
padding-bottom: calc(var(--nav-height) + env(safe-area-inset-bottom, 0px));
}
.bottom-nav {
position: fixed;
inset-inline: 0;
bottom: 0;
min-height: var(--nav-height);
display: flex;
justify-content: space-around;
align-items: center;
padding-bottom: env(safe-area-inset-bottom, 0px);
background: #fff;
border-top: 1px solid #ddd;
}La navegación inferior deja margen para el área segura sin tapar el contenido. El recuadro naranja representa ese inset: un navegador de escritorio no puede fingir las variables de hardware del iPhone.
dvh y svh, el hero que mide más que la pantalla
100vh puede no representar el área visible mientras aparecen y desaparecen las barras del navegador. CSS moderno incorpora unidades de viewport dinámicas y pequeñas. No son intercambiables.
Regla práctica: svh para un hero estable; dvh para overlays que deben seguir la altura disponible. No uses dvh en todo por deporte: cuando se mueven las barras del navegador, el layout puede reajustarse. Dentro de este iframe no hay barras de Safari; el ejemplo solo informa si el navegador entiende 100dvh.
<section class="hero-full">
<h2>Todo el espacio que realmente tienes</h2>
<p>Sin scroll fantasma por una barra del navegador.</p>
</section>.hero-full {
min-height: 100vh; /* fallback */
min-height: 100svh; /* tamaño pequeño estable */
display: grid;
place-content: center;
padding: 2rem;
}
.modal-full {
height: 100dvh; /* sigue el viewport dinámico */
overflow: auto;
}svh para un hero estable; dvh para overlays que deben seguir la altura disponible. Las barras de Safari solo se prueban en un dispositivo real.
Aspect ratio y tipografía fluida
El iPhone Duo tiene un área interior amplia, pero el área visible depende del viewport que Safari entregue. Puedes combinar proporción y ancho disponible sin asumir una postura física particular.
cqi referencia un contenedor de consulta adecuado; sin uno disponible, puede utilizar el small viewport como referencia. Asegúrate de probarlo con el componente realmente montado donde va. Una demo bonita en CodePen no garantiza nada dentro de tu sidebar.
<section class="hero-layout">
<div>
<h2>Interfaces que no estorban</h2>
<p>Diseño adaptable, sin inventar dispositivos.</p>
</div>
<div class="hero-visual" aria-hidden="true">{ }</div>
</section>.hero-layout {
container-type: inline-size;
display: grid;
gap: 1.5rem;
padding: clamp(16px, 3vw, 36px);
}
.hero-layout h2 {
font-size: clamp(2rem, 5cqi, 4rem);
line-height: 1.05;
text-wrap: balance;
}
.hero-layout p { max-width: 60ch; }
.hero-visual { min-height: 130px; }
@media (min-width: 700px) and (max-aspect-ratio: 3/2) {
.hero-layout {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
align-items: center;
}
}Tipografía fluida y dos columnas solo cuando el marco es ancho y su proporción no es más panorámica que 3/2. Estira la anchura: cerca de 700 px el texto y el recuadro quedan lado a lado; si el marco se vuelve muy apaisado, vuelven a apilarse.
¿Y el pliegue? No te cases con una API experimental
La familia de especificaciones para pantallas segmentadas incluye horizontal-viewport-segments y variables env(viewport-segment-*). No asumas que Safari en iPhone Duo expone dos segmentos ni que la bisagra produce un hueco CSS. El soporte debe verificarse en el navegador y dispositivo específicos. Además, una pantalla continua plegable y dos segmentos lógicos separados no son exactamente el mismo problema.
<div class="dual-pane">
<div>Panel A</div>
<div>Panel B</div>
</div>/* Mejora progresiva: jamás la única versión del layout. */
.dual-pane { display: grid; grid-template-columns: 1fr; gap: 1rem; }
@media (horizontal-viewport-segments: 2) {
.dual-pane {
grid-template-columns:
env(viewport-segment-width 0 0)
env(viewport-segment-width 1 0);
}
}El fallback de una columna funciona en todas partes. La variante segmentada solo se activa donde el navegador expone esa característica. La casilla simula dos paneles: no es soporte nativo de Safari.
Cómo probar CSS responsive en el iPhone Duo sin autoengañarte
No basta con seleccionar «iPhone» en DevTools, comprobar que el header se ve bonito y cerrar el ticket. Prueba al menos estos escenarios:
- Ventana estrecha: contenido, navegación, formularios y botones táctiles.
- Ventana amplia: Grid, tarjetas, dashboards y lectura de contenido largo.
- Multitarea: reduce el ancho mientras un panel o modal está abierto.
- Teclado visible: formularios, scroll, elementos fixed y focos.
- Safari con barras visibles y ocultas: svh frente a dvh.
- Reader Mode: títulos, orden de lectura, enlaces y código aún comprensibles sin componentes interactivos.
- Zoom de texto y orientación: nada debe depender de un ancho exacto.
- Rendimiento: no conviertas cada resize en una excusa para re-renderizar toda tu aplicación.
Cada ejemplo de arriba trae un control de anchura. Es una simulación del viewport, no un emulador del iPhone Duo ni una prueba de APIs nativas. Las pruebas finales tienen que hacerse en Safari real.
Conclusión: responsive design sin culto al breakpoint
Adaptar tu sitio web para el iPhone Duo no exige detectar el iPhone Duo. Exige respetar el viewport, construir componentes autocontenidos, usar CSS Grid y container queries donde tengan sentido, reservar safe areas y dejar de confundir pulgadas con píxeles CSS.
Si tu layout sobrevive a pasar de 360 a 900 píxeles, abrir un sidebar, mostrar el teclado y volver a estrecharse sin explotar, ya hiciste algo más valioso que «compatibilidad con el teléfono de moda»: hiciste una interfaz mantenible. Y sí, de paso funciona en el próximo dispositivo raro que alguien anuncie en un keynote.

