Flexbox
En esta lección
- El contenedor y sus hijos
- Los dos ejes
- flex-direction: la dirección del eje principal
- flex-wrap y flex-flow: una línea o varias
- Alinear en el eje principal: justify-content
- Alinear en el eje cruzado: align-items
- align-content: cuando hay varias líneas
- align-self: un hijo diferente
- gap: separación entre hijos
- order: cambiar el orden visual
- Tamaño de los hijos: flex-grow, flex-shrink y flex-basis
- margin: auto: empujar elementos
- Patrones habituales
- Errores frecuentes
- ¿Flexbox o Grid?
- Resumen
Flexbox sirve para colocar elementos en una fila o en una columna y repartir el espacio entre ellos. Es la forma más fácil de centrar cosas y de hacer menús, barras de herramientas, tarjetas en fila o formularios en línea. En esta lección verás todas sus propiedades, qué hace cada valor y los patrones que usarás a diario.
El contenedor y sus hijos
Flexbox se activa en el padre con display: flex. Sus hijos directos pasan a ser elementos flexibles (los nietos no se ven afectados):
<nav class="menu">
<a href="#">Inicio</a>
<a href="#">Cursos</a>
<a href="#">Contacto</a>
</nav>
.menu {
display: flex;
gap: 16px; /* espacio entre los hijos */
}
Solo con eso, los tres enlaces se colocan en fila con 16px de separación.
Las propiedades de Flexbox se dividen en dos grupos, y es importante no mezclarlas:
| Van en el contenedor | Van en los hijos |
|---|---|
display: flex, flex-direction, flex-wrap, flex-flow | flex-grow, flex-shrink, flex-basis, flex |
justify-content, align-items, align-content, gap | align-self, order |
Consejo: también existe
display: inline-flex. El contenedor se comporta como un elemento en línea (solo ocupa lo que necesita), pero sus hijos siguen siendo flexibles. Es útil para botones con icono y texto.
Los dos ejes
Todo Flexbox gira alrededor de dos ejes:
- Eje principal: la dirección en la que se colocan los hijos. Por defecto, horizontal (de izquierda a derecha).
- Eje cruzado: el perpendicular al principal. Por defecto, vertical.
Entender esto es la clave: justify-content siempre trabaja en el eje principal y align-items siempre en el eje cruzado. Si cambias la dirección, los ejes giran y esas propiedades cambian de sentido con ellos.
flex-direction: la dirección del eje principal
flex-direction: row; /* en fila, de izquierda a derecha (por defecto) */
flex-direction: row-reverse; /* en fila, de derecha a izquierda */
flex-direction: column; /* en columna, de arriba abajo */
flex-direction: column-reverse; /* en columna, de abajo arriba */
Con column, el eje principal pasa a ser vertical. Por eso, en una columna, justify-content: center centra en vertical y align-items: center centra en horizontal. Es el origen de muchas confusiones.
Cuidado:
row-reverseycolumn-reversesolo cambian el orden visual. Quien navega con el teclado o usa un lector de pantalla sigue el orden del HTML. No los uses para “arreglar” un HTML mal ordenado.
flex-wrap y flex-flow: una línea o varias
Por defecto, Flexbox mete todos los hijos en una sola línea, aunque tenga que encogerlos. Con flex-wrap les dejas pasar a la línea siguiente:
flex-wrap: nowrap; /* todo en una línea (por defecto) */
flex-wrap: wrap; /* si no caben, bajan a otra línea */
flex-wrap: wrap-reverse; /* igual, pero las líneas nuevas van encima */
flex-flow es un atajo que junta dirección y ajuste:
.galeria { flex-flow: row wrap; } /* = flex-direction: row; flex-wrap: wrap; */
Alinear en el eje principal: justify-content
Reparte el espacio sobrante a lo largo del eje principal:
justify-content: flex-start; /* todos al principio (por defecto) */
justify-content: flex-end; /* todos al final */
justify-content: center; /* todos en el centro */
justify-content: space-between; /* el primero y el último pegados a los bordes, el resto repartido */
justify-content: space-around; /* cada hijo con el mismo margen a ambos lados */
justify-content: space-evenly; /* exactamente el mismo espacio en todos los huecos */
La diferencia entre los tres space-* se ve mejor con un dibujo de tres cajas ([ ]) en una fila:
space-between: [1]-------[2]-------[3]
space-around: --[1]----[2]----[3]--
space-evenly: ---[1]---[2]---[3]---
En space-around, los bordes tienen la mitad de espacio que los huecos centrales, porque cada caja pone su margen a los dos lados. En space-evenly todos los huecos son iguales.
Consejo: también verás
startyenden lugar deflex-startyflex-end. Funcionan igual en los navegadores actuales y se usan también en Grid.
Alinear en el eje cruzado: align-items
Coloca cada hijo en el eje cruzado dentro de su línea:
align-items: stretch; /* se estiran hasta ocupar todo el alto (por defecto) */
align-items: flex-start; /* pegados arriba */
align-items: flex-end; /* pegados abajo */
align-items: center; /* centrados */
align-items: baseline; /* alineados por la línea base del texto */
stretch es el valor por defecto y explica por qué las cajas de una fila miden todas lo mismo de alto aunque tengan distinto contenido. Solo funciona si el hijo no tiene una altura fija.
baseline es útil cuando los hijos tienen textos de tamaños distintos, por ejemplo un precio grande junto a “/mes” pequeño: sus letras quedan apoyadas en la misma línea.
align-content: cuando hay varias líneas
align-content reparte el espacio entre las líneas, no entre los hijos. Por eso solo hace algo si hay flex-wrap: wrap y varias líneas, y si el contenedor tiene más alto del necesario:
.etiquetas {
display: flex;
flex-wrap: wrap;
height: 300px;
align-content: flex-start; /* las líneas se agrupan arriba, sin huecos entre ellas */
}
Admite los mismos valores que justify-content (center, space-between…) más stretch, que es el valor por defecto.
align-self: un hijo diferente
Si solo un hijo debe alinearse de otra forma, align-self sobrescribe el align-items del padre para ese hijo:
.barra { display: flex; align-items: flex-start; }
.barra .avatar { align-self: center; } /* solo el avatar, centrado */
No existe justify-self en Flexbox. Para empujar un hijo en el eje principal se usa margin: auto, que verás más abajo.
gap: separación entre hijos
gap pone espacio entre los hijos, nunca en los bordes exteriores:
.tarjetas {
display: flex;
flex-wrap: wrap;
gap: 16px; /* 16px entre filas y entre columnas */
/* gap: 24px 12px; primero entre filas, después entre columnas */
}
Antes de gap se usaban márgenes en los hijos, lo que obligaba a quitar el margen del último con :last-child. Hoy gap funciona en todos los navegadores y es mucho más limpio.
order: cambiar el orden visual
Todos los hijos tienen order: 0. Se colocan de menor a mayor; a igual valor, en el orden del HTML:
.destacado { order: -1; } /* pasa delante de todos */
.publicidad { order: 1; } /* se va al final */
Igual que row-reverse, solo cambia lo que se ve, no el orden de lectura del teclado ni del lector de pantalla. Úsalo con moderación, por ejemplo para subir una imagen en el móvil.
Tamaño de los hijos: flex-grow, flex-shrink y flex-basis
Estas tres propiedades deciden cuánto mide cada hijo en el eje principal.
flex-basis: el tamaño de partida
Es el tamaño que el hijo quiere tener antes de repartir el espacio. Con auto (por defecto), usa su width (o su contenido si no lo tiene).
.columna { flex-basis: 200px; }
flex-grow: cómo repartir lo que sobra
Si sobra espacio en la línea, se reparte en proporción al flex-grow de cada hijo. Por defecto vale 0: no crecen.
.buscador input { flex-grow: 1; } /* ocupa todo lo que sobre */
.buscador button { flex-grow: 0; } /* se queda con su tamaño */
Ejemplo numérico: un contenedor de 600px con dos hijos de flex-basis: 100px. Sobran 400px. Si el primero tiene flex-grow: 1 y el segundo flex-grow: 3, el primero recibe 100px extra (mide 200px) y el segundo 300px extra (mide 400px).
flex-shrink: cómo encoger si falta espacio
Si los hijos no caben, encogen en proporción a su flex-shrink. Por defecto vale 1: todos encogen por igual. Con 0, un hijo se niega a encoger:
.logo { flex-shrink: 0; } /* el logo nunca se aplasta */
El atajo flex
En la práctica casi siempre se usa el atajo flex, que junta las tres en el orden grow, shrink, basis:
| Valor | Equivale a | Qué hace |
|---|---|---|
flex: initial | 0 1 auto | El valor por defecto: no crece, sí encoge |
flex: 1 | 1 1 0% | Todos los hijos con flex: 1 miden lo mismo |
flex: auto | 1 1 auto | Crece y encoge, partiendo del tamaño de su contenido |
flex: none | 0 0 auto | Rígido: ni crece ni encoge |
flex: 1 1 250px | 1 1 250px | Parte de 250px y se adapta |
La diferencia entre flex: 1 y flex: auto es sutil pero importante. Con flex: 1 la base es 0, así que todo el espacio se reparte a partes iguales y las columnas quedan idénticas. Con flex: auto cada hijo parte de su contenido, así que el que tiene más texto acaba más ancho.
margin: auto: empujar elementos
En un contenedor flex, un margen auto se come todo el espacio libre en esa dirección. Es la forma de separar un elemento del resto:
.cabecera { display: flex; align-items: center; gap: 16px; }
.cabecera .login { margin-left: auto; } /* se va a la derecha del todo */
Con margin: auto en un hijo único, lo centras en los dos ejes.
Patrones habituales
Centrar en horizontal y vertical
El truco más buscado de CSS, en tres líneas:
.centrado {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
Barra de navegación
Logo a la izquierda y enlaces a la derecha:
<header class="barra">
<a class="barra-logo" href="/">Gimnasio Forma</a>
<nav class="barra-enlaces">
<a href="/clases">Clases</a>
<a href="/tarifas">Tarifas</a>
<a href="/contacto">Contacto</a>
</nav>
</header>
.barra {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap; /* en pantallas estrechas, los enlaces bajan */
gap: 12px;
padding: 12px 16px;
}
.barra-enlaces { display: flex; gap: 16px; }
Tarjetas de igual altura con el botón abajo
Las tarjetas de una fila ya miden lo mismo gracias a stretch. El problema es que los botones quedan a distintas alturas si los textos son de distinta longitud. Se arregla convirtiendo cada tarjeta en una columna flex:
.tarjetas { display: flex; flex-wrap: wrap; gap: 16px; }
.tarjeta {
flex: 1 1 220px;
display: flex;
flex-direction: column;
}
.tarjeta .boton { margin-top: auto; } /* siempre pegado abajo */
Pie de página pegado abajo
Si una página tiene poco contenido, el pie queda flotando a media pantalla. Solución:
body {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
}
main { flex: 1; } /* el contenido ocupa todo el alto sobrante */
Errores frecuentes
- Poner
display: flexen el hijo en vez de en el padre. Flexbox organiza a los hijos del elemento que lo lleva. - Confundir los ejes con
flex-direction: column. En columna,justify-contentes vertical yalign-itemshorizontal. - Usar
align-contentcon una sola línea. No hará nada; lo que buscas esalign-items. - Esperar que
stretchfuncione con altura fija. Si el hijo tieneheight, no se estira. - Texto largo que desborda. Un hijo flex no encoge por debajo del ancho de su palabra más larga, porque su
min-widthesauto. Si una URL larga rompe el diseño, añademin-width: 0al hijo. - Olvidar
flex-wrapy ver cómo todo se aplasta en el móvil.
¿Flexbox o Grid?
Flexbox piensa en una dimensión (una fila o una columna) y deja que el contenido decida el tamaño. Grid piensa en dos (filas y columnas a la vez) y tú decides la rejilla. Para un menú, una barra o un grupo de botones, Flexbox. Para el esqueleto de una página o una galería con columnas alineadas, Grid. Muchas webs usan los dos a la vez.
Resumen
| Propiedad | Dónde | Para qué |
|---|---|---|
display: flex | Padre | Activa Flexbox |
flex-direction | Padre | Fila o columna (eje principal) |
flex-wrap / flex-flow | Padre | Permitir varias líneas |
justify-content | Padre | Alinear en el eje principal |
align-items | Padre | Alinear en el eje cruzado |
align-content | Padre | Repartir varias líneas |
gap | Padre | Espacio entre hijos |
flex (grow shrink basis) | Hijo | Cuánto crece, encoge y su tamaño base |
align-self | Hijo | Alineación propia en el eje cruzado |
order | Hijo | Orden visual |
margin: auto | Hijo | Empujar al extremo |
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.