Skip to content
SP StackPractices
intermediate Por StackPractices

Guía Completa de Diseño Mobile Responsive

Construye layouts responsive que funcionan en cualquier dispositivo. Cubre CSS Grid, Flexbox, container queries, fluid typography, mobile-first breakpoints y responsive images.

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Guía Completa de Diseño Mobile Responsive

Introducción

Diseño responsive significa construir layouts que se adaptan a cualquier tamaño de pantalla — desde teléfonos de 320px hasta monitores 4K. CSS moderno nos da Grid, Flexbox, container queries y fluid typography para construir interfaces responsive sin JavaScript. Esta guía cubre estrategia mobile-first, layouts con CSS Grid, patrones de Flexbox, container queries, fluid typography, responsive images y estrategias de testing.

Estrategia Mobile-First

Empezar con la pantalla más pequeña y progresivamente enhancar para pantallas más grandes. Esto te fuerza a priorizar contenido y mantiene el CSS lean.

/* Base styles — mobile first */
.card {
  padding: 1rem;
  font-size: 0.875rem;
}

/* Tablet y superior */
@media (min-width: 768px) {
  .card {
    padding: 1.5rem;
    font-size: 1rem;
  }
}

/* Desktop y superior */
@media (min-width: 1024px) {
  .card {
    padding: 2rem;
    font-size: 1.125rem;
  }
}

Breakpoints comunes

/* Breakpoints inspirados en Tailwind */
/* sm:  640px */
/* md:  768px */
/* lg:  1024px */
/* xl:  1280px */
/* 2xl: 1536px */

/* Usar min-width (mobile-first), no max-width (desktop-first) */

Layouts con CSS Grid

Grid básico

.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}

@media (min-width: 640px) {
  .grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

Auto-fit grid (sin media queries)

.auto-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

Holy grail layout

.layout {
  display: grid;
  grid-template-areas:
    "header header header"
    "nav    main   aside"
    "footer footer footer";
  grid-template-columns: 200px 1fr 250px;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

@media (max-width: 768px) {
  .layout {
    grid-template-areas:
      "header"
      "main"
      "nav"
      "aside"
      "footer";
    grid-template-columns: 1fr;
  }
}

.header { grid-area: header; }
.nav    { grid-area: nav; }
.main   { grid-area: main; }
.aside  { grid-area: aside; }
.footer { grid-area: footer; }

Grid con named lines

.grid {
  display: grid;
  grid-template-columns: [sidebar-start] 250px [sidebar-end content-start] 1fr [content-end];
  grid-template-rows: [header-start] 80px [header-end body-start] 1fr [body-end];
}

.header  { grid-column: sidebar-start / content-end; grid-row: header-start / header-end; }
.sidebar { grid-column: sidebar-start / sidebar-end; grid-row: body-start / body-end; }
.content { grid-column: content-start / content-end; grid-row: body-start / body-end; }

Patrones de Flexbox

Centrado

.center {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

main {
  flex: 1;
}
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0 1.5rem;
}

.nav-links {
  display: flex;
  gap: 1.5rem;
}

@media (max-width: 768px) {
  .nav-links {
    display: none;
  }
  .nav-menu-toggle {
    display: block;
  }
}

Card con contenido flexible

.card {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.card-content {
  flex: 1;
}

.card-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

Container Queries

Container queries permiten a los componentes responder al tamaño de su container, no del viewport. Esto habilita responsiveness a nivel componente real.

/* Definir un contexto de containment */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* Query el tamaño del container */
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 120px 1fr;
    gap: 1.5rem;
  }
}

@container card (max-width: 399px) {
  .card {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
  }
}

Container query units

.sidebar {
  /* cqw = container query width */
  font-size: clamp(0.875rem, 3cqw, 1.25rem);
  padding: 2cqi;
}

Fluid Typography

clamp()

/* min, preferred (viewport-relative), max */
h1 {
  font-size: clamp(1.5rem, 5vw, 3.5rem);
}

h2 {
  font-size: clamp(1.25rem, 4vw, 2.5rem);
}

p {
  font-size: clamp(1rem, 2.5vw, 1.125rem);
}

Fluid spacing

.section {
  padding: clamp(1rem, 5vw, 4rem);
  margin-bottom: clamp(1.5rem, 4vw, 3rem);
}

Responsive Images

srcset y sizes

<img
  src="image-800.jpg"
  srcset="image-400.jpg 400w, image-800.jpg 800w, image-1200.jpg 1200w, image-1600.jpg 1600w"
  sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 33vw"
  alt="Description"
  loading="lazy"
  decoding="async"
/>

picture element (art direction)

<picture>
  <source media="(max-width: 600px)" srcset="mobile.jpg" />
  <source media="(max-width: 1200px)" srcset="tablet.jpg" />
  <img src="desktop.jpg" alt="Description" />
</picture>

Aspect ratio

.image-container {
  aspect-ratio: 16 / 9;
  overflow: hidden;
}

.image-container img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Responsive Tables

.table-wrapper {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

table {
  width: 100%;
  border-collapse: collapse;
  min-width: 600px;
}

/* Card layout en mobile */
@media (max-width: 640px) {
  table, thead, tbody, th, td, tr {
    display: block;
  }

  thead {
    display: none;
  }

  tr {
    margin-bottom: 1rem;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    padding: 0.75rem;
  }

  td {
    display: flex;
    justify-content: space-between;
    padding: 0.5rem 0;
  }

  td::before {
    content: attr(data-label);
    font-weight: 600;
    margin-right: 1rem;
  }
}

Testing

Browser DevTools

  • Chrome DevTools: Device Mode (Ctrl+Shift+M)
  • Firefox: Responsive Design Mode (Ctrl+Shift+M)
  • Safari: Responsive Design Mode (Cmd+Ctrl+R)

CSS para testing

/* Debug grid lines */
* {
  outline: 1px solid red;
}

/* Mostrar container query boundaries */
@container card (min-width: 400px) {
  .card {
    outline: 2px dashed blue;
  }
}

Pautas

  • Empezar mobile-first — los base styles apuntan a la pantalla más pequeña, min-width media queries enhancan hacia arriba
  • Usar auto-fit grids — eliminar media queries para card layouts
  • Preferir clamp() para tipografía — scaling smooth sin saltos de breakpoint
  • Setear aspect-ratio en imágenes — prevenir layout shift (CLS)
  • Usar srcset para imágenes — servir la resolución apropiada por dispositivo
  • Añadir loading="lazy" — deferir imágenes off-screen
  • Usar container queries para componentes — desacoplar del viewport
  • Testear en dispositivos reales — los emuladores miss touch behavior y rendering bugs
  • Evitar fixed pixel widths — usar %, fr, vw, clamp() en su lugar
  • Manejar overflow explícitamenteoverflow-x: auto en tablas y code blocks
  • Usar gap en lugar de margins — spacing más limpio en contextos flex/grid
  • Setear min-width: 0 en flex children — prevenir overflow en nested flex

Errores Comunes

  • Usar max-width media queries (desktop-first) — los overrides son más difíciles y el CSS es más grande
  • Fixed pixel widths en containers — rompe en pantallas más pequeñas
  • No setear dimensiones de imagen — causa layout shift
  • Usar display: none para navegación mobile sin toggle — los usuarios no pueden navegar
  • Olvidar overflow-x: auto en tablas — horizontal scroll rompe la página
  • No testear en dispositivos reales — los emuladores miss performance y touch issues
  • Usar vh units sin fallback — el mobile browser chrome cambia el viewport height
  • Overusar media queries — container queries y auto-fit grids reducen la necesidad
  • No setear min-width: 0 en flex children — el contenido overflow los containers
  • Ignorar landscape orientation — los teléfonos en landscape tienen constraints diferentes

Preguntas Frecuentes

¿Debo usar container queries o media queries?

Usar media queries para layout a nivel página (header, sidebar, main grid). Usar container queries para responsiveness a nivel componente (cards, widgets, sidebars que aparecen en diferentes contexts). Son complementarios — no un reemplazo.

¿Cuál es la diferencia entre auto-fit y auto-fill en CSS Grid?

auto-fit colapsa los tracks vacíos a zero, stretchando los items restantes para llenar la row. auto-fill preserva los tracks vacíos como gaps. Usar auto-fit cuando quieres que los items crezcan y llenen el espacio available. Usar auto-fill cuando quieres que los items mantengan su size y dejen gaps.

¿Cómo manejo el issue de mobile viewport height?

Los mobile browsers dinámicamente muestran/ocultan la address bar, cambiando vh. Usar 100dvh (dynamic viewport height) en lugar de 100vh. Para soporte de browsers más viejos, usar el viewport meta tag con interactive-widget=resizes-content o JavaScript con window.innerHeight.