wandres.dev
FLEXBOX EN LA PRÁCTICA · Alineación, orden y patrones

Alineación completa: justify, align y la caja de alineación

Las cuatro propiedades de alineación, la diferencia entre distribuir y posicionar, la alineación por línea base, gap frente a los márgenes y para qué sirven las palabras clave safe y unsafe.

⏱ 19 min

La alineación en flexbox se reparte entre cuatro propiedades que actúan sobre dos ejes y dos ámbitos distintos: el conjunto de elementos o cada elemento por separado. La confusión habitual no viene de la sintaxis sino de no tener claro sobre qué actúa cada una. Una vez que colocas las cuatro en la matriz correcta, la elección deja de tener ninguna dificultad.

🎯 Al terminar esta lección sabrás
  • Situar justify-content, align-items, align-self y align-content en su eje y ámbito.
  • Distinguir la alineación posicional de la distributiva.
  • Alinear por línea base y saber cuándo eso es lo correcto.
  • Explicar el desbordamiento de datos y qué resuelven safe y unsafe.

La matriz de cuatro casillas

Las cuatro propiedades se organizan por dos preguntas: en qué eje actúas y sobre qué actúas.

eje principal eje transversal
el grupo entero justify-content align-content
cada elemento no existe align-items y align-self

La casilla vacía es informativa. No existe justify-items en flexbox porque en el eje principal los elementos ya se distribuyen unos tras otros: no hay una franja individual dentro de la cual alinear a cada uno. El equivalente funcional es margin: auto, que es el tema de la lección siguiente.

Y align-content solo hace algo cuando hay varias líneas, es decir, con flex-wrap: wrap y contenido que efectivamente se envuelve. Con una sola línea no tiene nada que distribuir. Es la causa de la mitad de los “align-content no funciona”.

.contenedor {
  display: flex;
  justify-content: center;   /* el grupo, en el eje principal */
  align-items: center;       /* cada elemento, en el transversal */
}
.destacado {
  align-self: flex-end;      /* este elemento, sobrescribiendo align-items */
}

Posicional frente a distributivo

Los valores de justify-content y align-content se dividen en dos familias con comportamientos distintos.

Los posicionales colocan el bloque de elementos como un todo: flex-start, flex-end, center, start, end. Todo el espacio sobrante queda a un lado, al otro, o repartido en los extremos.

Los distributivos reparten el espacio entre los elementos: space-between deja los extremos pegados y reparte los huecos interiores; space-around da a cada elemento el mismo espacio a cada lado, con lo que los huecos interiores son el doble que los exteriores; space-evenly iguala todos los huecos, interiores y exteriores.

/* Tres elementos en 600px, cada uno de 100px, sobran 300px */
.entre    { justify-content: space-between; }  /* 0 - 150 - 150 - 0 */
.alrededor{ justify-content: space-around; }   /* 50 - 100 - 100 - 50 */
.uniforme { justify-content: space-evenly; }   /* 75 - 75 - 75 - 75 */

Hay un valor más, stretch, que en justify-content no hace nada en flexbox —los elementos ya tienen flex-grow para eso— pero en align-content sí estira las líneas para llenar el contenedor. Es el valor inicial de align-content.

💡
Los valores start y end frente a flex-start y flex-end

flex-start y flex-end son de flexbox y se refieren al inicio y final del eje flex, teniendo en cuenta row-reverse. start y end vienen de Box Alignment y se refieren al inicio y final del eje de escritura, ignorando el reverse. En un contenedor row-reverse, justify-content: flex-start alinea a la derecha y justify-content: start a la izquierda. La diferencia solo se nota con reverse o con rtl.

Los valores de align-items

En el eje transversal, cada elemento se alinea dentro de la altura de su línea. Los valores importantes son cinco.

stretch, el inicial, estira el elemento para llenar la línea, siempre que su tamaño en ese eje sea auto. Un elemento con block-size definido no se estira. Este matiz explica por qué a veces align-items: stretch no hace nada.

flex-start y flex-end lo pegan a un extremo. center lo centra.

baseline alinea los elementos por la línea base de su primera línea de texto. Es el valor que hace que un icono y un texto de tamaños distintos queden ópticamente alineados, en lugar de centrados geométricamente. También existe last baseline, que usa la última línea.

/* Un icono de 24px junto a un texto de 32px: centrarlos geométricamente
   los deja desalineados a la vista. La línea base los cuadra. */
.encabezado {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
}

La alineación por línea base es la que más se infrautiliza y la que más diferencia produce en tipografía. La regla: si los elementos contienen texto de tamaños distintos y quieres que se lean como una sola línea, usa baseline. Si contienen bloques de contenido, usa center.

gap, safe y unsafe: los dos añadidos recientes

gap define el espacio entre elementos sin añadirlo por fuera del grupo. Antes de que existiera en flexbox —llegó a todos los motores en 2021— la única forma de separar elementos era con márgenes, y eso obligaba a anular el margen del primero o del último con :first-child, o a compensarlo con un margen negativo en el contenedor.

/* El patrón de 2018, con toda su fragilidad */
.lista > * { margin-inline-end: 1rem; }
.lista > *:last-child { margin-inline-end: 0; }

/* El patrón actual */
.lista { display: flex; gap: 1rem; }

La diferencia no es solo de líneas. gap participa en el cálculo del espacio libre del algoritmo de flex, con lo que los elementos se encogen sabiendo que el hueco existe. Un margen, en cambio, forma parte del tamaño exterior de cada elemento y se comporta distinto al envolver. Y con flex-wrap, gap separa también las líneas entre sí mediante row-gap, algo que con márgenes exigía duplicar la lógica.

/* Separación distinta entre columnas y entre filas */
.rejilla {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem 1rem;   /* row-gap 2rem, column-gap 1rem */
}

El segundo añadido reciente ataca un problema poco conocido y muy molesto: cuando centras algo que no cabe, el desbordamiento ocurre por los dos lados. Y en el eje de inicio, el contenido desbordado es inaccesible: no se puede llegar a él con scroll, porque los navegadores no permiten desplazarse hacia el inicio más allá del origen.

/* Si el contenido no cabe, se pierde por el lado de inicio */
.centrado { display: flex; justify-content: center; overflow: auto; }

Box Alignment definió dos modificadores para esto. safe center centra mientras quepa y cambia automáticamente a alineación de inicio cuando no cabe, garantizando que nada se pierda. unsafe center centra siempre, aceptando la pérdida.

/* Centra si cabe, alinea al inicio si no. Nunca se pierde contenido. */
.barra-scroll {
  display: flex;
  justify-content: safe center;
  overflow-x: auto;
}

Es especialmente relevante en barras de pestañas horizontales con scroll, en menús de navegación centrados y en cualquier contenedor con overflow cuyo contenido pueda crecer. Está disponible en los motores desde 2024 y es una de esas mejoras pequeñas que eliminan una categoría entera de bugs.

La alineación es la única parte de CSS donde el contenedor manda sobre el hijo, y eso tiene un precio

Hay una asimetría de poder en el modelo de caja que casi nunca se enuncia: en flujo normal, cada caja decide sus propios márgenes, y el padre solo decide el espacio disponible. Con la alineación, esa relación se invierte: align-items en el contenedor decide qué hace cada hijo, y el hijo solo puede protestar con align-self. Es la única familia de propiedades donde el padre tiene autoridad directa sobre la geometría del hijo. Esa inversión es tremendamente cómoda —por fin puedes centrar verticalmente sin conocer alturas— y trae consigo un problema de arquitectura que aparece en cuanto trabajas en un equipo: tus componentes dejan de ser autónomos. Un botón que se ve perfecto en aislamiento se estira raro dentro de un contenedor flex ajeno que tiene align-items: stretch, y el autor del botón no tiene forma de anticiparlo. El patrón defensivo que acaban adoptando todas las librerías serias es el mismo: envuelve tu componente en un contenedor propio que absorba lo que le hagan desde fuera, y nunca dependas del align-items heredado para la geometría interna. Es la versión de layout del principio de encapsulación, y como toda encapsulación cuesta un nodo extra en el DOM. Que ese coste merezca la pena depende de si el componente lo vas a colocar tú en un sitio o lo van a colocar treinta personas en sitios que no puedes ver.