Un plegable con dos relaciones de aspecto muy distintas destapó roturas de layout que ningún iPhone normal había mostrado: así las arreglamos.
Eduardo··5 min de lectura
Cuando probamos el sitio en el simulador del iPhone Duo, el nuevo plegable de Apple, con su pantalla cerrada (angosta, tipo teléfono) y abierta (ancha, casi tableta), empezaron a aparecer roturas que en un iPhone normal no se veían: una mascota animada que se salía de su sitio, una sección con scroll-jacking que recortaba contenido, un header saturado. Nada de esto era un bug de "responsive" clásico por ancho de pantalla: era la combinación de un dispositivo con dos estados de aspecto muy distintos y con una altura de viewport que Safari no siempre reporta de forma estable.
Esto es lo que hicimos para arreglarlo.
1. La mascota del Hero: de "flotando" a anclada de verdad
El Hero tiene una mascota (HeroMascot) que se posiciona sola en una esquina inferior de la sección, con una animación al pasar el mouse. El objetivo de diseño: que se vea asomando desde el borde inferior, ligeramente recortada por el fold, nunca tapando el botón de CTA.
Fuimos encontrando, en capas, varios problemas relacionados:
Tapaba el botón "Ver servicios" en mobile. En pantallas angostas los botones del CTA se apilan en columna y crecen más de lo que el cálculo original preveía. Solución: medir en runtime la posición real del bloque de contenido (getBoundingClientRect) y, si invade la zona reservada para la mascota, bajarla debajo del contenido en vez de superponerla.
Saltaba al hacer scroll en mobile. La causa real no era el listener de resize, sino que el Hero usaba min-h-[90dvh]: en Safari, dvh cambia de forma continua mientras la barra de direcciones se colapsa durante el scroll, así que toda la sección (y la mascota dentro de ella) se redimensionaba en vivo justo en ese instante. Cambiamos a min-h-[90svh] (small viewport height): el valor mínimo y estable, que no varía con la barra de Safari.
Quedaba "flotando" separada del borde real. Inspeccionamos el sprite (fox-directions.webp, un grid 3×3 de 1080×1080px) y confirmamos que el frame de reposo deja ~24% de aire transparente en la parte inferior de su propio cuadro. La caja del componente estaba pegada al borde, pero el dibujo dentro de ella no. Ahora compensamos ese padding calculándolo como proporción del tamaño renderizado, para que sea el personaje, no la caja invisible, el que quede pegado al pie de la sección.
Se recortaba a la mitad en el Duo. Agregamos un tope de seguridad: sin importar qué tan corta resulte la sección medida en runtime, nunca se recorta más del 40% de la mascota.
2. El verdadero problema de fondo: centrar contenido en una pantalla ancha-y-baja
El Duo abierto es el caso que más dio batalla. Con su nav de escritorio completo pero una altura de viewport bastante más corta que la de un iPhone normal, centrar el contenido del Hero () dejaba el mismo margen arriba que abajo, y ese margen no alcanzaba para la mascota.
#responsive-design
#css
#manada-agency
Más de Manada Blog
justify-center
No hay forma de detectar "este es un iPhone Duo" desde CSS o JS: solo se pueden medir dimensiones, no modelos de dispositivo. Así que en vez de adivinar un breakpoint fijo por tamaño de pantalla, implementamos una detección dinámica: medimos si, centrado, el margen inferior alcanzaría un mínimo razonable para la mascota; si no alcanza, el layout pasa a un modo compacto (contenido pegado arriba, con menos padding), y todo el espacio libre se acumula abajo en lugar de repartirse por igual. Este enfoque se ajusta solo al dispositivo real, sea cual sea, sin arriesgar a otros tamaños de pantalla que ya funcionaban bien.
3. La sección "Nuestros pilares": un scroll-jack que no cabía
Esta sección usa GSAP ScrollTrigger para "pinear" un panel de pantalla completa mientras el usuario hace scroll, mostrando 9 pasos (uno por cada servicio). En mobile y en el rango intermedio (tablet/Duo), el layout se rompía por varios motivos a la vez:
Los tamaños "grandes" de texto, imagen y tags se activaban en el breakpoint sm (640px), pero el layout en fila (texto a la izquierda, imagen a la derecha) recién entraba en lg (1024px). Entre esos dos anchos, justo donde cae el Duo, el contenido seguía apilado en columna pero con medidas de escritorio, desbordando el panel.
El panel usaba h-dvh, con el mismo problema de inestabilidad que el Hero.
El padding superior del bloque de título era un valor fijo que no siempre alcanzaba a despejar la altura real del header sticky, dejando el título tapado.
Solución: alineamos el breakpoint del layout en fila a md (768px, el mismo que ya usa el header para su nav de escritorio), migramos a h-svh, y medimos en runtime la altura real del header para calcular el padding necesario en vez de adivinarlo.
4. Header: menos controles fijos en la barra mobile
Los botones de idioma (ES/EN) y modo claro/oscuro vivían siempre en la barra superior, incluso en mobile, compitiendo por espacio con el logo y el menú hamburguesa. Los movimos dentro del menú desplegable en mobile, dejando la barra superior solo con el logo y el ícono de menú; en desktop siguen donde estaban.
Aprendizaje general
Nada de esto se trataba de "otro breakpoint". El patrón que se repitió una y otra vez fue: un dispositivo con una relación ancho/alto atípica rompe supuestos que funcionan bien en el rango normal de iPhones: específicamente, cualquier cálculo de layout que asuma "la altura del viewport siempre da suficiente aire" o que use dvh sin considerar que cambia durante el scroll. La solución que mejor escaló no fue agregar más breakpoints fijos, sino medir en runtime las condiciones reales (altura de sección, posición del contenido, padding del sprite) y decidir el layout en base a eso; así el resultado se ajusta solo a cualquier proporción de pantalla, presente o futura, en vez de a un modelo de dispositivo específico.