El Problema del 100vh en Móviles: Guía Completa 2026 para Solucionarlo
¿Alguna vez has creado una sección con `height: 100vh` que funciona perfecto en desktop, pero en móvil tiene un scroll extra no deseado? No estás solo. Este es uno de los problemas CSS más frustrantes para desarrolladores web, y lleva causando dolores de cabeza desde 2015.
En esta guía completa voy a explicarte por qué ocurre, todas las soluciones disponibles, y cuál es el futuro de las unidades viewport en CSS.
¿Qué es exactamente el problema del 100vh?
El problema visual
Imagina esta situación:
«`css .hero { height: 100vh; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } «`
En desktop se ve perfecto: la sección ocupa exactamente el 100% de la altura visible.
Pero en móvil (especialmente iOS Safari), pasa esto:
1. La sección es MÁS ALTA que la pantalla visible 2. Aparece un scroll vertical no deseado 3. El contenido se «corta» o necesita scroll para verse completo
¿Por qué pasa esto?
El problema no es un «bug» sino una decisión de diseño de los navegadores.
En móviles, la barra de direcciones (URL bar) es dinámica:
– Desplazada hacia arriba: La barra ocupa espacio – Desplazada hacia abajo: La barra se oculta, ganando espacio
¿Qué significa 100vh? – `100vh` = 100% de la altura del viewport COMPLETO – Esto incluye el espacio donde está la barra de direcciones – Pero el usuario solo ve el viewport VISIBLE
El resultado: `100vh` > altura visible → scroll no deseado.
Historia del problema
Este comportamiento fue documentado por primera vez por Nicolas Hoizey en 2015. Se abrió un issue en el WebKit Bug Tracker, pero fue marcado como «intended behavior» (comportamiento intencional).
Navegadores afectados: – ✅ iOS Safari (todas las versiones) – ⚠️ Chrome para Android (parcialmente) – ⚠️ Firefox para Android (parcialmente)
Solución 1: CSS con `-webkit-fill-available` (Recomendada)
Esta es la solución más simple y efectiva para la mayoría de casos.
Código base
«`css html { height: -webkit-fill-available; }
body { min-height: 100vh; min-height: -webkit-fill-available; } «`
Versión con fallback (más robusta)
«`css html { height: -webkit-fill-available; }
body { margin: 0; padding: 0; min-height: 100vh; }
/* Solo Safari móvil */ @supports (-webkit-touch-callout: none) { body { min-height: -webkit-fill-available; } } «`
¿Por qué funciona?
`-webkit-fill-available` es una propiedad propietaria que: – Ocupa el espacio DISPONIBLE en el contenedor – Ignora la barra de direcciones en Safari – Usa solo el viewport visible real
Limitaciones
– ⚠️ No es estándar (aunque todos los navegadores la soportan) – ⚠️ Puede tener comportamientos inesperados en contenedores anidados – ⚠️ Requiere `@supports` para evitar afectar otros navegadores
Solución 2: JavaScript con CSS Variables (Más flexible)
Si necesitas control total o animaciones, esta solución es ideal.
Paso 1: CSS con variable
«`css :root { –vh: 1vh; /* Fallback */ }
body { margin: 0; padding: 0; }
.hero { height: 100vh; height: calc(var(–vh, 1vh) * 100); } «`
Paso 2: JavaScript que calcula la altura real
«`javascript // Función que calcula y actualiza la variable CSS const setVH = () => { const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty(‘–vh’, `${vh}px`); };
// Ejecutar al cargar setVH();
// Actualizar en resize y orientación window.addEventListener(‘resize’, setVH); window.addEventListener(‘orientationchange’, setVH); «`
Versión mejorada con debounce
«`javascript const setVH = () => { const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty(‘–vh’, `${vh}px`); };
// Debounce para evitar demasiados cálculos let resizeTimer; window.addEventListener(‘resize’, () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(setVH, 100); });
// Orientación cambio (disparado inmediatamente) window.addEventListener(‘orientationchange’, () => { setVH(); }); «`
Ventajas de esta solución
✅ Control total: Sabes exactamente qué altura se usa ✅ Animaciones: Puedes animar transiciones de altura ✅ Cross-browser: Funciona en todos los navegadores ✅ Responsive: Se adapta en tiempo real
Desventajas
❌ Requiere JavaScript ❌ Pequeño delay en cálculo (milisegundos) ❌ Necesita debounce para performance
Solución 3: PostCSS con autoprefixer (Automatizada)
Si usas un build process, puedes automatizar la solución.
Instalar dependencias
«`bash npm install postcss postcss-viewport-height-correction –save-dev «`
Configuración postcss.config.js
«`javascript module.exports = { plugins: [ require(‘postcss-viewport-height-correction’)({ userAgent: navigator.userAgent, // Opciones adicionales // onlyMobile: true, // force: true }), ], }; «`
Resultado
El plugin automáticamente convierte:
«`css .hero { height: 100vh; } «`
En:
«`css .hero { height: 100vh; height: -webkit-fill-available; } «`
Casos de Uso Comunes
Hero section con imagen de fondo
«`css .hero { height: 100vh; height: -webkit-fill-available; background-image: url(‘hero-bg.jpg’); background-size: cover; background-position: center; display: flex; align-items: center; justify-content: center; } «`
Modal pantalla completa
«`css .modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; height: 100vh; height: -webkit-fill-available; overflow: auto; } «`
Slide tipo carrusel
«`css .slide { height: 100vh; height: calc(var(–vh, 1vh) * 100); scroll-snap-align: start; }
.carousel { height: 100vh; height: calc(var(–vh, 1vh) * 100); scroll-snap-type: y mandatory; overflow-y: scroll; } «`
El Futuro: Nuevas Unidades Viewport
CSS está evolucionando para resolver este problema de forma nativa.
Nuevas unidades propuestas
| Unidad | Significado | |——–|————-| | `dvh` | Dynamic Viewport Height (viewport con barras dinámicas) | | `svh` | Small Viewport Height (viewport con barras visibles) | | `lvh` | Large Viewport Height (viewport con barras ocultas) |
Soporte actual (2026)
«`css .hero { /* Futuro: cuando el soporte sea universal */ height: 100dvh; /* Dynamic viewport height */
/* Fallback actual */ height: -webkit-fill-available; } «`
Estado de soporte: – ✅ Chrome 108+ – ✅ Edge 108+ – ✅ Firefox 101+ – ⚠️ Safari (en desarrollo)
Estrategia de implementación
«`css .hero { /* Fallback para navegadores antiguos */ height: 100vh;
/* Solución actual Safari móvil */ height: -webkit-fill-available;
/* Futuro: unidades dinámicas */ height: 100dvh; } «`
Troubleshooting: Problemas Comunes
Problema 1: Scroll horizontal no deseado
Causa: Content overflow + viewport height
Solución: «`css html, body { overflow-x: hidden; width: 100%; }
.hero { max-width: 100%; overflow: hidden; } «`
Causa: `position: fixed` sin considerar el viewport
Solución: «`css .navbar { position: fixed; top: 0; left: 0; right: 0; height: 60px; z-index: 1000; }
.hero { height: calc(100vh – 60px); height: calc(-webkit-fill-available – 60px); padding-top: 60px; } «`
Problema 3: No funciona en landscape
Causa: Orientación cambia el viewport
Solución (JavaScript): «`javascript const setVH = () => { const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty(‘–vh’, `${vh}px`); };
window.addEventListener(‘orientationchange’, () => { setTimeout(setVH, 100); // Delay para que el navegador recalcule }); «`
Checklist para Desarrollo
Antes de deployar, verifica:
Probado en iOS Safari (iPhone) Probado en Chrome Android Probado en landscape y portrait Probado con barra de direcciones visible/oculta Sin scroll horizontal no deseado Las animaciones funcionan correctamente El contenido es accesible en todas las pantallas
Herramientas Útiles
Test en dispositivo real
Xcode + iOS Simulator: – Simula iPhone/iPad – Testing preciso de Safari iOS – Debug con Safari DevTools
BrowserStack: – Testing en dispositivos reales – Múltiples navegadores – Capturas de pantalla
CodePen para pruebas rápidas
«`html
«`
Conclusión
El problema del `100vh` en móviles es uno de esos casos donde la teoría CSS (`100vh` = altura completa) choca con la práctica (barras de direcciones dinámicas).
Resumen de soluciones:
| Solución | Cuando usarla | |———-|—————| | `-webkit-fill-available` | Casos simples, sin JS | | CSS Variables + JS | Control total, animaciones | | PostCSS plugin | Build process automatizado | | `100dvh` (futuro) | Cuando el soporte sea universal |
Mi recomendación: Para 2026, usa `-webkit-fill-available` con fallback, y empieza a probar `100dvh` en proyectos nuevos.
El futuro de CSS está prometiendo soluciones nativas, pero mientras tanto, estas técnicas te ahorrarán horas de debugging.
¿Sigues teniendo problemas con el 100vh en tu proyecto? Déjame un comentario con tu caso específico y te ayudo a diagnosticarlo.
¿Necesitas ayuda con CSS responsive? Echa un vistazo a mis otros tutoriales de CSS maquetación.



