Centrado horizontal y vertical «like a boss»

Menos de 24h y ya escribo otra guía. Me parece justo, llevaba un mes sin publicar nada; aunque he de admitir que me gusta esta «serie», si es que se le puede llamar así. Ahora toca el turno de los centrados, algo tan simple y que a su vez mucha gente implementa mal. Antes de empezar, vamos a situarnos. Cuando nos referimos a centrar tenemos que saber que existen dos formas de centrar las cosas: horizontal (con respecto al eje de las X) y vertical (con respecto al eje de las Y). A parte de esto, existen dos posibilidades para centrar dependiendo de si el elemento se comporta como un bloque o como texto. Es decir, tenemos 22 posibilidades para centrar.

Centrado horizontal

<center> o align="center", los métodos del demonio

La etiqueta <center> es una etiqueta obsoleta, al mismo tiempo que añadir la propiedad align="center" al padre del texto que quieres centrar.
  • <center> lo que hace es crear un elemento de tipo block al rededor de todo su contenido. Dicho elemento lo que hacía (cuando no estaba obsoleto) era centrar los elementos de tipo bloque que fuesen hijos, además de todo el texto.
  • align="center" sigue una lógica similar a la anterior. Aunque su uso fuese recomendado desde que se desaconsejó el uso de <center>, a día de hoy están ambas al mismo nivel de maldad.
Como ya dije en la publicación anterior, el HTML debe ser siempre sintáctico y tener un significado para el contenido, no para su presentación. Por ello fueron rápidamente descartados.

Texto

Una vez mencionado los métodos malos para centrar de forma horizontal, vamos a analizar las alternativas correctas (o mejor dicho, alternativa) de centrar texto con CSS y, posteriormente, las de los bloques.

text-align: center, el método para dominarlos a todos

Este es el único método que deberías utilizar para centrar textos (por no decir el único —viable—, je je). Por lo general, se aplica la propiedad al padre y automáticamente centra el texto, dejando sin modificar las dimensiones del contenido. Hay que destacar que esta propiedad se hereda en los hijos sucesivos a no ser que se diga lo contrario en uno de ellos y que las dimensiones de ninguno de estos se ven modificados (si fuesen bloques). Vamos a verlo mejor con un poco de código:
<div id="contenedor">
    <div id="contenido">
        <div id="inception">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea iste ipsa ex sapiente rerum, alias unde atque debitis soluta iure quam exercitationem mollitia magni similique, dolores molestias ut, voluptates at?</div>
    </div>
</div>
#contenedor {
    width: 700px;
    background-color: #4DC6BB;
    padding: 20px;
}

#contenido {
    width: 400px;
    background-color: #FAC44D;
    padding: 20px;
}

#inception {
    width: 200px;
    padding: 20px;
    display: inline-block;
    background-color: #FF6227;
}
Vamos ahora a aplicar la propiedad text-align: center; a #contenedor:
En la anterior captura podemos ver como el texto contenido en #inception está ahora centrado, como era de esperar. Aunque la propiedad está colocada en su «abuelo», como no hemos especificado lo contrario en ninguno de los descendientes de éste, la propiedad se hereda hasta llegar a #inception. Además, el propio #inception también está centrado. ¿Cómo es esto posible? Flerex, ¿no habías dicho que solo servía para texto? Sí, es cierto. Pero si te fijas incluí también la propiedad display: inline-block a #inception, que lo hace comportarse como un bloque que sigue el flujo del baseline del texto (es decir, se comporta como si fuese texto aunque conserva algunas características de los bloques). He hecho esto para haceros ver que sí que se pueden centrar bloques con este método, pero no es por norma general el método que se debería usar. Solo en algunos casos específicos convertir un elemento bloque a inline-block puede resultar útil. De lo contrario, podemos llegar a problemas con la alineación al baseline como comenté ayer en «Columnas “like a boss”».

Bloques

En el ejemplo anterior ya hemos visto una posibilidad de cómo centrar un bloque, aunque no es un buen método. La manera ideal de centrar un bloque está en las próximas líneas, pero antes vamos a decir cómo no se debe centrar un bloque.

position, el método de los matados

Tanto position con valor relative como absolute son métodos horribles para centrar. Requieren de que saquemos la calculadora para calcular cuántos pixeles tenemos que separar nuestro elemento desde el borde izquierdo, teniendo en cuenta su propio ancho. Muy engorroso. Tan engorroso que ni le dedicaré mucho tiempo. El método con position: absolute es el siguiente:
  1. Ponemos position: relative en el padre para que los hijos lo tomen como referencia.
  2. Al hijo le añadimos la propiedad position: absolute (recuerda que desde el momento que pongas ese valor, el elemento no existirá para el padre a la hora de calcular las dimensiones del mismo, por lo que es posible que perdieses la altura y anchura si el elemento influyese en ellas).
  3. Ahora tenemos que mover el elemento. Vamos a pensarlo con respecto a la izquierda, aunque sería lo mismo si se hiciese con respecto a la derecha. Ponemos la propiedad left: 50% para mover nuestro elemento la cantidad exacta de la mitad del ancho del padre.
  4. Obviamente esto no estaría centrado, puesto que estaríamos moviendo nuestro elemento desde su esquina superior izquierda, no desde su propio centro. Para solucionarlo, tendríamos que reposicionar el elemento un poco más a la izquierda con exactamente la mitad de su ancho. Una forma de hacerlo sería margin-left: -{la mitad del ancho del elemento}px; o, si no quieres sacar la calculadora, transform: translateX(-50%).
El método con position: relative es aún más de locos puesto que habría que hacerlo todo con calculadora. Aún así solo funcionaría si el ancho del elemento fuese fijo (y no siempre es así). El método con absolute tampoco funcionaría con ancho variable si se usase el truco de margin (aunque sí funcionaría con transform).

margin-left: auto; margin-right: auto;, el método bueno

Sí señor. Esta es la forma en la que lo deberías estar haciendo. La propiedad margin con valor auto a ambos lados de un bloque lo centra perfectamente con respecto a su padre, sin más líos y sin ser tan engorroso como los anteriores trucos.
#contenido {
    margin-left: auto;
    margin-right: auto;
}
La propiedad funciona porque el valor auto al margen indica que debe aprovechar tanto espacio como tenga disponible. Si solo colocásemos el valor auto al lado izquierdo, se movería el bloque totalmente a la derecha. Al hacerlo tanto en babor como en estribor garantizamos que se utilice de forma equitativa el espacio a ambos lados. Con este método solo el elemento al que le aplicamos la propiedad se ve afectado, y esto es algo bueno, puesto que hace el CSS más predecible.

Flexbox, el método «like a boss»

Sí, ya hemos llegado aquí. El momento de las guías de la serie «like a boss» donde predico sobre lo bueno que es flexbox. En este caso, flexbox va a aparecer varias veces en esta guía. Aunque quizás no merezca la pena en un principio utilizar flexbox para centrar de forma horizontal al tener efectos «secundarios» sobre cómo se comportan los elementos hijos, una vez que lo dominas a la perfección verás que será tu primera opción. La forma de centrar los elementos con flexbox es la siguiente:
  1. Aplicamos al padre del elemento o elementos que queremos centrar la propiedad display: flex;.
  2. Por defecto, flexbox hace que todos los hijos estén en una misma línea, el eje de las X. El comportamiento normal del flujo de los bloques es que uno se debe colocar bajo el otro. Para obviar esto en flexbox tenemos la propiedad y valor flex-direction: column;, que pasa a trabajar sobre el eje de las Y. Aunque no es exactamente lo mismo y sigue teniendo otros comportamientos ajenos al comportamiento normal de flujo (véase la documentación de flexbox para información más detallada), nos servirá para la mayor parte de los casos.
  3. Añade la propiedad align-items: center; si estás trabajando sobre el eje de las Y (es decir, si has añadido flex-direction: column; o justify-content: center; si no lo has añadido (por ejemplo, si solo tenías un elemento no sería necesario cambiar el eje de trabajo).
#contenedor {
    display: flex;
    flex-direction: column;
    align-items: center;
}
Aún así, flexbox sigue teniendo comportamientos especiales que hay que tener en cuenta. Por ejemplo, los elementos siempre ocuparán todo el espacio del eje alternativo (el eje con el que no estés trabajando, las Y si no has puesto flex-direction: column; y viceversa). Todos estos comportamientos dejan de ser un problema cuando se aprende flexbox, por lo que te invito a buscar en internet información sobre él para dominarlo a la perfección y aprovecharte de todas sus capacidades.

Centrado vertical

Aquí llega la gallina de los huevos de oro. Lo que seguramente estabas esperando. El centrado horizontal son juegos de niños y todo el mundo lo sabe. No hay que ser muy genio para saber que <center> es de malos desarrolladores y que margin es bastante straightforward. El problema que tiene el centrado vertical es que, mientras que por defecto el ancho de los bloques toman todo el espacio disponible, el alto solo toma el espacio necesario para mostrar el contenido (¿¿¿POR QUÉ??? No me lo preguntes a mí. Yo no soy 100tifiko. Venga, ya está con los memes del 2000). Utilizar un margin-top: auto y margin-bottom: auto simplemente haría que dichos valores se calculasen a cero. En realidad no se puede porque, en resumen, provocaría que el navegador entrase en un bucle infinito a la hora de calcular las alturas. Entonces… si no se puede, ¿cómo se centra de forma vertical?

Texto

line-height, el método masoca

Existe un método bastante curioso para centrar el texto que implicaría poner la propiedad line-height a exactamente el mismo valor que la altura del elemento. Obviamente esto solo da problemas, puesto que no se podría tener varias líneas de texto, pero funciona en cosas simples como por ejemplo una navegación. Si no lo he dejado ya a entender, este no es un buen método.

vertical-align: middle;, para los que son unos morbosos de las tablas

Otro truco bastante original es utilizar la propiedad vertical-alignment: middle;. Como supongo que ya sabrás, esta propiedad está ideada para trabajar con elementos inline y table-cell. Los elementos inline los vamos a descartar porque es una tontería trabajar con ellos. El método que vamos a analizar es el siguiente:
  1. Convierte tu elemento en un table-cell con display: table-cell;.
  2. Por limitaciones, tiene que haber un padre con display: table;
  3. Añade a la celda, la propiedad vertical-alignment: middle;
  4. Añade al padre width: 100%; para que se comporte como un bloque.
¿Engorroso, verdad? ¿Quién ha dicho que el CSS era sencillo?

flexbox, again

Este es el método para centrar verticalmente textos que me he dado cuenta que es el mejor. Como el método es igual que para centrar bloques, lo dejaré para el final, para la guinda del pastel.

Bloques

position: relative; y position: absolute;, the return of the matados

Si no te habías dado cuenta cuando ya hablé de los position en el centrado vertical, te voy a comentar que también se podría haber utilizado el mismo método pero en vez de trabajar con la izquierda, trabajando con la parte superior (incluso con la inferior). Tan solo tendrías que hacer los mismo pasos, pero poner top: 50%; y compensar el tamaño del elemento restándolo con margin-top: -{la mitad del elemento}px; o el equivalente CSS3 transform: translateY(-50%).

flexbox. (Not this again ?)

¡Volvemos a los ejes! Yaaay. Centrar de forma vertical con flexbox es absurdamente simple. Tan solo tenemos que volver a tener en cuenta que el comportamiento de los elementos cambia con flexbox aplicado, por lo que tenemos que ser conscientes de ello. Como ya he explicado así un poco por encima cómo funcionan los ejes, voy a suponer que vamos a alinear elementos que están sobre el eje de las X (el por defecto). Para centrar un elemento, ya sea de tipo en línea o bloque, tan solo tenemos que añadir display: flex a su padre, junto con la propiedad align-items: center; (justo lo contrario que antes). Automáticamente todos los hijos serán centrados verticalmente. Y ya está.
Hasta aquí todos los métodos que se me han ocurrido para centrar. Es posible que se me haya escapado alguno que otro, pero básicamente la intención de esta guía era aclarar qué métodos son los correctos. Recuerda:
  • Para centrar de forma horizontal, utiliza el valor auto en los margin horizontales o, si sabes flexbox y lo estabas utilizando porque está relacionado con tu estructura, utilízalo directamente.
  • Para centrar de forma vertical, la mejor alternativa es directamente utilizar flexbox.
Como ya te habrás podido dar cuenta leyendo esta guía, no es el objetivo de la misma el enseñar a utilizar la tecnología Flexbox. Actualmente hay infinidad de guías por internet que lo explican mejor de lo que yo podría. Si estás interesado en aprender todas sus posibilidades, no dudes en hacer una rápida búsqueda en tu buscador favorito.