Flexbox
Flexbox (Flexible Box Layout) es el sistema de layout unidimensional de CSS: está diseñado para distribuir elementos en una sola dirección (fila o columna) y controlar cómo se estiran, encogen y alinean dentro de su contenedor. Antes de Flexbox, lograr layouts como centrar verticalmente, crear barras de navegación con espacios distribuidos, o hacer que los ítems de una fila crezcan proporcionalmente era un dolor—requería hacks con float, inline-block, table-cell y position. Con Flexbox, todo eso se resuelve con pocas propiedades intuitivas.
El concepto clave es la relación padre-hijo: aplicás propiedades de flexbox al contenedor (el padre) para definir la dirección, alineación y envoltura, y aplicás propiedades al ítem (los hijos) para controlar cómo crecen, se encogen y se ordenan individualmente. Las propiedades del contenedor son display: flex, flex-direction, justify-content, align-items, flex-wrap y gap. Las de los ítems son flex-grow, flex-shrink, flex-basis, align-self y order.
Propiedades del contenedor
Todo sistema Flexbox empieza con un contenedor al que se le aplica display: flex. A partir de ese momento, todos los hijos directos de ese contenedor se convierten automáticamente en flex items y responden a las propiedades de flexbox. No necesitás agregar clases extra ni modificar el HTML—el hecho de que el padre sea flex ya cambia completamente el comportamiento de los hijos. Los items pierden su comportamiento block/inline predeterminado y adoptan las reglas del contexto de formato flex.
flex-direction
Esta propiedad define el eje principal del contenedor: la dirección en la que los flex items se disponen uno detrás de otro. El valor por defecto es row (horizontal, de izquierda a derecha). Al cambiar la dirección, también cambia cuál es el eje principal y cuál el eje transversal. El eje principal es donde se distribuyen los items, el transversal es donde se alinean. row-reverse y column-reverse invierten el orden visual de los items, pero también el orden de lectura y tabulación—usálos con precaución por temas de accesibilidad.
/* row (default): horizontal, izquierda a derecha
Eje principal = horizontal, eje transversal = vertical */
.nav {
display: flex;
flex-direction: row;
}
/* row-reverse: horizontal, derecha a izquierda
CUIDADO: invierte tambien el orden de tabulacion */
.nav-rtl {
display: flex;
flex-direction: row-reverse;
}
/* column: vertical, arriba a abajo
Eje principal = vertical, eje transversal = horizontal
Ideal para: sidebars, formularios, stacks de contenido */
.sidebar-menu {
display: flex;
flex-direction: column;
}
/* column-reverse: vertical, abajo a arriba
Usos: chat messages (los mas recientes arriba) */
.chat-messages {
display: flex;
flex-direction: column-reverse;
}
justify-content (eje principal)
justify-content controla la distribución de los items a lo largo del eje principal. Es la propiedad que usás para crear espacios entre elementos, alinear contenido a un lado del contenedor, o distribuir el espacio libre de manera uniforme. Los valores más usados son flex-start (agrupar al inicio), flex-end (agrupar al final), center (centrar en el eje principal) y space-between (espacio igual entre items, sin espacio en los bordes).
.container {
display: flex;
}
/* flex-start (default): agrupa los items al inicio
[item][item][item] ____espacio libre */
.container-start {
justify-content: flex-start;
}
/* flex-end: agrupa al final
____espacio libre [item][item][item] */
.container-end {
justify-content: flex-end;
}
/* center: centra los items en el eje principal
__[item][item][item]__ */
.container-center {
justify-content: center;
}
/* space-between: espacio igual ENTRE items, sin espacios en bordes
[item] [item] [item] */
/* EL MAS USADO para navbars y toolbars */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
}
/* space-around: espacio igual ALREDEDOR de cada item
_[item]_[item]_[item]_
El espacio entre items es el doble que en los bordes */
.container-around {
justify-content: space-around;
}
/* space-evenly: espacio IGUAL en todas partes (bordes y entre items)
_[item]_[item]_[item]_ */
.container-evenly {
justify-content: space-evenly;
}
align-items y align-content (eje transversal)
Mientras justify-content controla el eje principal, align-items controla la alineación de los items en el eje transversal (perpendicular al eje principal). Si tu flex-direction es row, el eje transversal es vertical, así que align-items controla la alineación vertical. Si es column, el eje transversal es horizontal. El valor por defecto es stretch, que estira los items para que ocupen toda la altura del contenedor. align-content, en cambio, solo funciona cuando hay múltiples líneas de items (es decir, con flex-wrap: wrap) y controla el espacio entre esas líneas.
/* stretch (default): estira los items para llenar el eje transversal
Todos los items tendran la misma altura (la del contenedor) */
.container-stretch {
display: flex;
align-items: stretch;
}
/* flex-start: alinea al inicio del eje transversal
(top si es row, left si es column) */
.container-top {
display: flex;
align-items: flex-start;
}
/* flex-end: alinea al final del eje transversal
(bottom si es row, right si es column) */
.container-bottom {
display: flex;
align-items: flex-end;
}
/* center: centra en el eje transversal
PATRON CLASICO para centrar verticalmente */
.container-vcenter {
display: flex;
align-items: center;
}
/* baseline: alinea segun la linea base del texto
Util para que los textos de items con diferentes
tamanios de fuente esten alineados */
.container-baseline {
display: flex;
align-items: baseline;
}
/* ============================================
align-content: solo con flex-wrap: wrap
Controla el espacio ENTRE LINEAS de items
============================================ */
.wrapped-container {
display: flex;
flex-wrap: wrap;
align-content: flex-start; /* Lineas agrupadas arriba */
/* align-content: center; Lineas centradas */
/* align-content: space-between; Espacio entre lineas */
/* align-content: space-evenly; Espacio uniforme */
}
| Propiedad | Eje | Qué controla | Funciona siempre? |
|---|---|---|---|
justify-content |
Eje principal | Distribución de items a lo largo del eje principal (espacios, alineación). | Si |
align-items |
Eje transversal | Alineación de items individuales en el eje transversal. | Si |
align-content |
Eje transversal | Espacio entre líneas de items (solo con wrap). | Solo con flex-wrap: wrap y líneas múltiples |
align-self |
Eje transversal | Sobreescribe align-items para un item específico (propiedad del item). |
Si (se aplica en el item) |
flex-wrap y gap
Por defecto, los flex items intentan caber todos en una sola línea, encogiéndose si es necesario. flex-wrap: wrap cambia este comportamiento: si los items no caben, pasan a una nueva línea. Esto es fundamental para layouts responsivos donde no sabés de antemano cuántos items hay ni el tamaño de la pantalla. La propiedad gap (disponible en Flexbox desde 2020, con soporte completo en todos los navegadores) define el espacio entre items sin necesidad de margenes. gap reemplazó los viejos hacks de margenes negativos y :last-child { margin-right: 0 }.
/* nowrap (default): todo en una linea, se encogen */
.container-nowrap {
display: flex;
flex-wrap: nowrap;
}
/* wrap: los items pasan a la siguiente linea si no caben */
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
/* wrap-reverse: como wrap, pero las lineas nuevas van arriba */
.container-reverse {
display: flex;
flex-wrap: wrap-reverse;
}
/* ============================================
gap: espacio entre items (sin margenes!)
============================================ */
/* gap uniforme (filas y columnas) */
.flex-grid {
display: flex;
flex-wrap: wrap;
gap: 1rem; /* 1rem entre todos los items */
}
/* gap diferente para filas y columnas */
.flex-grid-dense {
display: flex;
flex-wrap: wrap;
gap: 1rem 1.5rem; /* row-gap: 1rem, column-gap: 1.5rem */
/* o explicitamente: */
row-gap: 1rem;
column-gap: 1.5rem;
}
/* ============================================
Ejemplo practico: card grid responsivo
============================================ */
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.card-grid .card {
/* Cada card ocupa minimo 280px, maximo 1fr del espacio libre */
flex: 1 1 280px;
/* Equivalente a: flex-grow: 1, flex-shrink: 1, flex-basis: 280px */
/* Si el contenedor mide 800px: caben 2 cards de ~380px cada una
Si mide 1200px: caben 3 de ~360px
Si el screen es 600px: 1 card por linea */
}
gap vs margenes
gap solo añade espacio entre los items, nunca en los bordes del contenedor. Esto es superior a los margenes porque evita el problema del último item (necesitabas :last-child { margin-right: 0 }) y el problema del colapso de márgenes (gap no colapsa). Para Flexbox y Grid, usá siempre gap en lugar de margenes para espaciar items.
Propiedades de los ítems
Las propiedades de los ítems flex controlan cómo cada elemento individual se comporta dentro del contenedor flex. Mientras que las propiedades del contenedor definen las reglas generales (dirección, alineación, envoltura), las propiedades de los ítems permiten ajustar cada uno independientemente: cuánto debe crecer, cuánto debe encoger, cuál es su tamaño base y si debe alinearse diferente al resto.
flex-grow, flex-shrink y flex-basis
Estas tres propiedades juntas definen el comportamiento de crecimiento y encogimiento de cada flex item. flex-grow controla cuánto del espacio libre disponible se reparte a este item (0 = no crece, 1 = crece proporcionalmente). flex-shrink controla cuánto se reduce si no hay suficiente espacio (0 = no se encoge, 1 = se encoge proporcionalmente). flex-basis es el tamaño inicial del item antes de que se distribuya el espacio libre (puede ser un tamaño fijo, un porcentaje o auto).
En la práctica, casi siempre usás el shorthand flex que combina los tres. El patrón más común es flex: 1 1 300px que significa: "empieza en 300px, crece si hay espacio, se encoge si hace falta". Otro patrón clásico es flex: 1 (equivalente a flex: 1 1 0%) que hace que un item ocupe todo el espacio disponible dividiéndolo equitativamente con otros items que también tengan flex: 1.
/* ============================================
flex-grow: cuanto espacio libre absorbe (0 = nada)
============================================ */
/* Los items con grow: 0 no crecen, con grow: 1 se reparten igual */
.nav-main {
display: flex;
}
.nav-main .logo {
flex-grow: 0; /* Tamaño fijo */
}
.nav-main .nav-links {
flex-grow: 1; /* Ocupa todo el espacio restante */
}
/* Proporciones desiguales: el primero crece el doble */
unequal-grow {
display: flex;
}
.unequal-grow .sidebar {
flex-grow: 1; /* 1 parte del espacio libre */
}
.unequal-grow .main-content {
flex-grow: 2; /* 2 partes del espacio libre (el doble) */
}
/* ============================================
flex-shrink: cuanto se reduce si falta espacio
============================================ */
/* Cero: el item NO se encoge (mantiene su tamaño) */
.important-element {
flex-shrink: 0;
}
/* Ejemplo: navbar con logo que nunca debe encogerse */
.navbar {
display: flex;
}
.navbar .logo {
flex-shrink: 0; /* Siempre su tamaño completo */
width: 120px;
}
.navbar .search-bar {
flex: 1; /* Se encoge si hace falta */
min-width: 0; /* Permite que el texto se trunque */
}
.navbar .user-menu {
flex-shrink: 0;
}
/* ============================================
flex-basis: tamaño inicial antes de distribuir
============================================ */
/* Tamaño fijo de inicio */
.item-fixed {
flex-basis: 200px;
}
/* auto: usa el tamaño natural del contenido */
.item-auto {
flex-basis: auto;
}
/* Porcentaje del contenedor */
.item-percent {
flex-basis: 50%;
}
/* ============================================
Shorthand: flex (grow shrink basis)
============================================ */
/* flex: 1 = flex: 1 1 0% (crece, se encoge, base = 0) */
.sidebar-layout {
display: flex;
}
.sidebar-layout .sidebar {
flex: 0 0 280px; /* Fijo en 280px, no crece ni se encoge */
}
.sidebar-layout .content {
flex: 1; /* Crece para ocupar el resto */
}
/* flex: 1 1 300px = crece, se encoge, empieza en 300px */
.responsive-grid {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.responsive-grid .item {
flex: 1 1 300px; /* Minimo 300px, crece si hay espacio */
}
/* Patrones comunes de flex shorthand */
.flex-none { flex: 0 0 auto; } /* No crece ni se encoge */
.flex-1 { flex: 1 1 0%; } /* Crea partes iguales */
.flex-auto { flex: 1 1 auto; } /* Crece pero respeta contenido */
.flex-fill { flex: 1 1 0%; } /* Igual que flex-1 */
align-self y order
align-self permite sobreescribir la alineación del contenedor (align-items) para un ítem específico. Es la propiedad del item, no del contenedor, y acepta los mismos valores que align-items: auto, flex-start, flex-end, center, baseline y stretch. order cambia el orden visual de un item sin modificar el HTML. El valor por defecto es 0, y items con order menor aparecen primero. Aunque es tentador usar order para reorganizar layouts, la recomendación es usarlo con moderación porque afecta el orden de tabulación y el flujo lógico del DOM, lo cual puede confundir a usuarios de lectores de pantalla.
/* align-self: sobreescribir la alineacion de un item
El contenedor tiene align-items: flex-start (arriba) */
.container {
display: flex;
align-items: flex-start;
gap: 1rem;
}
/* Este item se alinea al fondo, no arriba como los demas */
.container .special-item {
align-self: flex-end;
}
/* Este item se centra verticalmente */
.container .centered-item {
align-self: center;
}
/* Ejemplo: card con un boton pegado al fondo */
.card-footer {
display: flex;
align-items: center; /* Todos centrados */
gap: 0.5rem;
}
.card-footer .spacer {
flex: 1; /* Empuja el boton a la derecha */
}
/* ============================================
order: cambiar el orden visual
============================================ */
/* Los items se muestran en orden de menor a mayor order */
/* Todos los items por defecto tienen order: 0 */
.sidebar-layout {
display: flex;
}
/* En desktop: sidebar primero, contenido despues */
@media (min-width: 1024px) {
.sidebar-layout .sidebar { order: 1; }
.sidebar-layout .content { order: 2; }
}
/* En mobile: contenido primero, sidebar despues
(sin cambiar el HTML, solo con CSS) */
@media (max-width: 1023px) {
.sidebar-layout .sidebar { order: 2; }
.sidebar-layout .content { order: 1; }
}
/* ============================================
min-width: 0 — el fix para el overflow
============================================ */
/* IMPORTANTE: los flex items tienen min-width: auto por defecto,
lo que significa que NUNCA se encogen por debajo de su contenido.
Esto puede causar overflow. La solucion: */
.flex-item-safe {
flex: 1;
min-width: 0; /* Permite que el item se encogue por debajo del contenido */
/* Tambien aplica a min-height: 0 para flex-direction: column */
}
| Shorthand | Se expande a | Comportamiento |
|---|---|---|
flex: 0 1 auto |
flex-grow: 0; flex-shrink: 1; flex-basis: auto |
Valor por defecto. No crece, se encoge si hace falta, respeta el tamaño del contenido. |
flex: 1 |
flex-grow: 1; flex-shrink: 1; flex-basis: 0% |
Crece para ocupar espacio libre proporcionalmente. El más usado para layouts. |
flex: none |
flex-grow: 0; flex-shrink: 0; flex-basis: auto |
Tamaño fijo del contenido. No crece ni se encoge. Útil para elementos que no deben cambiar. |
flex: auto |
flex-grow: 1; flex-shrink: 1; flex-basis: auto |
Crece y se encoge, pero respeta el tamaño del contenido como base. |
flex: 1 1 300px |
flex-grow: 1; flex-shrink: 1; flex-basis: 300px |
Empieza en 300px, crece o se encoge según el espacio. Ideal para cards responsivas. |
flex: 0 0 280px |
flex-grow: 0; flex-shrink: 0; flex-basis: 280px |
Sidebar fija de 280px que no crece ni se encoge. |
No uses flex: 1 para texto largo
Si un flex item con flex: 1 contiene texto largo (párrafos, URLs, palabras sin espacios), el texto puede hacer que el item no se encoga correctamente porque el tamaño mínimo del contenido supera el espacio disponible. La solución es agregar min-width: 0 (o overflow: hidden) al item para permitir que se encoga por debajo del tamaño de su contenido. Este es uno de los bugs más comunes de Flexbox.
Patrones comunes con Flexbox
Una vez entendidas las propiedades, la mejor forma de interiorizar Flexbox es ver cómo se aplican a los patrones de layout reales que usás todos los días. Estos son los patrones más frecuentes y la combinación exacta de propiedades para cada uno. La mayoría se resuelven con 3-5 líneas de CSS, lo cual demuestra la potencia de Flexbox frente a las técnicas legacy.
Centrado perfecto (vertical y horizontal)
El Santo Grial de CSS que antes requería hacks como position: absolute + transform: translate(-50%, -50%) o display: table-cell + vertical-align: middle. Con Flexbox, centrar cualquier elemento en ambas direcciones es literalmente dos líneas. Es probablemente la razón número uno por la que los devs aprenden Flexbox.
/* Centrado perfecto */
.center-container {
display: flex;
justify-content: center; /* Eje principal (horizontal) */
align-items: center; /* Eje transversal (vertical) */
min-height: 100vh; /* Necesita altura para centrar verticalmente */
}
Navbar con logo, links y botón
El patrón de navbar clásico: logo a la izquierda, enlaces en el centro o distribuidos, y un botón de acción a la derecha. Con justify-content: space-between no necesitás separar en tres grupos—Flexbox se encarga de la distribución automáticamente.
/* Navbar: logo a la izq, links al centro, boton a la der */
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--space-lg);
height: 64px;
border-bottom: 1px solid var(--border-color);
}
.navbar .logo {
flex-shrink: 0; /* Nunca encoger el logo */
}
.navbar .nav-links {
display: flex;
gap: var(--space-md);
list-style: none;
}
.navbar .cta-button {
flex-shrink: 0; /* El boton tampoco se encoge */
}
/* Variante: logo y links a la izq, boton a la der */
.navbar-alt {
display: flex;
align-items: center;
gap: var(--space-lg);
}
.navbar-alt .nav-links {
display: flex;
gap: var(--space-sm);
flex: 1; /* Ocupa el espacio entre logo y boton */
}
Card layout responsivo
Un grid de cards que se adapta automáticamente al ancho disponible sin media queries. Cada card tiene un tamaño mínimo (flex-basis) y crece proporcionalmente hasta que entran más cards por fila. Es el patrón más usado para grids de contenido que no son Grid.
/* Card grid responsivo sin media queries */
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.card-grid .card {
flex: 1 1 280px; /* Minimo 280px, crece para llenar la fila */
max-width: calc(50% - 0.75rem); /* Maximo 2 por fila en pantallas grandes */
background: var(--bg-secondary);
border-radius: var(--border-radius);
padding: var(--space-lg);
}
/* Layout sidebar + contenido */
.app-layout {
display: flex;
min-height: 100vh;
}
.app-layout .sidebar {
flex: 0 0 260px;
border-right: 1px solid var(--border-color);
padding: var(--space-lg);
}
.app-layout .main {
flex: 1;
padding: var(--space-xl);
min-width: 0; /* Fix para overflow de contenido */
}
/* En mobile: sidebar se oculta, contenido ocupa todo */
@media (max-width: 768px) {
.app-layout {
flex-direction: column;
}
.app-layout .sidebar {
flex: 0 0 auto; /* Solo el alto de su contenido */
}
}
Footer sticky
El patrón de "footer pegajoso" donde el footer se queda en el fondo de la página cuando el contenido es corto, y aparece naturalmente debajo del contenido cuando es largo. Con Flexbox, la técnica es aplicar display: flex y flex-direction: column al wrapper de la página, darle min-height: 100vh, y usar flex: 1 en el contenido principal para que crezca y empuje el footer hacia abajo.
/* Footer sticky con Flexbox */
.page-wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.page-wrapper .site-header {
flex-shrink: 0; /* El header nunca se encoge */
}
.page-wrapper .main-content {
flex: 1; /* Crece para empujar el footer abajo */
/* Si el contenido es corto: main crece, footer queda abajo
Si el contenido es largo: main crece, footer baja naturalmente */
}
.page-wrapper .site-footer {
flex-shrink: 0; /* El footer nunca se encoge */
}
Flexbox vs Grid: cuándo usar cada uno
Flexbox es unidimensional (una dirección a la vez): ideal para barras de navegación, herramientas, filas de botones, distribución de elementos en un eje, y layouts simples con sidebar. Grid es bidimensional (filas y columnas): ideal para grids complejos de cards, layouts de páginas completas con áreas definidas, y cualquier cosa que necesite control sobre dos ejes. En la práctica, se usan juntos: Flexbox para componentes internos (navbar, card header, listas) y Grid para el layout principal de la página.
Probá en MiniDevTools
Si querés experimentar con lo que vimos en esta sección, probá el Flexbox Playground.