Matemáticas de Diseño Responsivo
IDENTIFICAR: Cuándo activarse
Activar cuando:
- Se define el diseño de página, cuadrícula o disposición de tarjetas
- Se establece tipografía o espaciado entre viewports
- El usuario reporta problemas de diseño en móvil
- Se crea cualquier componente nuevo que necesita soporte multi-viewport
PRINCIPIO FUNDAMENTAL
Dile al navegador QUÉ quieres, no CÓMO hacerlo. Usa dimensionamiento basado en restricciones CSS (clamp(), min(), max(), minmax(), auto-fit). Evita breakpoints fijos para cuadrículas y tipografía.
SELECCIÓN DE TÉCNICA
SUPOSICIÓN: Tailwind CSS v4 (configuración nativa CSS mediante @theme). Si se detecta v3 (tailwind.config.ts), ajustar la sintaxis de clases utilitarias en consecuencia.
SI se construye una cuadrícula de elementos (tarjetas, publicaciones, productos) →
USAR auto-fit + minmax(), Paso 1
SI se establecen tamaños de fuente →
USAR clamp(), Paso 2
SI se define espaciado →
USAR sistema de cuadrícula de 8px, Paso 3
SI se establecen anchos de contenedor →
USAR w-full + max-w-*, Paso 4
SI se hace un componente responsivo a su padre →
USAR container queries, Paso 5
SI se dimensionan objetivos táctiles →
USAR mínimo 44×44px, Paso 6
EJECUTAR: Instrucciones
Paso 1: Cuadrículas Fluidas con auto-fit + minmax()
NUNCA usar recuentos de columnas dependientes de breakpoints:
.grid {
display: grid;
grid-template-columns: 1fr;
}
@media (min-width: 768px) { .grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1024px) { .grid { grid-template-columns: 1fr 1fr 1fr; } }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
gap: 1rem;
}
Tailwind v4:
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(100%,300px),1fr))] gap-4">
Paso 2: Tipografía Fluida con clamp()
SIEMPRE usar clamp(MIN, PREFERIDO, MAX), nunca unidades vw solas:
h1 { font-size: 4vw; }
h1 { font-size: clamp(1.5rem, 4vw, 3rem); }
REGLA: Nunca usar vw sola para tamaño de fuente. SIEMPRE envolver en clamp() con límites mínimo y máximo.
Paso 3: Cuadrícula de Espaciado de 8px
Cada margen, relleno y espacio DEBE ser múltiplo de 8:
| Multiplicador | Valor | Uso |
|---|
| 0.5x | 4px | Ajustado: iconos, insignias |
| 1x | 8px | Espacio por defecto, elementos relacionados |
| 2x | 16px | Relleno de tarjetas, espacios de sección |
| 3x | 24px | Relleno de sección |
| 4x | 32px | Espaciado grande |
| 6x | 48px | Relleno a nivel de página |
| 8x | 64px | Relleno de sección hero |
<section class="py-20 px-4">
<div class="gap-4">
<div class="p-4">
<div class="p-[13px] ml-[7px]">
Paso 4: Restricciones de Ancho: SIEMPRE w-full + max-w-*
| Elemento | Ancho Máximo | Clase Tailwind |
|---|
| Texto del cuerpo / prosa | ~65 caracteres | max-w-prose |
| Contenido de página | 1280px | max-w-7xl |
| Formularios / contenido estrecho | 640px | max-w-2xl |
| Cuadrículas de tarjetas | El contenedor se llena, las tarjetas tienen ancho mínimo | cuadrícula auto-fit |
<div class="w-full max-w-7xl mx-auto px-4">
<div class="w-[500px]">
Paso 5: Container Queries (Responsividad a Nivel de Componente)
Usar container queries en lugar de media queries de viewport para el interior de componentes:
.card-grid {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
}
Esto hace que los componentes sean verdaderamente reutilizables, responden al espacio disponible real.
Paso 6: Dimensionamiento de Objetivos Táctiles
button { min-height: 44px; min-width: 44px; }
<button class="min-h-11 min-w-11 px-4">Guardar</button>
<button class="h-6 px-2 text-xs">Guardar</button>
VALIDAR: Puertas de Calidad
ANTIPATRONES: SIEMPRE Evitar
| Antipatrón | Por qué está mal | Solución |
|---|
w-[500px] | Se desborda en móvil, no escala | max-w-[500px] w-full |
grid-cols-1 md:grid-cols-2 lg:grid-cols-3 | Necesita breakpoint por viewport | auto-fit minmax(min(100%,300px),1fr) |
font-size: 4vw | Ignora el zoom, diminuto o enorme | clamp(1.5rem, 4vw, 3rem) |
margin-left: 23px | Número mágico, no se alineará | Múltiplo de 8 (24px) |
5+ @media queries por componente | Mantenimiento frágil | clamp()/minmax() fluido en su lugar |