Sitio siempre responsive
Una página solo está terminada si pasa python3 scripts/check_responsive.py (sin desplazamiento horizontal, nada desborda la pantalla, objetivos táctiles lo suficientemente grandes) en los 6 tamaños de referencia. Este control también se ejecuta en la CI: un PR que rompa el responsive no puede desplegarse.
Anchuras de referencia (probar todas)
| Anchura | Caso |
|---|---|
| 320 px | Android pequeño, iPhone SE con zoom |
| 375 px | iPhone estándar (objetivo n° 1: la audiencia viene de TikTok) |
| 414 px | Teléfono grande |
| 768 px | Tablet en vertical |
| 1024 px | Tablet en horizontal / portátil pequeño |
| 1440 px | Escritorio |
Tamaños probados por el script: 320×640, 375×667, 414×896, 768×1024, 1024×600 (pantalla baja), 1440×900.
Reglas CSS
- Mobile first: estilos base para 320 px, luego
@media (min-width: …)para ampliar. Puntos de ruptura:600px,900px,1200px(sin valores mágicos en otros lugares; un componente puede usar@container). - Sin ancho fijo superior a 280 px sin
min()/max-width: 100%:width: min(520px, 100%). - Tipografía fluida con
clamp(): títulosclamp(32px, 7vw, 72px), texto corriente 16 px mínimo (nunca por debajo en móvil, de lo contrario iOS hace zoom en los campos). - Alturas:
min-height: 100svh(no100vh, que queda oculto bajo la barra de direcciones móvil). Nuncaheightfija en un bloque de texto. - Márgenes laterales: canalón de 16 px en móvil (
space-4), másenv(safe-area-inset-left/right)para pantallas con notch (el viewport ya tieneviewport-fit=cover). - Texto largo:
overflow-wrap: anywhereen URLs, comandos y nombres de archivo; bloques de código conoverflow-x: autodentro del bloque, nunca en la página. - Rejillas:
grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr))para las tarjetas de tutoriales. - Elementos decorativos que sobresalen (bloques
byte/spark, canvas):position: fixedo padre conoverflow: clip, y tamaños enmin(vw, px). - Táctil: botones y enlaces aislados ≥ 44 × 44 px (24 px = mínimo bloqueante del script, WCAG 2.2), 8 px de separación mínima entre dos objetivos. No ofrecer información disponible solo al pasar el ratón (
:hover): prever:focus-visibley el estado táctil. - Vídeos e iframes (YouTube):
aspect-ratio: 16 / 9; width: 100%; height: auto;— vídeos TikTok en9 / 16conmax-width: 360px.
Imágenes
- Siempre
widthyheighten el HTML (evita el desplazamiento de maquetación, CLS) +max-width: 100%; height: autoen CSS. - Formatos: SVG para logotipos e iconos, WebP/AVIF para capturas, con
srcset+sizes(ej. 480w, 960w, 1600w). loading="lazy"ydecoding="async"bajo el pliegue; nunca en la imagen principal (LCP), que llevafetchpriority="high".- Miniaturas de tutoriales:
aspect-ratio: 16 / 9; object-fit: cover.
Viewport (ya implementado, no eliminar)
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
Prohibido: maximum-scale=1, user-scalable=no (bloquea el zoom, malo para la accesibilidad).
Animación y rendimiento móvil
prefers-reduced-motionrespetado (regla del proyecto).- Canvas: limitar
devicePixelRatioa 2 y reducir la densidad por debajo de 600 px (ya hecho enapp.js, mantener este principio). - Sin
backdrop-filterpesado apilado en móvil.
Verificar
# con el contenedor de check-site ejecutándose en :8088, o sin argumento (servidor local en site/)
python3 scripts/check_responsive.py # todas las páginas de site/
python3 scripts/check_responsive.py --shots /tmp/rs # + capturas PNG por ancho
El script falla si scrollWidth > anchura, si un elemento visible desborda el borde, si un objetivo táctil mide menos de 24 px (los enlaces dentro de una frase están exentos) o si el texto corriente mide menos de 16 px. Mira las capturas en 320 y 375 px antes de enviar.