Cómo posicionar un elemento en el borde izquierdo de la pantalla: un caso práctico

Graffitti de mano apuntando a la izquierda
Imagen de Quinn Dombrowski - modificada.

De vez en cuando me doy de bruces con un caso que aunque a priori parece muy sencillo, termina complicándose hasta el punto de no saber por donde atajarlo. Para este caso concreto, por más que he buscado, rebuscado y leído, no he encontrado una solución a la que dar crédito, salvo la que vamos a ver en el artículo de hoy.

Bien parece un caso simple, y de hecho lo es, si se trabaja con las herramientas adecuadas y no confundes el funcionamiento de algunas de la propiedades o valores que vamos a usar. Sin embargo es un caso específico, en que jugamos con márgenes automáticos, por tanto desconocemos completamente las medidas. Así que mejor vamos al lío.

Poniéndonos en situación

Lo primero de todo es ver el layout con el que vamos a trabajar. De hecho no nos hace falta verlo al completo, sólo una parte que es la que bien podría ser una entrada de cualquier blog.

Tenemos por tanto un elemento padre, que contiene una entrada, con su encabezado y sus párrafos. Todo ello diseñado según el nuevo dogma, sí, responsive o fluido, como prefieras llamarlo.

Este es el problema principal para el posicionamiento de nuestro elemento, que para este caso va a ser el h1 del artículo, ya que no conocemos ninguna medida concreta, y esto es lo que realmente lo complica todo. El código html es el siguiente:


<article>
    <h1>Título</h1>
    <p>Contenido</p>
    <p>Más contenido</p>
</article>

Ahora vamos añadir css muy básico, para que quede claro el punto de partida. Todo es fluido, por lo tanto article ocupará el 100% del ancho de la pantalla en dispositivos pequeños, menos unos márgenes a izquierda y derecha para evitar que el texto quede pegado a los extremos de la pantalla, que los daremos mediante padding.

Cuando el dispositivo sea mayor, article no puede ser tan grande como todo el ancho de la pantalla, ya que líneas de texto muy largas pierden legibilidad. Por tanto, lo suyo es limitar su anchura mediante max-width y centrarlo con márgenes automáticos.

En cuando al encabezado y los párrafos no hay mucho que decir, son elementos de bloque y por tanto ocupan el 100% de su padre, y supongo que ya vas adivinando donde está el problema.


.article {
    max-width: 50em;
    margin: 0 auto;
    padding: 0 1em;
}

h1, p {
    margin: 1.25em 0;
}

La primera tentativa

Lo lógico y según van las cosas es pensar en un posicionamiento absoluto, pero hay un problema, el solapamiento. Un elemento posicionado de forma absoluta, como supongo ya sabes, se sale del flujo normal del programa y pasa a ocupar la esquina superior izquierda de su elemento contenedor. Por contra, el resto de elementos se sitúan de la misma forma que si h1 no existiese, por tanto el encabezado y el primer párrafo acabarían solapándose.

Cierto que esa no es la posición que buscamos, ya que lo queremos en el extremo izquierdo, pero nada nos asegura que su anchura vaya a ser tan pequeña para que no sobrepase lo márgenes automáticos de article, y en dispositivos de pequeña pantalla pasaría sí o sí.

Además no sabemos la altura del encabezado, por tanto no podemos dar un margen superior al primer párrafo para evitar el solapamiento. Se podría calcular, en función al tamaño de la fuente, pero nada nos garantiza que el texto vaya a ocupar una sola línea. Es lo que ocurre con diseños fluidos.


h1 {
    position: absolute;
    left: 0;
}

Por tanto esto, no es lo que buscamos.

Acercándonos a la solución

Si no podemos usar una posición absoluta para luego mover el elemento, existen otras dos formas para hacerlo y que se mantenga el espacio que este debería ocupar: con una posición relativa o bien usando la propiedad transform con la función translate().


/*primera solución*/
h1 {
    position: relative;
    left: -x;
}

/*segunda solución*/
h1 {
    transform: translateX(-x);
}

El problema radica en que no sabemos cuanto tenemos que mover el encabezado a la izquierda. Con el elemento posicionado de forma relativa no nos sirve el valor cero para la propiedad left, ya que a diferencia de un elemento posicionado de forma absoluta, un elemento posicionado de forma relativa mantiene su posición de la misma forma que si estuviese posicionado de forma estática (que es el valor por defecto).

Además toma su referencia para el desplazamiento en función al lugar donde está ubicado, y no a la esquina superior izquierda de la pantalla como lo hace un elemento posicionado de forma absoluta (siempre que no tenga algún ancestro posicionado de forma absoluta o relativa).

Por tanto un elemento posicionado de forma relativa con los valores a cero de left y top se queda en el mismo punto en que estaba.

Así que tanto para el caso de que posicionásemos el elemento de forma relativa, como si pensamos moverlo mediante la función translate(), necesitamos saber cuanto vamos a moverlo hacia la izquierda, y sin ninguna medida concreta, la cosa se complica.

Y por fin, la solución

Como ya comenté al principio del artículo, no he encontrado ningún artículo, ni ninguna referencia para solucionar este problema, al menos no de forma directa. Por suerte soy un hambriento lector de todo tipo de artículo sobre CSS, y no hacía mucho que había leído uno de Chris Coyier en el que daba una serie de formas para conseguir que un elemento sobrepasase los límites de su contenedor. El artículo, Full Width Containers in Limited Width Parents.

Aunque evidentemente no es una solución directa a nuestro problema, si que se parece en cierta forma. En varias de las soluciones que se dan en este artículo manejan las unidades de viewport, que son la clave para calcular el desplazamiento de nuestro elemento.

Vamos a repasar que es lo que sabemos: con las unidades de viewport, en todo momento sabemos cuando mide a lo ancho la pantalla del navegador, y también sabemos lo que mide a lo ancho nuestro encabezado. Sí, ocupa del 100% de su contenedor. También conocemos el padding que usamos para evitar que el texto se sitúe demasiado cerca de los bordes de la pantalla en dispositivos pequeños, pero este dato es irrelevante.

Y ahora, lo que no sabemos: desconocemos la distancia, tanto a la izquierda como a la derecha, de nuestro contenedor (article) respecto a los bordes del navegador.

Con todo esto ya podemos calcular el desplazamiento a la izquierda. El tamaño del viewport horizontal menos el tamaño del elemento que queremos mover, nos da el valor de los huecos vacíos que tenemos a derecha e izquierda (esto incluye margin y padding). Por tanto la mitad de este valor, es la distancia que debemos mover nuestro objeto, para situarlo justo en el borde izquierdo de la pantalla.


/*Posicionar elemento en borde izquierdo*/
h1 {
    transform: translateX(calc((-100vw + 100%)/ 2));
}

Podemos hacer exactamente lo mismo para desplazar el elemento hacia la derecha, sin embargo las unidades de viewport no tienen en cuenta las barras de desplazamiento, y el calculo de un viewport 100 lo hace sin ella. Esto hace que el elemento se sitúe como si no existiese esa barra de desplazamiento, lo que al final se traduce en un "overflow" y la aparición de la nada deseada barra de desplazamiento horizontal.

Por supuesto se puede arreglar. Como no sabemos el ancho de la barra de desplazamiento vertical, y dudando que sea el mismo para todos los navegadores, el problema podemos solucionarlo con overflow-x. Además, hay que tener en cuenta que no todos lo navegadores muestran por defecto la barra de desplazamiento vertical, generalmente los navegadores de dispositivos móviles la tienen ocultan y sólo la muestran cuando nos desplazamos, con lo cual jugar con overflow-x es la mejor solución.


/*Posicionar elemento en borde derecho*/
body {
    overflow-x: hidden;
}

h1 {
    transform: translateX(calc((100vw - 100%)/ 2));
    padding-right: 1em; /*para evitar que se nos corte el texto*/
}

Este no ha sido el típico artículo que suelo publicar en el blog, pero ante las complicaciones que encontré para realizar algo, en principio tan simple, me pareció apropiado comentarlo por si puede serle útil a alguien. Además, de paso, aprovecho para aclarar ciertos conceptos e ir introduciendo en el blog las unidades viewport, que si bien no creo que sean el paradigma para el diseño responsive, en ciertas ocasiones nos vienen muy bien.