en CSS

Alinear texto verticalmente con css vertical-align y flexbox

Vertical-align es el método CSS para alinear verticalmente texto. Seguro que habréis leído sobre múltiples técnicas con floatposition: absolutepadding y/o margin negativos y otras filigranas donde tienes que ir ajustando los valores para apañar la posición… Pero si vas a trabajar con proyectos donde tanto el tamaño de los bloques de texto como el número de párrafos es variable, o si tiene requerimientos responsive… todos esos métodos para alinear el texto verticalmente son inútiles por su rigidez.

Quizás hayáis leído también sobre los métodos para alinear de flexbox. Tranquilos, los vamos a ver en este artículo. Sin embargo prefiero comenzar mostrándoos cómo usar la propiedad vertical-align de CSS para solventar los problemas de alineado vertical más comunes.

CSS vertical-align

Como decía al inicio, vertical-align es la propiedad CSS que nos permite alinear texto o contenido tabular verticalmente y lo hace de dos formas distintas.

Alinear texto o elementos dentro de una celda

Una de las propiedades por defecto de las tablas es que sus celdas centran verticalmente cualquier nodo hijo, ya sea sólo texto, un bloque (p, div…) o un elemento en línea (span, a…). Usando vertical-align podemos modificar la posición a top, middle o bottom, como seguramente ya sabréis, por lo tanto, las tablas no suponen un problema a la hora de alinear verticalmente su contenido. Sin embargo, los elementos de bloque son harina de otro costal, pues carecen totalmente de esa propiedad. Pero no todo está perdido, CSS nos brinda la oportunidad de usar las propiedades de las tablas en otros elementos mediante el atributo display: table-cell.

Esta técnica nos permite mantener la semántica del marcado HTML sin tener que recurrir al uso del tag <table>. Una vez convertido el elemento padre en una celda de tabla ya podremos alinear cualquier elemento dentro de ella.

See the Pen vvPgPj by Jesuke (@jesuke) on CodePen.

Recordad que si usáis este método en dos elementos contiguos, se alinearán en horizontal ya que son celdas.

Alinear texto y otros elementos inline entre sí

La segunda forma en que vertical-align nos permite centrar o alinear elementos html es cuando son de tipo inline. Por ejemplo, un texto seguido de un botón o una imagen, o dos elementos inline con diferentes line-height. También nos permite crear subíndices y superíndices como 1er o n10, pero mejor centrarnos en solucionar nuestro problema de centrado vertical.

En ocasiones necesitamos alinear elementos como bloques o tablas sin perder sus propiedades naturales. Para esas ocasiones nos aprovecharemos de nuevo de la propiedad display de CSS y los valores inline-block, inline-table e incluso inline-flex. Los textos u otros elementos contenidos dentro de ellos les darán distintas alturas y sólo tendremos que aplicar vertical-align: middle  para que queden perfectamente centrados en vertical, pero a diferencia de las tablas, en lugar de aplicarlo al contenedor, aplicaremos la propiedad a uno o a todos ellos, según la necesidad.

Pero esto sólo alineará los elementos entre sí. ¿Qué he de hacer si quiero que además se centren en vertical respecto a su contenedor y no me interesa usar display: table-cell? Pues necesitamos otro elemento que mida el 100% de la altura del contenedor. Nooo, no os voy a sugerir que añadáis HTML. La técnica es mucho más respetuosa con la semántica y las buenas prácticas y por supuesto sencilla.

El método :before + inline-block

Esta técnica os permitirá centrar el contenido, tanto si es un elemento como varios, usando elemento de tipo inline dentro de su contenedor. También os permite centrar texto puro, siempre que éste sólo ocupe una línea, de otro modo saltaría fuera de su contenedor al cambiar de línea. Por lo que es siempre mejor englobarlo dentro de un elemento. Esta es la técnica:

.container {
  white-space: nowrap; /*evitamos que los elementos rompan en dos líneas*/
}
.container:before {
  content:"";
  display: inline-block; /*Creamos un pseudo-elemento :before de tipo inline-block*/
  height: 100%; /*Hacemos que mida el 100% de la altura del contenedor*/
  vertical-align: middle; /*Lo centramos con el/los elemento/s hermanos*/
}
.element{
  display: inline-block; /*hacemos que nuestro elemento a centrar también sea inline-block*/
  vertical-align: midddle; /*Lo centramos también verticalmente respecto de su pseudo hermano*/
  white-space: normal; /*Reiniciamos este valor para que sus hijos puedan romper las líneas*/
}

Y ahora me preguntaréis «¿y qué pasa si a veces tengo uno y otras veces varios y en esos casos no quiero que se centren verticalmente?». Pues sencillo:

Actualización: me vine un poco arriba con lo de sencillo. En realidad es un caso bastante particular y necesita algo más de aclaración. Añado un poco de código al ejemplo y dejo un acceso a un CODEPEN en los comentarios como respuesta a @Gabriel Villalba.
En esta técnica, el :before no se crea como hijo de .container, sino de .element y éste debe medir el 100% del padre. Además se combina con text-overflow: ellipsis; y white-space: nowrap;

HTML
<div class="container">
  <div class="element">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
</div>

CSS
.parentElement {
   height: 48px; /*Cualquier altura superior al alto de línea para poder ver el efecto*/
}
.element {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.element:only-child {
  height: 100%;
}
.element:only-child:before { /*Sólo si es hijo único creamos el elemento alineador*/
  content: "";
  display: inline-block; /*Para que height: 100%; tenga efecto*/
  height: 100%;
  vertical-align: middle;
}

Si hay más de un elemento ni se creará el pseudoelemento :before. Por supuesto la técnica admite múltiples variantes de selectores.

See the Pen Align middle with inline-block :before by Jesuke (@jesuke) on CodePen.

Nota: para el DOM el texto es una entidad inline en sí misma aunque no esté comprendida dentro de un elemento de tipo inline como pueda ser <span>, y es considerada como un hijo del elemento que lo contiene. De ahí que <p></p> pueda seleccionarse con p:empty mientras que <p>texto</p> no, a pesar de no contener otros elementos html.

Flexbox align-content, align-items and align-self

Ante todo, decir que en la actualidad casi la totalidad de navegadores soportan flexbox, con la excepción por supuesto de IE que sólo lo soporta parcialmente a partir de su versión 10 y con muchas inconsistencias. Con lo que, ahora que MS se ha pasado a Webkit, con suerte cuando estéis leyendo esto, ya sea un método compatible con todos los navegadores.

Dicho esto, lo primero que tenéis que saber, es que flexbox con toda seguridad pasará en un futuro próximo a solventar muchos casos que ahora se resuelven con los elementos inline que, de por sí ya es raro verlos, e incluso sustituirá las técnicas con display: table-cell. La razón es que nos permite una flexibilidad similar a la de las tablas en cuanto a la adaptación a su contenido se refiere —flexibilidad que los elementos inline no tienen—, pero nos otorga mucho más control sobre los elementos a la vez que sortea la rigidez de las filas de tabla (<tr>), como hacen también las técnicas con elementos inline.

Actualización 03/04/2022 ↓

Una cuestión importante que hemos de tener en cuenta al trabajar con flexbox es que el efecto de sus atributos de alineación dependerá del valor de su flex-flow, es decir de su flex-wrap y sobre todo de su flex-direction.

flex-flow: column|row wrap|nowrap; /*valores de flex-direction y flex-wrap respectivamente*/

Entonces, partiendo de sus valores por defecto flex-flow: row wrap; podemos hacer comparaciones más claras con vertical-align.

Actualización 03/04/2022 ↑

Align-content

Permite alinear el contenido tal como hace vertical-align en las tablas pero aporta nuevos valores que la hacen más flexible.

Flex-start: equivalente a vertical-align: top.

Flex-end: equivalente a vertical-align: bottom.

Center: equivalente a vertical-align: middle.

Stretch: el contenido se estira para ocupar todo el espacio disponible verticalmente. Es el valor por defecto.

Space-around: el espacio vacío se distribuye equitativamente entre líneas de elementos.

Space-between: igual que el anterior pero eliminando los espacios superior e inferior .

*[movido a cambio 1 ↓]

Align-items

Es el equivalente a vertical-align aplicado a elementos inline pero, al igual que align-content, se aplica al padre y además aporta el valor baseline con idéntico resultado que vertical-align. Comparte con align-content los valores flex-start, flex-end, center y stretch con similares resultados pero aplicados a las filas o columnas de elementos dependiendo de si usamos flex-direction: row/column respectivamente.

Align-self no es más que una opción para que los elementos flex se puedan escabullir de la alineación global y sus valores son los mismos que los de align-items. Con vertical-align también podemos jugar con la alineación particular de los elementos, aunque los resultados pueden ser ligeramente diferentes.

Align-items alinea los elementos entre sí y además dentro de su contenedor, pero sólo si tenemos una única línea, caso en el que align-content no funcionará. Por esta razón lo ideal es combinar ambas propiedades.

.flexAligner { /*Combinamos ambos métodos*/
  display: flex;
  align-content: center;
  align-items: center;
}

See the Pen
CSS vertical align with flexbox test
by Daniel Abril (@elcssar)
on CodePen.

Flex-direction: column

*[cambio 1] La diferencia principal de las dos propiedades anteriores con vertical-align es que podemos combinarlas con flex-direction para invertir la alineación o rotarla junto con el contenido. (03/04/2022 →) En ese caso el efecto de align-content y align-items ya no sería vertical sino, horizontal. Por lo que si quisiésemos alinear verticalmente el contenido sería necesario usar la propiedad justify-content que para flex-direction: row, centraría el contenido en horizontal.

Mientras align-content alinea el contenido respecto del padre, align-items, lo hace entre los contenidos permitiéndonos mayor flexibilidad en la disposición de los elementos. Podemos ver un ejemplo:

.flexAligner {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-content: center;
  align-items: flex-end;
}

See the Pen
CSS vertical align with flexbox
by Jesuke (@jesuke)
on CodePen.

Los otros métodos. Centrando con padding, margin y con line-height

Antes de que cerréis la ventana, NO OS VOY A EXPLICAR LOS MÉTODOS DE SIEMPRE. Nada de padding negativos ni rollos raros.

Centrado vertical con padding y line-height

Cuando diseño, normalmente lo hago con módulos que luego aplico al CSS. Son medidas que hacen que todo el conjunto cuadre y tenga coherencia. Trabajar así me facilita mucho la vida a la hora de maquetar puesto que si quiero que un elemento que debe ocupar una única línea esté centrado sólo tengo que aplicar mi módulo al line-height. Si ese elemento tiene márgenes internos (padding), entonces se los resto al módulo principal. Esto lo puedo hacer tanto directamente con SCSS:

line-height: $module - $spacing*2;

como mediante calc():

line-height: calc(#{$module} - #{$spacing}*2);

Es un método sencillo que al trabajar con variables seguirá funcionando aunque cambies los valores. Por no hablar de que lo puedes reutilizar en múltiples proyectos si te acostumbras a trabajar así.

Centrado vertical con margin

En otras ocasiones no será una simple línea de texto, sino un bloque con múltiples contenidos como por ejemplo un formulario de login al inicio de una aplicación. Para estos casos no será necesario usar position absolute como por ejemplo se emplea en las modales, sino que será suficiente con margin:

margin-top: calc(50vh - #{$formHeight}/2);

Habréis notado que he usado vh en vez de %; ésto es debido a que un margin porcentual hace referencia al width, incluso si es margin-top o margin-bottom. Lo mismo ocurre con los padding. El uso de vh también nos indica que el centrado será siempre respecto al viewport. Por lo que es una técnica sencilla pero limitada a ciertos usos.

Otro factor importante es que, para que no se produzca overflow debido al margin, el padre deberá tener un padding o un borde que fuercen a los márgenes del elemento que queremos centrar a quedarse dentro. Por esta razón deberemos o bien restarlos del height del padre o usar box-sizing: border-box;

See the Pen
Untitled
by Daniel Abril (@elcssar)
on CodePen.

Como veréis estos dos últimos métodos están pensados para centrar contenido del que conocemos su altura. Aunque podríamos usar el método :before + inline-block, no olvidemos que los pseudoelementos modifican el DOM, razón por la cual pueden generar problemas de rendimiento si se abusa.

Sumario

Tenemos pues las siguientes posibilidades a la hora de centrar verticalmente elementos de los que desconocemos su altura usando vertical-align:

  1. Aplicándolo a una celda de tabla o bien convirtiendo otro elemento en celda mediante display: table-cell.
  2. Usando elementos inline y aplicando la técnica :before + inline-block. Recomendado para alinear bloques de texto y otros conjuntos de elementos.
  3. Usando flexbox. Recomendado para diseños responsive en los que necesitamos rellenar todo el espacio disponible con una gran cantidad de elementos.

Por otro lado tenemos 3 técnicas para centrar verticalmente elementos de los que sí conocemos su altura.

  1. Línea de texto única aplicando un alto de línea igual a la altura de su contenedor.
  2. Línea de texto centrada en un espacio mayor a su contenido calculando sus márgenes interiores y restándoles el alto de línea.
  3. Elementos más complejos como un formulario de login, calculando sus márgenes verticales para centrarlos en un contenedor mayor a él aplicando calc() a la propiedad margin.

¿Cuál es el mejor método css para centrar verticalmente?

Pues aquel que se ajuste mejor a vuestras necesidades en cada situación. Cada uno implica unas ventajas y unas desventajas. De los 3 métodos que permiten centrar contenido verticalmente sin conocer su altura, display: table-cell es el más sencillo y puede ser muy útil si estás montando un sistema de columnas de igual altura. Pero no olvidemos que es una celda y se comportará como tal respecto de su contenido.

Por otro lado flexbox te permite múltiples configuraciones y no require mucho código, aunque sí que es algo más complejo de comprender, especialmente cuando comenzamos a conjugar todos sus parámetros.

Con el método :before + inline-block sin embargo, una única configuración os valdrá para todos los casos y además os libráis del flexbox, haciendo mucho más sencillo centrar también horizontalmente. Si bien es cierto que si queremos centrar un conjunto de elementos dispuestos en columna, deberemos sí o sí envolverlos en otro que se alinee con el pseudoelemento :before.

Para que os sea más fácil decidir os dejo estos dos enlaces comparativos:

Flexbox vs :before + inline-block

Como siempre, espero que os haya sido útil el artículo.

Saludos y no dudéis en dejar vuestros comentarios.

Escribe un comentario

Comentario

    • Gracias Jordan, un placer contribuir a la comunidad.
      Escribí el artículo porque me pareció que la mayoría de las soluciones al problema del alineado vertical que se podían encontrar en la red estaban obsoletas, y pensé en contribuir con algunas más modernas.

  1. Hola. Espero estés bien.
    Pude analizar la actualización y hacer varias pruebas. Ahora me queda claro el método de alineación con :only-child:before. Gracias por tu respuesta aclaratoria y el snippet.
    Saludos y éxitos.

    Nota:
    Para que funcione el ejemplo hay que agregar la altura del contenedor y la propiedad content:″″; en el alineador .element:only-child:before.

    • Correcto,
      como cualquier :before necesita un content y, para poder ver el efecto, necesitaremos que el padre tenga una altura mayor a la del line-height, tal como aparece en el CODEPEN. Lo he añadido también al artículo ya que no es tan obvio como pensaba.
      Gracias Gabriel y suerte con tus proyectos.

  2. Me gusta mucho el artículo. Estoy estudiándolo y poniendo a prueba cada método con la intención de incorporarlos a mis recursos. Muchas gracias por compartir la información.

    Tengo una duda.
    En el método :before + inline-block cuando intento aplicar la técnica del único elemento para alinear con .element:only-child:before no logro que se comporte de la forma esperada. He intentado copiando exactamente el código de ejemplo y pegando la regla tal cual, pero ni así.

    Puedes ilustrarme con algún ejemplo en el que la técnica se esté aplicando correctamente.

    Gracias de antemano.

    • Muchas gracias Gabriel,
      te pido disculpas por el error en el artículo. Nunca te va a funcionar porque el :before debería ser hermano de .element, es decir debería estar asociado a .container siguiendo el ejemplo que puse, pero para esta técnica lo que alineamos es el texto dentro de .element, no el propio element. He añadido una actualización y además te he hecho un CODEPEN.
      Espero que ahora te funcione 😉

  3. En los tiempos en que las webs se hacian con tablas, esto era sencillo gracias a la propiedad vertical-align de las mismas; pero con la aparicion de las capas (Divs) y desaparicion de las tablas esta propiedad murio.

    • Gracias por el comentario.
      Digamos @I_Love_PvM que vertical-align quedó en «coma» durante un tiempo porque no se conocía su aplicación más allá de centrar el contenido dentro de las celdas, de ahí que se pueda pensar que es una propiedad exclusiva de las tablas; pero como explico en el artículo es un atributo compartido con todos los tipos de elementos inline. Otra razón de ese «coma» inducido por el tableless, es que aún tardó un tiempo en popularizarse el uso de display: table y display: table-cell que lo sacaron del letargo hasta la llegada de la era del flexbox, que probablemente lo vuelva a enchufar al respirador asistido. Pero lo cierto es que no siempre queremos un layout flex, pero lo acabamos usando sólo porque nos es sencillo usarlo para centrar verticalmente obligándonos a controlar todas sus propiedades para evitar que se comporte según su naturaleza flexible.
      Mi recomendación es «usad la técnica que se adecúe mejor a las necesidades de cada caso» y para facilitaros la tarea escribí este post con todas las técnicas que uso yo habitualmente. Me haría muy feliz si alguno dejase un comentario con un enlace a su web donde ha usado alguna de las técnicas del artículo, en especial «:before + inline-block» que es mi vástago 😉

  4. Interesante fórmula °-° no la conocía. Probé un montón de métodos diferentes, pero al final me quedé con esta clase extremadamente sencilla y funcional:

    .centrartextoenDIV {display:flex; justify-content:center; align-items:center;}

    • Hola @MacGyver,
      gracias por tu comentario. Me alegra ver que el artículo aporta soluciones diferentes que os puedan servir en vuestros proyectos.
      Efectivamente, el sistema flexbox, una vez que lo entiendes, es de los más sintéticos y efectivos. Sin embargo, si lo usas con regularidad verás que en algunos casos puede ser algo traicionero. De ahí que os ofrezca diferentes métodos para que podáis cubrir distintas necesidades.