Flexbox a fondo: grow, shrink, basis, wrap y patrones
En esta lección
- Cómo reparte el espacio Flexbox
- flex-basis: el punto de partida
- flex-grow: repartir lo que sobra
- flex-shrink: quitar lo que falta
- flex: 1 frente a flex: auto
- min-width: 0: el problema del texto largo
- flex-wrap, align-content y las líneas
- gap, row-gap y column-gap
- order y el orden de lectura
- margin: auto: empujar y separar
- Patrón 1: barra de navegación
- Patrón 2: tarjetas que se reparten el ancho
- Patrón 3: pie pegado y zona con scroll propio
- Cuándo no usar Flexbox
- Errores frecuentes
- Resumen
Con la lección de Flexbox ya sabes colocar elementos en fila o en columna y alinearlos. Lo que muchas veces se queda sin entender es cómo decide Flexbox el ancho de cada hijo: por qué dos columnas con flex: 1 salen iguales y con flex: auto no, por qué un texto largo rompe un diseño o por qué las tarjetas de la última fila quedan más anchas. En esta lección verás el reparto del espacio con números reales, los problemas clásicos y varios patrones de uso diario.
Si todavía no has visto los ejes, justify-content o align-items, empieza por la lección básica.
Cómo reparte el espacio Flexbox
Para cada línea, el navegador sigue siempre estos pasos:
- Calcula el tamaño base de cada hijo (
flex-basis; si esauto, mirawidtho el contenido). - Suma esos tamaños base y los compara con el ancho del contenedor (descontando los
gap). - Si sobra espacio, lo reparte según
flex-grow. - Si falta espacio, lo quita según
flex-shrink. - Respeta los límites
min-widthymax-widthde cada hijo.
Con eso se explica casi todo. Veamos cada paso con cifras.
flex-basis: el punto de partida
flex-basis es el tamaño del hijo antes de repartir. Funciona como un width (en una fila) o un height (en una columna), y si ambos existen, gana flex-basis.
.item { width: 300px; flex-basis: 150px; } /* arranca en 150px, no en 300px */
Valores útiles:
| Valor | Significado |
|---|---|
auto | Usa width, y si no hay, el tamaño del contenido |
0 | Parte de cero: todo el espacio es “sobrante” |
200px, 30% | Un tamaño concreto |
content | El tamaño del contenido, ignorando width |
Consejo:
flex-basises un tamaño deseado, no una garantía. Si hay poco sitio, el hijo encoge, y si hay mucho, puede crecer. Para fijar de verdad un ancho, usaflex: none(oflex: 0 0 200px) junto a él.
flex-grow: repartir lo que sobra
Un contenedor de 700px con tres hijos de flex-basis: 100px suma 300px, así que sobran 400px. Con flex-grow: 1, 2 y 1, el espacio sobrante se divide en 4 partes (1 + 2 + 1) de 100px:
.a { flex: 1 1 100px; } /* 100 + 1 parte = 200px */
.b { flex: 2 1 100px; } /* 100 + 2 partes = 300px */
.c { flex: 1 1 100px; } /* 100 + 1 parte = 200px */
Fíjate: flex-grow reparte el sobrante, no el total. El hijo b no mide el doble que los otros (300 frente a 200), porque todos partían de 100px. Para que las proporciones sean exactas, la base debe ser 0.
.a { flex: 1 1 0; } /* 700 / 4 * 1 = 175px */
.b { flex: 2 1 0; } /* 700 / 4 * 2 = 350px */
.c { flex: 1 1 0; } /* 175px */
Esa es la razón por la que flex: 1 se escribe como 1 1 0%: base cero y proporciones limpias.
Y si la suma de los flex-grow es menor que 1, no se reparte todo el sobrante, sino solo esa fracción. Con un único hijo con flex-grow: 0.5 y 400px libres, crece 200px.
flex-shrink: quitar lo que falta
Ahora al revés: un contenedor de 500px con tres hijos de flex-basis 300, 200 y 200px. Suman 700px y faltan 200px. Con flex-shrink: 1 en todos, cabría pensar que cada uno pierde 66px, pero no es así: la reducción se reparte en proporción al flex-shrink multiplicado por el tamaño base. Así, los hijos grandes encogen más.
pesos: 300 x 1 = 300, 200 x 1 = 200, 200 x 1 = 200 (total 700)
pierde: 200 * 300/700 = 85,7px | 200 * 200/700 = 57,1px | 57,1px
queda: 214,3px | 142,9px | 142,9px (suma 500)
Con flex-shrink: 0 un hijo se niega a encoger (es lo que quieres para un logo o un botón). Un valor mayor que 1 lo hace encoger más rápido que los demás.
Nota: si todos los hijos son rígidos (
flex-shrink: 0) y no caben, se desbordan del contenedor. Es una señal de que necesitasflex-wrapo reducir tamaños.
flex: 1 frente a flex: auto
La diferencia entre estos dos atajos se ve con números. Un contenedor de 600px con tres hijos cuyo contenido mide 100, 200 y 100px:
.fila > * { flex: 1; } /* 1 1 0% -> base 0, sobran 600 -> 200px cada uno */
.fila > * { flex: auto; } /* 1 1 auto -> base 100, 200, 100; sobran 200 */
/* cada uno recibe +66,7px: 166,7 / 266,7 / 166,7px */
| Atajo | Equivale a | Resultado |
|---|---|---|
flex: 1 | 1 1 0% | Columnas idénticas, sin importar el contenido |
flex: auto | 1 1 auto | Cada hijo parte de su contenido: el largo queda más ancho |
flex: none | 0 0 auto | Rígido, con el tamaño de su contenido |
flex: 0 0 200px | Ancho fijo de 200px | |
flex: 1 1 200px | Quiere 200px, pero se adapta |
Usa flex: 1 cuando quieras columnas iguales; flex: auto cuando el contenido deba influir en el reparto, por ejemplo en una barra de pestañas.
min-width: 0: el problema del texto largo
Aquí está el error que más tiempo hace perder. Un hijo flex tiene por defecto min-width: auto, que significa: “no encojas por debajo del tamaño de tu contenido”. Con texto sin espacios (una URL, un nombre de archivo largo), el hijo se niega a encoger y empuja a los demás o se sale del contenedor.
.fichero { display: flex; gap: 8px; }
.fichero .nombre { flex: 1; } /* el nombre largo empuja el botón fuera */
La solución es permitirle encoger a cero:
.fichero .nombre {
flex: 1;
min-width: 0; /* ahora sí puede encoger */
overflow: hidden;
text-overflow: ellipsis; /* puntos suspensivos */
white-space: nowrap;
}
Lo mismo ocurre en la dirección vertical con min-height: 0 cuando un hijo de una columna flex debe tener su propio scroll (lo verás en el patrón de aplicación más abajo). También overflow: hidden (o auto) en el hijo cambia el min-width automático a 0, y es otra forma de resolverlo.
flex-wrap, align-content y las líneas
Con flex-wrap: wrap, los hijos que no caben bajan a otra línea. El flex-basis es entonces lo que decide cuántos caben por línea; el flex-grow decide cómo se reparte el sobrante de cada línea por separado.
.tarjetas {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.tarjeta { flex: 1 1 220px; }
Con 600px de ancho caben dos tarjetas por línea; con 1000px, cuatro. Esto da un diseño adaptable sin media queries.
La última línea ensanchada
Si tienes 5 tarjetas y caben 3 por línea, la segunda línea tiene 2 y, con flex-grow: 1, se estiran para ocupar todo el ancho: quedan más anchas que las de arriba. Tienes dos salidas:
/* 1. Tope de ancho: no crecen más de la cuenta */
.tarjeta { flex: 1 1 220px; max-width: 320px; }
/* 2. Pasa a Grid, que sí mantiene las columnas alineadas */
.tarjetas { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
Si necesitas columnas exactas en Flexbox, descuenta los gap del cálculo. Para tres por fila con un gap de 16px:
.tarjeta { flex: 0 0 calc((100% - 2 * 16px) / 3); }
align-content: el espacio entre líneas
Cuando hay varias líneas y el contenedor tiene más alto que ellas, align-content reparte ese alto entre las líneas (no entre los hijos). Por defecto vale stretch, que estira las líneas y deja huecos si los hijos no se estiran.
.etiquetas {
display: flex;
flex-wrap: wrap;
gap: 8px;
height: 200px;
align-content: flex-start; /* las líneas se agrupan arriba, sin huecos */
}
Con una sola línea no hace nada: para eso está align-items.
gap, row-gap y column-gap
gap pone espacio entre los hijos y entre las líneas. Puedes separar los dos ejes:
.tarjetas { gap: 24px 12px; } /* 24px entre filas, 12px entre columnas */
.tarjetas { row-gap: 24px; column-gap: 12px; } /* lo mismo, con nombre */
Los gap cuentan en el cálculo del paso 2, así que reducen el espacio disponible. Si las columnas “no caben”, comprueba si el gap es el culpable.
order y el orden de lectura
order mueve un hijo en el orden visual (0 por defecto; menor, antes). Es cómodo para subir una imagen en el móvil:
.articulo { display: flex; flex-direction: column; }
.articulo .foto { order: -1; } /* la foto pasa arriba */
Pero el teclado y los lectores de pantalla siguen el orden del HTML, así que no lo uses para corregir un HTML mal ordenado. Si el orden visual y el del código se separan mucho, la navegación con Tab salta de forma extraña.
margin: auto: empujar y separar
Un margen auto en un hijo flex se come todo el espacio libre en ese lado, antes de que actúe justify-content. Eso permite separar grupos sin contenedores extra:
.barra { display: flex; align-items: center; gap: 12px; }
.barra .buscar { margin-left: auto; } /* este y los siguientes, a la derecha */
Con margin-inline: auto en un hijo, queda centrado y los demás a los lados. En una columna, margin-top: auto lo manda abajo del todo.
Patrón 1: barra de navegación
Logo a un lado, enlaces a otro, acciones al final, y que se adapte al móvil:
<header class="barra">
<a class="logo" href="/">Gimnasio Forma</a>
<nav class="enlaces" aria-label="Principal">
<a href="/clases">Clases</a>
<a href="/tarifas">Tarifas</a>
</nav>
<a class="boton" href="/alta">Hazte socio</a>
</header>
.barra {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px 16px;
padding: 12px 16px;
}
.logo { flex: none; font-weight: bold; }
.enlaces { display: flex; gap: 16px; margin-inline: auto 0; } /* empuja a la derecha */
.boton { flex: none; }
El logo y el botón no encogen (flex: none); si no hay sitio, los enlaces bajan a otra línea gracias a flex-wrap.
Patrón 2: tarjetas que se reparten el ancho
.planes { display: flex; flex-wrap: wrap; gap: 16px; }
.plan {
flex: 1 1 240px;
max-width: 360px;
display: flex;
flex-direction: column; /* la tarjeta es, a su vez, una columna flex */
}
.plan .precio { margin-top: auto; } /* el precio siempre abajo */
Aquí hay dos niveles de Flexbox: el contenedor reparte el ancho y cada tarjeta reparte su alto. Es muy habitual anidar flex, y no hay problema.
Patrón 3: pie pegado y zona con scroll propio
El pie que no flota ya lo viste en la lección básica. Una versión más completa es la de aplicación: cabecera y pie fijos y solo la zona central con scroll.
body {
display: flex;
flex-direction: column;
height: 100dvh; /* alto exacto de la ventana */
margin: 0;
}
header, footer { flex: none; }
main {
flex: 1;
min-height: 0; /* imprescindible para que el scroll funcione */
overflow: auto;
}
Sin min-height: 0, main se negaría a encoger por debajo de su contenido, el body crecería más que la ventana y no habría scroll interno. Es exactamente el min-width: 0 de antes, pero en vertical.
Cuándo no usar Flexbox
Si necesitas que las filas y las columnas se alineen entre sí (una tabla de tarjetas, un calendario), Grid es mejor. Para centrar una sola cosa tienes Centrar elementos, y para recetas completas, Maquetaciones típicas.
Errores frecuentes
- Creer que
flex-growreparte el total. Reparte el sobrante después de las bases. Para proporciones exactas, pon base 0 (flex: 2 1 0). - Olvidar
min-width: 0. Un texto largo o una imagen ancha no encoge y rompe el diseño. - Usar
widthen el hijo y esperar que mande. Si hayflex-basis, gana este. Yflex-shrinkpuede encoger elwidthigualmente. - Esperar columnas iguales con
flex: auto. Conautocada una parte de su contenido; para iguales,flex: 1. - Tarjetas de la última fila demasiado anchas. Pon
max-widtho usa Grid conauto-fill. - Usar
align-contentcon una línea. No hace nada: lo que buscas esalign-items. - Olvidar el
gapen el cálculo de anchos porcentuales. Tres columnas del 33 % congapya no caben: descuenta elgapo usaflex: 1. - Depender de
orderpara el orden de lectura. El teclado sigue el HTML.
Resumen
| Propiedad o truco | Qué hace |
|---|---|
flex-basis | Tamaño de partida antes de repartir |
flex-grow | Reparte el espacio sobrante en proporción |
flex-shrink | Reparte lo que falta, ponderado por el tamaño base |
flex: 1 | 1 1 0%: columnas iguales |
flex: auto | 1 1 auto: parte del contenido |
flex: none | 0 0 auto: rígido |
min-width: 0 | Deja que un hijo encoja por debajo de su contenido |
flex-wrap + flex: 1 1 220px | Tarjetas adaptables sin media queries |
align-content | Reparte el espacio entre líneas (con wrap) |
gap, row-gap, column-gap | Separación entre hijos y líneas |
margin: auto | Empuja un hijo al extremo |
min-height: 0 | Scroll interno en una columna flex |
En la siguiente lección aprenderás Grid, la herramienta que complementa a Flexbox cuando necesitas filas y columnas a la vez; más adelante tendrás Grid a fondo.
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.