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 porque CSS nos brinda la oportunidad de usar las propiedades de las tablas 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:before {
  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*/
  white-space: nowrap; /*evitamos que los elementos rompan en dos líneas*/
}
.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:

.element:only-child:before { /*Sólo si es hijo único creamos el elemento alineador*/
  display: inline-block;
  height: 100%;
  vertical-align: middle;
  white-space: nowrap;
}

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.

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 .

La diferencia principal con vertical-align es que podemos combinarlo con flex-direction para invertir la alineación o rotarla junto con el contenido.

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.

Para no dejaros sin un ejemplo:

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 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 padding

Aún trabajando con módulos, a veces el espacio está condicionado por la adaptación a la pantalla del dispositivo, por lo que no conoces la altura pero aún así necesitas que tu texto aparezca perfectamente centrado.

En este caso en lugar de modificar el line-height, lo utilizaremos para sacar los cálculos de la siguiente manera.

padding: calc(50% - #{$lineHeight}/2) $spacing;

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: calc(50% - #{$formHeight}/2) auto;

Como veréis estos tres ú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 más de un elemento, 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.

Deja tu comentario

Comentario