Saltar al contenido
flexbox.css · devschool

Flexbox

Lección 12 de 25 · 12 min de lectura · Actualizado el

En esta lección
  1. El contenedor y sus hijos
  2. Los dos ejes
  3. flex-direction: la dirección del eje principal
  4. flex-wrap y flex-flow: una línea o varias
  5. Alinear en el eje principal: justify-content
  6. Alinear en el eje cruzado: align-items
  7. align-content: cuando hay varias líneas
  8. align-self: un hijo diferente
  9. gap: separación entre hijos
  10. order: cambiar el orden visual
  11. Tamaño de los hijos: flex-grow, flex-shrink y flex-basis
  12. margin: auto: empujar elementos
  13. Patrones habituales
  14. Errores frecuentes
  15. ¿Flexbox o Grid?
  16. 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 contenedorVan en los hijos
display: flex, flex-direction, flex-wrap, flex-flowflex-grow, flex-shrink, flex-basis, flex
justify-content, align-items, align-content, gapalign-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-reverse y column-reverse solo 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 start y end en lugar de flex-start y flex-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:

ValorEquivale aQué hace
flex: initial0 1 autoEl valor por defecto: no crece, sí encoge
flex: 11 1 0%Todos los hijos con flex: 1 miden lo mismo
flex: auto1 1 autoCrece y encoge, partiendo del tamaño de su contenido
flex: none0 0 autoRígido: ni crece ni encoge
flex: 1 1 250px1 1 250pxParte 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: flex en 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-content es vertical y align-items horizontal.
  • Usar align-content con una sola línea. No hará nada; lo que buscas es align-items.
  • Esperar que stretch funcione con altura fija. Si el hijo tiene height, 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-width es auto. Si una URL larga rompe el diseño, añade min-width: 0 al hijo.
  • Olvidar flex-wrap y 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

PropiedadDóndePara qué
display: flexPadreActiva Flexbox
flex-directionPadreFila o columna (eje principal)
flex-wrap / flex-flowPadrePermitir varias líneas
justify-contentPadreAlinear en el eje principal
align-itemsPadreAlinear en el eje cruzado
align-contentPadreRepartir varias líneas
gapPadreEspacio entre hijos
flex (grow shrink basis)HijoCuánto crece, encoge y su tamaño base
align-selfHijoAlineación propia en el eje cruzado
orderHijoOrden visual
margin: autoHijoEmpujar al extremo

Pruébalo tú

Cambia el código y pulsa Ejecutar (o Ctrl + Enter).

flexbox.html
Resultado

Pon a prueba lo que has aprendido

[CSS] Con display: flex, ¿qué propiedad centra los hijos en horizontal (en una fila)?

[CSS] ¿Qué propiedad hace que los hijos de un contenedor flex bajen a otra línea si no caben?

[CSS] ¿Dónde se escribe display: flex?

[CSS] El contenedor mide 400px de ancho y no tiene gap ni padding. ¿Qué ancho tendrá .b?
.fila { display: flex; width: 400px; }
.a, .c { flex: 1; }
.b { flex: 2; }

[CSS] En una barra de navegación con display: flex, ¿qué hace margin-left: auto en el último enlace?
.nav { display: flex; gap: 16px; }
.nav .salir { margin-left: auto; }

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.