Ir al contenido

CSS position fixed en mobile: el bug que nadie ve

8 de agosto de 2026 por
CSS position fixed en mobile: el bug que nadie ve
ContentFlow

Controles táctiles en mobile que nadie podía tocar: el bug de CSS que pocos conocen

Imagina que diseñas una interfaz móvil cuidadosamente, defines botones de 64px como indica el estándar, y aun así los usuarios no pueden presionarlos. No porque el diseño sea malo, sino porque el contenedor que rodea esos botones aplica una escala del 58%. El botón que tú ves en el código mide 64px. El botón que el dedo del usuario intenta tocar mide 37px. Apple recomienda un mínimo de 44px. El problema no era visual — era arquitectónico.

Este es exactamente el escenario que documentó el equipo de Weird Codes en el devlog de su juego Moksha, publicado el 8 de agosto de 2026 en Dev.to. Lo que comenzó como un reporte de UX terminó siendo una lección profunda sobre cómo funciona CSS cuando se combina transform con position: fixed, y por qué window.innerHeight puede mentirte en mobile.

En este artículo extraemos los tres insights técnicos más importantes de ese fix, y explicamos por qué son relevantes no solo para juegos web, sino para cualquier aplicación móvil — incluyendo apps empresariales, dashboards y sistemas ERP accedidos desde el celular.

El problema: CSS position: fixed no siempre fija al viewport

Esta es la regla que más sorprende a los desarrolladores cuando la encuentran por primera vez: position: fixed se ancla al viewport solo si ningún ancestro del elemento tiene un CSS transform aplicado. Si existe un ancestro con transform, el elemento "fijo" se ancla a ese contenedor transformado, no a la pantalla.

No es un bug del navegador. Está definido en la especificación CSS. Pero es una trampa silenciosa porque todo funciona perfectamente en desktop, donde el contenedor no necesita escalar. El problema aparece únicamente en viewports pequeños, justo donde el usuario más necesita que los controles funcionen.

En el caso de Moksha, el contenedor principal recibía un transform: scale(0.58) en pantallas angostas para que el juego cupiera en la pantalla. Todos los elementos adentro — canvas, HUD, botones táctiles — se encogían juntos. La solución fue mover el div de controles fuera del contenedor escalado, directamente al body. Simple en concepto, pero requiere entender primero por qué el comportamiento era incorrecto.

El segundo problema: window.innerHeight te da el tamaño equivocado

Una vez resuelto el problema de posición, apareció otro: el canvas seguía desbordándose debajo de la barra de URL del navegador móvil. El motivo es que window.innerHeight incluye el espacio detrás de la barra de URL, incluso cuando esa barra está visible y ocupa espacio real en pantalla.

La alternativa correcta es visualViewport.height, que retorna la altura realmente disponible para el contenido — excluyendo la barra de URL, el teclado virtual y cualquier otro elemento del navegador que reduzca el espacio visible. Para CSS, el equivalente es la unidad 100dvh (dynamic viewport height), que se ajusta dinámicamente cuando la barra de URL aparece o desaparece al hacer scroll.

Este detalle importa mucho en aplicaciones empresariales móviles: si un formulario o dashboard calcula su altura con innerHeight, puede quedar parcialmente oculto bajo la barra del navegador en dispositivos Android o iOS, generando una experiencia de usuario frustrante que es difícil de reproducir en desktop.

El tercer insight: un Set de bloqueadores es mejor que un boolean

El tercer cambio arquitectónico del fix es el más elegante y el más aplicable a proyectos grandes. El código original usaba un solo boolean para controlar si los botones táctiles eran visibles o no. Eso funciona bien cuando solo hay una condición que puede ocultarlos.

Pero en la práctica, cuatro sistemas distintos podían necesitar ocultar los controles independientemente: la pantalla de inicio, el tutorial, un overlay de información, y futuras transiciones. Con un boolean, cada sistema necesita saber qué hicieron los otros antes de tomar su decisión. Es una fuente garantizada de bugs de estado.

La solución fue reemplazar el boolean por un Set de "bloqueadores". Cada sistema agrega su propio identificador al Set cuando necesita ocultar los controles, y lo elimina cuando termina. Los controles solo se muestran cuando el Set está completamente vacío. Ningún sistema necesita coordinar con los demás — cada uno gestiona únicamente su propio estado.

Este patrón tiene nombre en arquitectura de software: se llama reference counting o gestión por semáforos. Es el mismo principio que usan los sistemas operativos para gestionar recursos compartidos. Aplicarlo a la visibilidad de elementos de UI es una decisión de diseño madura que escala bien.

¿Cómo aplica esto en empresas de Perú y LATAM?

En 2026, una porción significativa del tráfico a aplicaciones empresariales — ERPs, dashboards, portales de clientes — llega desde dispositivos móviles. En Perú y América Latina, donde el celular es frecuentemente el primer (y a veces único) dispositivo de acceso a internet, esto es todavía más marcado.

Los tres problemas documentados en este fix no son exclusivos de juegos web. Son errores que aparecen en cualquier aplicación que combine escalado dinámico con posicionamiento CSS, o que calcule alturas de layout en JavaScript sin considerar la barra de URL del navegador móvil. Un formulario de pedidos, un módulo de aprobaciones, o un panel de métricas pueden sufrir exactamente los mismos síntomas: botones que no responden, contenido que se desborda, o estados de UI que se contradicen entre sí.

Las empresas que invierten en desarrollo móvil propio — ya sea apps nativas con Flutter o aplicaciones web progresivas — necesitan equipos que conozcan estas reglas del CSS spec y las API del navegador moderno. No son detalles menores: son la diferencia entre una app que los usuarios adoptan y una que abandonan después del primer intento.

¿Cómo aplica esto en tu empresa?

Si tu empresa tiene una aplicación web o un ERP accedido desde mobile, considera estos puntos de revisión concretos:

  • Audita tus contenedores con transform: Si algún elemento de layout usa transform: scale() para adaptarse a pantallas pequeñas, verifica que ningún control interactivo importante viva dentro de ese contenedor.
  • Reemplaza innerHeight por visualViewport.height en cualquier lógica de JavaScript que calcule alturas para escalar o posicionar elementos en mobile.
  • Usa 100dvh en lugar de 100vh en CSS para layouts que deben ocupar toda la pantalla en mobile — especialmente si el contenido puede quedar oculto cuando aparece el teclado virtual.
  • Aplica el patrón de bloqueadores cuando más de un sistema pueda controlar la visibilidad de un mismo elemento de UI. Evita el boolean compartido.
  • Verifica tamaños de áreas táctiles: El mínimo recomendado por Apple HIG y Google Material Design es 44px. Un botón visualmente grande puede ser tácticamente pequeño si está dentro de un contenedor escalado.

Estos no son cambios costosos. Son ajustes de arquitectura que pueden implementarse en horas y que tienen un impacto directo en la tasa de adopción de cualquier herramienta móvil.

Conclusión

El bug de los controles táctiles de Moksha es un ejemplo perfecto de cómo un problema de UX puede tener una causa técnica no obvia. Los botones eran visualmente correctos. El diseño era apropiado. El error estaba en asumir que el DOM se comporta igual dentro y fuera de un contenedor con transform — y esa asunción es falsa según el CSS spec.

Para equipos de desarrollo en Perú y LATAM que construyen aplicaciones móviles o web, estos tres patrones — posicionamiento fuera de contenedores transformados, uso de visualViewport, y gestión de estado con Sets — son conocimiento fundamental que ahorra horas de debugging en producción.

En Consultoría-Ti trabajamos con equipos de desarrollo que enfrentan exactamente estos desafíos: aplicaciones que funcionan bien en desktop pero fallan en mobile, interfaces que se rompen en dispositivos específicos, o arquitecturas de UI que se vuelven imposibles de mantener a medida que crecen. Si tu equipo está construyendo o mejorando una aplicación móvil y quieres una revisión técnica, conversemos aquí.

Fuentes y Referencias

Dev.to — Weird Codes: Mobile Touch Controls: Scale-Independent Fix



✨ Contenido generado con ContentFlow — Consultoría-Ti

Compartir
Etiquetas
Reverse Engineering BLE en Android: Lecciones GATT