Diversas formas de alinear elementos de bloque en CSS2 y CSS3

Figura
Figura. Nueve posiciones de alineado con CSS2 y CSS3.

No hay ninguna propiedad específica para alinear elementos de bloque en CSS2. Se han venido usando diversas técnicas como la del valor auto para los márgenes horizontales que nos permite alinear en esa dirección. Otra forma es dar al elemento de bloque hijo el valor inline-block en su propiedad display. Así podemos dimensionarlo y obedecerá la alineación de elementos Inline que vimos en temas anteriores. Usando text-align para el bloque padre y vertical-align para el bloque hijo conseguimos, con ciertas limitaciones, alinear en ambas direcciones.

Una tercera forma de alinear con CSS2 es usando el valor table-cell para la propiedad display. El elemento de bloque se comportará como una celda de tabla, un elemento en CSS2 sobre el que se puede aplicar al mismo tiempo las propiedades text-align y vertical-align.

Lo anterior no deja de ser otra cosa que trucos para alinear elementos de bloque. Porque CSS3 finalmente deberá incorporar propiedades específicas para este cometido. La especificación en fase de borrador CSS Box Alignment Module Level 3, documento del 18 de diciembre de 2014, establece la alineación en varios modelos de caja: block layout, table layout, flex layout y grid layout. Por ahora sólo es soportado en el modelo de cajas flexibles (flex) y de rejilla (grid). Y aún así estos modelos también tienen muchas limitaciones de soporte, como puede verse actualmente en Caniuse flex y Caniuse grid.

Según la nueva especificación de Box Alignment las propiedades disponibles para alinear contenidos son las siguientes:

Propiedad delAlineaNombre propiedadAplica aNombre computado
blockflexgrid
ContenedorContenidojustify-content
align-content
HijoA sí mismojustify-self
align-self
ContenedorHijos (items)justify-items
align-items

En la tabla podemos ver en la columna de nombre computado aquellas que son soportadas en el navegador que esté usando. Pueden aparecer en color verde si se soporta, naranja si necesita un prefijo CSS y finalmente en rojo con las palabras no soportado. Esto se obtiene con JavaScript después de cargar la página consultando el estilo computado.

Tal como están las cosas tendremos que seguir usando los viejos trucos para alinear elementos de bloque. También podemos aprovechar que el modelo de cajas flexibles es soportado por los navegadores actuales para alinearlos. El siguiente ejemplo presenta un contenedor de bloque con fondo azul que alberga un único elemento de bloque con fondo verde:

<div id="conten-bloque">
    <div id="bloque">top-left</div>
</div>
    

Con las propiedades por defecto display: block y margin: 0 aparecerá en la esquina superior izquierda, pues el modelo de cajas clásico dispone los elementos de bloque apilándolos en líneas, ubicando el primero en esa esquina. El ejemplo alineará el elemento de bloque verde en las nueve posiciones representativas.

Usaremos tres técnicas de CSS2: márgenes (margin), altura de línea (line-height) y presentación de celda de tabla (table-cell). Usando la propiedad de CSS3 transform junto al posicionamiento absoluto tendremos la técnica por posicionamiento (position). Con la técnica de cajas flexibles haremos dos ejemplos de flex alignment. Por último también disponemos un ejemplo con box alignment que debería también aplicar a elementos de bloque, aunque por ahora no funciona.

Ejemplo: Alineando bloques en CSS2 y CSS3

Medidas
Bloque azul: auto × 16em
Bloque verde: 8em × 4em
CSS bloque azul
CSS bloque verde

Se dan las medidas ancho y alto de ambos contenedores así como el estilo importante de cada elemento para conseguir el adecuado alineamiento. Se señalan las propiedades que efectivamente entran en juego en la alineación de cada eje "X", "Y" o "XY" cuando afecta a las dos direcciones. Los valores de las propiedades son los que hemos adjudicado para cada posición de alineación.

En rojo y entre corchetes aparecen los valores computados cuando éstos no coinciden con los valores que se le dieron a cada propiedad. En algunos casos no es necesariamente un error, pues si damos un valor de 16em el navegador finalmente lo computará con 256px. Pero si puede servirnos para ver cuando el valor de una propiedad no fue adjudicado por el navegador.

Firefox no recupera el valor computado para márgenes con valor auto

Esto no afecta a la técnica de alineación, pero si hay que resaltarlo en relación con el valor computado de márgenes en Firefox. Podemos, por ejemplo alinear con margin: 6em auto para centrar en ambas direcciones. Si luego consultamos en Chrome 47 o IE 11 vemos el valor computado (obtenido con getComputedStyle) para los márgenes como 96px Npx 96px Npx, donde "N" será un número de píxeles en función del ancho de la ventana del navegador. Pero en Firefox 43 obtenemos 96px 0px 96px 0px, con lo que getComputedStyle() no está devolviendo el valor computado de los márgenes establecidos en el CSS con auto. Es decir, todo lo que esté como auto lo devuelve con valor computado cero. Hay un bug Mozilla abierto desde el año 2007 sobre esto que aún no ha sido resuelto.

El resto de este tema se dedica a desmenuzar cada uno de los ejemplos explicando las particularidades y exponiendo todo el CSS que afecta a ambos elementos.

Alineando elementos de bloque con margin en CSS2

La especificación CSS2.1, en un apartado sobre cómo calcular anchos y márgenes, expone que si los márgenes derechos e izquierdos margin-left y margin-right tienen ambos el valor auto la caja de bloque se centrará horizontalmente. De hecho el valor auto en esos márgenes nos permite también alinear a la derecha e izquierda. Hice un ejemplo interactivo en un tema acerca de posicionar con márgenes CSS, donde se muestran ejemplos de alineación horizontal y otros detalles de la propiedad margin que quizás le interese consultar.

En cualquier caso el ancho o alto del contenedor padre debe ser mayor que el respectivo del hijo, pues de otra forma no cabe el alineado en esa dirección. El valor auto para márgenes funciona en horizontal, pero es ineficaz en alineación vertical. Si queremos alinear en esa dirección el contenedor deberá tener una altura (height) determinada. En horizontal tenemos la ventaja que un contenedor de bloque con valor auto para width hará que ocupe todo el ancho disponible de su bloque de contención. Pero ese valor auto para height no produce el mismo efecto, pues la altura vendrá entonces determinada por su contenido. La alineación vertical con márgenes obliga entonces a usar dimensiones específicas para cada situación, algo engorroso como veremos en el siguiente ejemplo.

Como en todos los ejemplos tendremos un contenedor de bloque que alberga un elemento de bloque:

<div id="conten-bloque-margin">
    <div id="bloque-margin">DIV</div>
</div>
    

El contenedor azul tiene dimensiones de 150 × 150 píxeles. Su hijo es de 50 × 50 píxeles. En este ejemplo fijamos la alineación actuando sobre los márgenes del elemento de bloque hijo.

Ejemplo: Alineando bloques con margin en CSS2

DIV
CSS bloque azul
CSS bloque verde

Las propiedades señaladas son las representativas para ejecutar el ejemplo. Agregamos el contenido de texto del bloque verde para realizar algunas pruebas como veremos más abajo. Al modificar los valores de las propiedades se actualizará el código CSS que afecta a los dos elementos:

La alineación horizontal se consigue con los valores "0" para la izquierda, "V auto" para el centro y "V 0 0 auto" para la derecha. El valor "V" corresponde a los píxeles de la alineación vertical. Con el valor cero alineamos arriba. Para alinear al centro vertical dividimos entre dos la altura del contenedor azul y le restamos la mitad del verde, es decir V = 150/2 - 50/2 = 75-25 = 50 píxeles. Para alinear abajo usamos la altura del contenedor azul menos la del verde, V = 150 - 50 = 100 píxeles.

Con el valor auto alinea horizontalmente cualquier elemento sea cual sea su ancho. Pero se hace evidente la desventaja de esta técnica para alinear verticalmente, pues en cada caso deberán calcularse estos valores. De nada sirve usar porcentajes, también admitidos para márgenes verticales, pues se trata de porcentajes siempre sobre el ancho del elemento, aunque estemos declarandos esos márgenes verticales.

Usar medidas específicas verticales es también una desventaja cuando usamos un valor auto para la altura del elemento verde así como un valor normal para su altura de línea, valores por defecto para un elemento de bloque. En ese caso la alineación al centro vertical e inferior no es fácil de precisar porque no conocemos previamente cual será la altura final que tomará el elemento verde. Esa altura vendrá condicionada por la fuente y no tiene porque comportarse igual en todos los navegadores.

Para que el ejemplo funcione adecuadamente entonces necesitamos que el elemento verde tenga una altura declarada. Podemos hacerlo de tres formas. Con height: 50px más line-height: normal con correcta alineación, sobresaliendo el texto que no cabe a no ser que le demos valor distinto de visible a su propiedad overflow. Con height: auto y line-height: 50px también se alinea correctamente, pero si el texto ocupa más de una línea el elemento toma un alto más grande que lo necesario para los cálculos de los márgenes. La tercera opción es igualar altura y altura de línea. Es la aconsejable pero sólo podemos meter una línea de texto, pues las siguientes se saldrán del elemento verde.

Es importante también advertir el borde rojo superior del contenedor azul. Se incluye para evitar el comportamiento de CSS relacionado con el collapsamiento de márgenes verticales. Esto se explica en Collapsing margins de CSS2.1, exponiendo que los márgenes verticales de dos cajas contiguas collapsan en flujo normal, con lo que el margen que los separa no será la suma de los dos márgenes, sino el mayor de los dos. Si en el ejemplo usa una alineación vertical al centro o abajo y elimina el borde superior del contenedor azul verá que el margen dado al elemento verde pasa a formar parte del margen de todo el conjunto. Interponiendo un borde en el contenedor azul evitamos que los márgenes collapsen. En un ejemplo de uso final podríamos usar un color transparente para este borde.

En resumen, la alineación con márgenes es adecuada para dirección horizontal, pero dependiente de las dimensiones de ambos elementos en la alineación vertical. Una técnica fácil de usar no debería tener esta dependencia, como todas las siguientes que expondremos a continuación.

Alineando elementos de bloque con line-height en CSS2

Las propiedades de CSS2.1 text-align y vertical-align nos permiten alinear horizontal y verticalmente elementos en línea o Inline, como expusimos en temas anteriores. El truco está en convertir el elemento de bloque hijo en un inline-block. Así aún podemos dotarlo de dimensiones y obedecerán esas propiedades de alineación. En alineación horizontal tenemos los valores left, center y right para text-align.

Para la alineación vertical la cosa se complica un poco más. En el tema anterior vimos que vertical-align está intímamente ligado al concepto de altura de línea. Haciendo que el contenedor padre tenga una altura de línea igual a su alto con (line-height = height), podemos usar los valores top y bottom en vertical-align para alinear arriba y abajo respectivamente. Para alinear al centro no hay que usar el valor middle, sino el que viene por defecto baseline.

Tenemos ahora un HTML similar al anterior ejemplo, pero agregando un texto después del elemento de bloque interior.

<div id="conten-bloque-line-height">
    <div id="bloque-line-height">DIV</div>
    DIV
</div>
    

Ejemplo: Alineando bloques con line-height en CSS2

DIV
DIV
CSS bloque azul
CSS bloque verde

Todo el CSS para generar caja ejecución del ejemplo es el siguiente:

Dijimos antes que centramos verticalmente con el valor baseline para vertical-align. Si lo hacemos comprobaremos que los dos textos DIV aparecen alineados por su línea base, línea imaginaria donde se asientan los caracteres. Esto lo vimos en el tema anterior sobre la alineación vertical Inline. Igualar la altura de línea con el alto del contenedor azul supone que su caja de línea normal se ubique equidistante de los bordes superior e inferior.

Por otro lado como el elemento verde es inline-block su línea base será la de su última línea de texto. Si sólo hay una línea supone que ese hijo verde alineará su caja con el padre haciendo coincidir el baseline de la línea de texto con la del padre. Y esto centra el elemento verde necesariamente pues ambas cajas de líneas normales son iguales dado que usan igual fuente y tamaño.

Esa es la razón por la que usamos baseline en lugar de middle para alinear al centro vertical. A excepción de celdas de tabla, el valor middle no alinea al centro vertical sino al centro de la letra "x" minúscula, que no tienen porque coincidir necesariamente con el centro vertical del elemento.

Se puede producir una alineación al centro vertical incorrecta cuando tenemos varias líneas de texto en el hijo verde, por el tema comentado de que se alinea por la última línea de texto de ese elemento. Pero a diferencia de la técnica con márgenes, ahora un valor auto para height y un valor normal para line-height, los valores por defecto, producen una correcta alineación vertical.

Alineando elementos de bloque con table-cell en CSS2

La propiedad text-align aplica a elementos de bloque alineando sus hijos de línea (Inline). Por otro lado vertical-align se aplica sólo a elementos Inline. Hay una excepción a esta regla y es que para celdas de tabla podemos aplicar ambas propiedades a la celda. Alineará horizontalmente sus contenidos de línea y verticalmente cualquier contenido de línea o de bloque.

<div id="conten-bloque-table-cell">
    <div id="bloque-table-cell">DIV</div>
</div>
    

Ejemplo: Alineando bloques con table-cell en CSS2

DIV
CSS bloque azul
CSS bloque verde

La técnica se basa en dar el valor table-cell a la propiedad display del contenedor padre azul. Sigue siendo imprescindible que el hijo verde sea inline-block para poder aplicar text-align, aunque si sólo necesitamos vertical-align el hijo puede ser block.

Como en el ejemplo anterior, los valores auto para la altura y normal para la altura de línea del elemento verde producirán un correcto alineado vertical. La principal ventaja es que no estamos obligados a usar igual altura de línea y alto en el padre y, por otro lado, la misma fuente y/o tamaño en el hijo y el padre. Así la alineación vertical no está condicionada por fuentes, tamaños de fuentes o alturas de línea.

La alineación vertical con middle es ahora un verdadero centrado. Esto se expone en la especificación de CSS2.1, en su apartado Table height algorithms. Dice que el valor middle alinea el centro vertical [del contenido] de la celda con el de las filas que abarca. Ahora una celda solo abarca una única fila implícita, aunque no declarada. Por lo tanto la alineación se llevará a cabo por el centro de su contenido.

Otra posible ventaja es que ambas propiedades de alineado están en el padre. También que el hijo no tiene porque ser inline-block si sólo necesitamos alineación vertical, como comentamos antes. Una desventaja importante es que el contenedor padre ha de tener un ancho determinado si queremos alinear horizontalmente. Con valor auto el ancho de la celda se estrechará al mínimo para albergar su hijo verde.

Alineando elementos de bloque con position y transform en CSS3

Aplicando posicionamiento y traslación podemos también alinear un elemento de bloque dentro de un contenedor de bloque. El contenedor tendrá position con valor relative con el objeto de que su hijo, que tiene position con valor absolute, se posicione con respecto a su padre. La posición se marca con las propiedades left y top que son las distancias que separan la esquina superior izquierda del hijo con respecto a la misma esquina del padre. Para completar la alineación habría que trasladar el hijo hasta su centro.

<div id="conten-bloque-position">
    <div id="bloque-position">DIV</div>
</div>
    

Ejemplo: Alineando bloques con position y transform en CSS3

DIV
CSS bloque verde

La propiedad transform es de CSS3, pero los navegadores actuales la soportan ya sin prefijo.

Esta técnica de alineación no depende del conocimiento previo de las dimensiones de los elementos. Tanto el posicionamiento como la traslación podemos hacerla en porcentajes. Para left: X% y top: Y% los porcentajes son referidos al ancho y alto respectivamente del padre con posicionamiento relativo. Para transform: translate(X%, Y%) son referidos al ancho y alto del hijo. La traslación es negativa para hacer coincidir el punto de alineación, contrarrestando el posicionamiento de la esquina superior izquierda del hijo.

Como con la técnica anterior, no hay dependencias de fuentes o alturas de línea. Y ahora no necesitamos inline-block o medidas determinadas. Pero no dejamos de usar números para los porcentajes y además nos vemos obligados a posicionar elementos, algo que habría que probar en cada caso para ver si afecta al resto del diseño de otros elementos cercanos o relacionados.

Alineando elementos de bloque con flex alignment CSS3

Mientras la especificación Box Alignment que comentamos en el primer apartado de este tema no se implemente para elementos de bloque, podemos usar Flex Alignment para alinearlos. Con valor flex para la propiedad display hacemos que un contenedor de bloque se comporte como caja flexible. Así todos sus hijos podrán ser alineados horizontalmente con la propiedad justify-content y verticalmente con align-items y align-self.

<div id="conten-bloque-flex">
    <div id="bloque-flex">DIV</div>
</div>
    

Ejemplo: Alineando bloques con flex alignment en CSS3

DIV
CSS bloque azul
CSS bloque verde

Estas nuevas propiedades que se aplican al contenedor flex funcionan en los navegadores actuales sin prefijar. Para cajas flexibles tenemos los valores de alineación flex-start, center y flex-end. Con align-items en el padre damos la misma alineación vertical a todos los hijos. Luego con align-self podemos alinear individualmente cada hijo. Si damos valor auto a esta propiedad la alineación que se aplica es la del padre. En otro caso se sobrescribe la del padre aplicándose la del hijo.

Cuando se implemente Box Alignment para alinear elementos de bloque deberíamos usar align-content en lugar de align-items, así como start y end en lugar de los que empiezan por "-flex". Es indudable que sería la mejor forma de alinear elementos de bloque, pues no lidiamos con medidas, porcentajes ni otras dependencias o "trucos". Esperemos que llegue a implementarse, pero no hay que olvidar que es una especificación aún en fase de borrador.