Diseño de revista en cuadrícula responsiva en solo 20 líneas de CSS
Recientemente estuve trabajando en una versión moderna del blogroll. La idea era ofrecer a los lectores una selección de las últimas publicaciones de esos blogs en un diseño estilo revista, en lugar de simplemente mostrar una lista de nuestros blogs favoritos en la barra lateral.
La parte fácil fue obtener una lista de publicaciones con extractos de nuestros canales RSS favoritos. Para eso, utilizamos un complemento de WordPress, Feedzy lite, que puede agregar múltiples feeds en una única lista ordenada por tiempo, perfecta para mostrar sus últimas ofertas. Lo difícil fue hacer que todo pareciera increíble.
La interfaz de usuario de lista predeterminada del complemento es bastante insípida, por lo que quería diseñarla para que pareciera el sitio web de un periódico o revista con una combinación de paneles de “contenido destacado” más pequeños y más grandes.
¡Este parece un caso ideal para CSS Grid! Cree un diseño de cuadrícula para diferentes diseños, por ejemplo, un diseño de cinco columnas y otro de tres columnas, luego use consultas de medios para alternar entre ellos en diferentes puntos de interrupción. ¿Bien? Pero, ¿realmente necesitamos esas consultas de los medios (y toda la molestia de identificar puntos de interrupción) cuando podemos usar auto-fit
las opciones de la cuadrícula para crear automáticamente una cuadrícula con capacidad de respuesta fluida para nosotros?
El enfoque sonaba tentador, pero cuando comencé a introducir elementos que abarcaban columnas, tuve problemas con la cuadrícula que se desbordaba en las pantallas estrechas. Las consultas de los medios parecían ser la única solución. Es decir, ¡hasta que encontré una solución!
Después de mirar varios tutoriales sobre CSS Grid, descubre que se divide en gran medida en dos campos:
- Tutoriales que le muestran cómo crear un diseño interesante con elementos distribuidos, pero para un número fijo de columnas.
- Tutoriales que explican cómo crear una cuadrícula responsiva que cambia de tamaño automáticamente, pero con todos los elementos de la cuadrícula del mismo ancho (es decir, sin columnas distribuidas).
Quiero que la cuadrícula haga ambas cosas: crear un diseño fluido totalmente responsivo que también incluya el cambio de tamaño de elementos de varias columnas de manera responsiva.
Lo bueno es que una vez que se comprenden las limitaciones de las cuadrículas responsivas, y por qué y cuándo los espacios de columnas interrumpen la capacidad de respuesta de la cuadrícula, es posible definir un diseño de estilo de revista/noticias responsivo en sólo una docena de líneas de código más una simple consulta de medios (o incluso sin consultas de los medios si está dispuesto a limitar sus opciones de extensión).
Aquí hay una imagen que muestra el complemento RSS listo para usar y cómo se verá después de darle estilo.
Este diseño de cuadrícula estilo revista responde completamente y los paneles destacados de colores se ajustan dinámicamente a medida que cambia el número de columnas. La página muestra alrededor de 50 publicaciones, pero el código de diseño es independiente de la cantidad de elementos mostrados. Aumente el complemento para mostrar 100 elementos y el diseño seguirá siendo interesante en todo momento.
Todo esto se logra usando solo CSS y con una sola consulta de medios para manejar una visualización de una sola columna en las pantallas más estrechas (es decir, más pequeñas que 460 px).
Increíblemente, este diseño solo tomó 21 líneas de CSS (excluyendo el estilo de contenido global). Sin embargo, para lograr tal flexibilidad en tan pocas líneas de código, tuve que profundizar en las partes más oscuras de algunas de CSS Grid y aprender a solucionar algunas de sus limitaciones.
Los elementos esenciales del código que producen este diseño son increíblemente cortos y un testimonio de la genialidad de CSS Grid:
.archive { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); grid-gap: 32px; grid-auto-flow: dense;}/* Extra-wide grid-posts */.article:nth-child(31n + 1) { grid-column: 1 / -1;}.article:nth-child(16n + 2) { grid-column: -3 / -1;}.article:nth-child(16n + 10) { grid-column: 1 / -2;}/* Single column display for phones */@media (max-width: 459px) { .archive { display: flex; flex-direction: column; }}
Las técnicas de este artículo podrían usarse igualmente bien para diseñar cualquier contenido generado dinámicamente, como la salida de un widget de publicaciones más recientes, páginas de archivo o resultados de búsqueda.
Creando una parrilla responsiva
He configurado diecisiete elementos que muestran una variedad de contenido simulado (títulos, imágenes y extractos), todos los contenidos en un voltorio.
div article !-- content -- /article !-- 16 more articles -- /div
El código que convierte estos elementos en una cuadrícula responsiva es notablemente compacto:
.archive { /* Define the element as a grid container */ display: grid; /* Auto-fit as many items on a row as possible without going under 180px */ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); /* A little spacing between articles */ grid-gap: 1em;}
Observe cómo las alturas de las filas se ajustan automáticamente para acomodar el contenido más alto de la fila. Si cambia el ancho del lápiz, verá que los elementos crecen y se reducen con fluidez y que el número de columnas cambia de una a cinco, respectivamente.
La magia de CSS Grid en juego aquí es la auto-fit
palabra clave que funciona de la mano con la minmax()
función que se aplica a grid-template-columns
.
Cómo funciona
Podríamos haber logrado el diseño de cinco columnas solo usando esto:
.archive { display: grid; grid-template-columns: repeat(5, 1fr);}
Sin embargo, esto crearía cinco columnas que crecen y se reducen con diferentes anchos de pantalla, pero siempre permanecen en cinco columnas, lo que hace que se vuelvan ridículamente estrechas en pantallas pequeñas. La primera idea podría ser crear un montón de consultas de medios y redefinir la cuadrícula con diferentes números de columnas. Eso funcionaría bien, pero con la auto-fit
palabra clave, todo se hace automáticamente.
Para que el ajuste automático funcione como queremos, debemos usar la minmax()
función. Esto le indica al navegador qué tan pequeñas se pueden reducir las columnas, seguido del ancho máximo al que se pueden expandir. Si es más pequeño, se reducirá automáticamente el número de columnas. Si es mayor, el número de columnas aumentará.
.archive { grid-template-columns: repeat (auto-fit, minmax(180px, 1fr));}
En este ejemplo, el navegador cabe en tantas columnas como sea posible de 180 píxeles de ancho. Si espacio queda sobre las columnas, todas crecerán por igual al compartir el espacio restante entre ellas; eso es lo que 1fr
dice el valor: hacer que las columnas sean fracciones iguales del ancho disponible.
Arrastre la ventana hacia afuera y, a medida que aumenta el espacio disponible, todas las columnas crecen por igual para utilizar cualquier espacio adicional. Las columnas seguirán creciendo hasta que el espacio disponible permita una columna adicional de 180 píxeles, momento en el que aparece una columna completamente nueva. Disminuya el ancho de la pantalla y el proceso se invierte, ajustando perfectamente la cuadrícula hasta llegar a un diseño de una sola columna. ¡Magia!
Y obtienes toda esta capacidad de respuesta con una sola línea de código. ¿Cuan genial es eso?
Creando tramos con “flujo automático: denso”
Hasta ahora, tenemos una cuadrícula receptiva pero todos los elementos tienen el mismo ancho. Para el diseño de una noticia o revista, necesitamos que algún contenido se presente en dos o más columnas o incluso, tal vez, en todas las columnas.
Para crear tramos de varias columnas, podemos agregar la función de tramo de columnas a los elementos de la cuadrícula que queremos que ocupen más espacio. Por ejemplo, si queremos que el tercer elemento de nuestra lista tenga dos columnas de ancho, podemos agregar:
.article:nth-child(3) { grid-column: span 2;}
Sin embargo, una vez que comenzamos a agregar tramos, pueden surgir una serie de problemas. En primer lugar, pueden aparecer espacios en la cuadrícula porque es posible que un elemento ancho no quepa en la fila, por lo que la cuadrícula auto-fit
lo empuja a la siguiente línea, dejando un espacio donde habría estado:
La solución fácil es agregar grid-auto-flow: dense
un elemento de cuadrícula que le indique al navegador que llene los espacios con otros elementos, haciendo que el contenido más limitado fluya alrededor de los elementos más amplios, como este:
Tenga en cuenta que los elementos ahora están desordenados, y el cuarto elemento viene antes que el tercero, que tiene el doble de ancho. Hasta donde yo sé, no hay forma de evitar esto, y es una de las limitaciones que debes aceptar con CSS Grid.
Consulte “The Auto-Flowing Powers of Grid's Dense Keyword” de Geoff Graham para obtener una introducción al grid-auto-flow: denso con ejemplos de cómo se comporta.
Formas de especificar tramos
Hay varias formas de indicar cuántas columnas debe abarcar un elemento. Lo más fácil es aplicar grid-columns: span [n]
a uno de los elementos, donde n
está el número de columnas que abarcará el elemento. El tercer elemento de nuestro diseño tiene grid-column: span 2
, lo que explica por qué tiene el doble de ancho que otros elementos que solo abarcan una sola columna.
Otros métodos requieren que usted defina explícitamente líneas de cuadrícula. El sistema de numeración de las líneas de la cuadrícula es el siguiente:
Las líneas de cuadrícula se pueden especificar de izquierda a derecha usando valores positivos (por ejemplo, 1, 2, 3) o valores negativos (por ejemplo, -1, -2, -3) para ir de derecha a izquierda. Estos se pueden usar para colocar elementos en la cuadrícula usando la propiedad grid-column como esta:
.grid-item { grid-column: (start track) / (end track);}
Entonces, esto nos brinda formas adicionales de especificar un elemento distribuido. Esto es especialmente flexible ya que el valor inicial o final se puede reemplazar con la palabra clave span. Por ejemplo, el cuadro azul de tres columnas del ejemplo anterior podría crearse agregando cualquiera de los siguientes elementos al octavo elemento de la cuadrícula:
grid-column: 3 / 6
grid-column: -4 / -1
grid-column: 3 / span 3
grid-column: -4 / span 3
grid-column: span 3 / -1
- Etc.
En una cuadrícula que no responde (es decir, columnas fijas), todas producen el mismo efecto (como el cuadro azul de arriba); sin embargo, si la cuadrícula responde y el número de columnas cambia, sus diferencias comienzan a hacerse evidentes. Ciertos tramos de columnas rompen el diseño con una cuadrícula que fluye automáticamente, haciendo que las dos técnicas parezcan incompatibles. Afortunadamente, existen algunas soluciones que nos permiten combinar ambos con éxito.
Sin embargo, primero debemos comprender el problema.
Problemas de desplazamiento lateral desbordado
A continuación se muestran algunas áreas destacadas creadas utilizando la notación anterior:
Todo se ve bien en ancho completo (cinco columnas), pero cuando se cambia el tamaño a lo que deberían ser dos columnas, el diseño se divide así:
Como puede ver, nuestra cuadrícula ha perdido su capacidad de respuesta y, aunque el contenedor se ha reducido, la cuadrícula intenta mantener las cinco columnas. Para hacerlo, ha dejado de intentar mantener columnas de igual ancho y la cuadrícula se está saliendo del lado derecho de su contenedor, provocando un desplazamiento horizontal.
¿Por qué es esto? El problema surge porque el navegador intenta respetar las líneas de cuadrícula explícitas que nombramos. Con este ancho, la auto-fit
cuadrícula debería mostrar implícitamente dos columnas, pero nuestro sistema de numeración de líneas de cuadrícula contradice esto al hacer referencia explícita a la quinta línea de la cuadrícula. Esta contradicción lleva al desorden. Para mostrar correctamente nuestra cuadrícula implícita de dos columnas, los únicos números de línea permitidos son 1, 2 y 3 y -3, -2, -1, así:
Pero si alguno de nuestros elementos de la cuadrícula contiene grid-column
referencias que se encuentran fuera de esto, como la línea de la cuadrícula número 4, 5 o 6 (o -4, -5 o -6), el navegador recibe mensajes contradictorios. Por un lado, le hemos pedido que cree automáticamente columnas flexibles (lo que implícitamente debería darnos dos columnas con este ancho de pantalla) pero también nos hemos referido explícitamente a líneas de cuadrícula que no aparecen en una cuadrícula de dos columnas. Cuando hay un conflicto entre columnas implícitas (automáticas) y un número explícito de columnas, la cuadrícula siempre difiere de la cuadrícula explícita ; de ahí las columnas no deseadas y el desbordamiento horizontal (que también se ha denominado acertadamente pérdida de datos CSS). Al igual que con los números de línea de la cuadrícula, los intervalos también pueden crear columnas explícitas. Entonces, grid-column: span 3
(el octavo elemento de la cuadrícula en la demostración) obliga a la cuadrícula a adoptar explícitamente al menos tres columnas, mientras que queremos que muestre implícitamente dos.
En este punto, podría parecer que la única forma de avanzar es utilizar consultas de medios para cambiar los valores de las columnas de la cuadrícula en el ancho donde nuestro diseño se rompe, ¡pero no tan rápido! Eso es lo que supuse al principio. Pero después de pensarlo un poco más y jugar con varias opciones, descubrí que hay un conjunto limitado de soluciones que funcionan hasta dos columnas, dejando solo una consulta de medios para cubrir un diseño de una sola columna para las pantallas más estrechas.
Las soluciones
Me di cuenta de que el truco consiste en especificar tramos únicamente utilizando líneas de cuadrícula que aparecen en la cuadrícula más estrecha que desea mostrar. En este caso, se trata de una cuadrícula de dos columnas. (Usaremos una consulta de medios para cubrir el escenario de una sola columna para pantallas muy estrechas). Eso significa que podemos usar de forma segura las líneas de cuadrícula 1, 2 y 3 (o -3, -2 y -1) sin romper la cuadrícula.
Inicialmente pensé que eso significaba limitarme a un lapso máximo de dos columnas, usando combinaciones de lo siguiente:
grid column: span 2
grid-column: 1 /3
grid-column: -3 / -1
Que sigue respondiendo perfectamente hasta en dos columnas:
Aunque esto funciona, es bastante limitante desde una perspectiva de diseño y no particularmente emocionante. Quería poder crear tramos que tuvieran tres, cuatro o incluso cinco columnas de ancho en pantallas grandes. ¿Pero cómo? Lo primero que pensé fue que tendría que recurrir a consultas de los medios (¡Dios mío, los viejos hábitos son difíciles de morir!), pero estaba tratando de alejarme de ese enfoque y pensar de manera diferente sobre el diseño responsivo.
Echando otro vistazo a lo que podemos hacer con solo 1 a 3 y -3 a -1, gradualmente me di cuenta de que podía mezclar números de línea positivos y negativos para los valores inicial y final de la columna de la cuadrícula, como 1/-3
y 2/-2
. A primera vista esto no parece muy interesante. Eso cambia cuando te das cuenta de dónde se encuentran estas líneas a medida que cambias el tamaño de la cuadrícula: estos elementos distribuidos cambian de ancho con el tamaño de la pantalla. Esto abrió un conjunto completamente nuevo de posibilidades para la distribución de columnas responsivas: elementos que abarcarán diferentes números de columnas a medida que la pantalla se ensancha, sin necesidad de consultas de medios.
El primer ejemplo que descubrí es grid-column: Esto 1/-1
hace que el elemento actúe como un banner de ancho completo, que abarca desde la primera hasta la última columna en todos los números de columna. ¡Incluso funciona hasta una columna de ancho!
Al usar grid-column: 1/-2
, se podría crear un intervalo de ancho casi completo alineado a la izquierda que siempre dejaría un elemento de una columna a la derecha. Cuando se reduce a dos columnas, se reducirá en respuesta a una sola columna. Sorprendentemente, incluso funciona cuando se reduce a un diseño de una sola columna. (La razón parece ser que la cuadrícula no colapsará un elemento a un ancho cero, por lo que permanece con un ancho de una columna, al igual que grid-column: 1/1
). Supuse grid-column: 2/-1
que funcionaría de manera similar, pero alineado con el borde derecho, y en su mayor parte funciona , excepto en la visualización de una columna cuando provoca un desbordamiento.
Luego probé 1/-3
lo que funcionó bien en pantallas más anchas, mostrando al menos tres columnas, y en pantallas más pequeñas, mostrando una columna. Pensé que haría algo extraño en una cuadrícula de dos columnas ya que la primera línea de la cuadrícula es la misma que la línea de la cuadrícula con -3
. Para mi sorpresa, todavía se muestra bien como elemento de una sola columna.
Después de jugar mucho, se me ocurrieron once posibles valores de columnas de la cuadrícula usando números de línea de la cuadrícula de dos columnas. Sorprendentemente, tres de ellos funcionan con diseños de una sola columna. Siete más funcionan en dos columnas y solo necesitarían una única consulta de medios para manejar la visualización de una sola columna.
Aquí está la lista completa:
Como puede ver, aunque este es un subconjunto limitado de cada intervalo de respuesta posible, en realidad hay muchas posibilidades.
2/-2
¡Es interesante ya que crea un tramo centrado que llega hasta una columna!3/-1
es menos útil ya que provoca desbordamiento incluso con dos columnas.3/-3
fue una sorpresa.
Al utilizar una variedad de grid-column
valores de esta lista, es posible crear un diseño interesante y totalmente responsivo. Utilizando una única consulta de medios para la visualización de una sola columna más estrecha, tenemos diez grid-column
patrones de intervalo diferentes con los que jugar.
La consulta de medios de una sola columna también suele ser sencilla. El de esta demostración final vuelve a usar flexbox en pantallas más pequeñas:
@media (max-width: 680px) { .archive { display: flex; flex-direction: column; } .article { margin-bottom: 2em; }}
Aquí está la cuadrícula final que, como puede ver, responde completamente de una a cinco columnas:
Usando :nth-child() para repetir visualizaciones de longitud variable
El último truco que utilicé para reducir mi código a dos docenas de líneas fue el :nth-child(n)
selector que utilicé para diseñar varios elementos en mi cuadrícula. Quería que mi estilo de extensión se aplicara a varios elementos de mi feed, de modo que los cuadros de publicación destacados aparecieran regularmente en toda la página. Para empezar utilicé una lista de selección separada por comas, como esta:
.article:nth-child(2),.article:nth-child(18),.article:nth-child(34),.article:nth-child(50) { background-color: rgba(128,0,64,0.8); grid-column: -3 / -1;}
Pero pronto encontré esto engorroso, especialmente porque tenía que repetir esta lista para cada elemento secundario al que quería darle estilo dentro de cada artículo, como el título, los enlaces, etc. Durante la creación de prototipos, si quería jugar con la posición de mis elementos distribuidos, tenía que cambiar manualmente los números en cada una de estas listas, lo cual era tedioso y propenso a errores.
Fue entonces cuando me di cuenta de que podía usar un :nth-child
pseudoselector de funciones potente en lugar de un número entero simple como había usado en la lista anterior. :nth-child(n)
También puede tomar una ecuación, como :nth-child(2n+ 2)
, que apuntará a uno de cada dos elementos secundarios.
Así es como usé :nth-child([formula])
para crear los paneles azules de ancho completo en mi cuadrícula que aparecen en la parte superior de la página y se repiten un poco más de la mitad hacia abajo:
.article:nth-child(31n + 1) { grid-column: 1 / -1; background: rgba(11, 111, 222, 0.5);}
El bit entre paréntesis ( 31n + 1
) garantiza que se seleccione el hijo 1.º , 32.º , 63.º , etc. El navegador ejecuta un bucle que comienza con n=0 (en cuyo caso 31 * 0 + 1 = 1
), luego n=1
( 31 * 1 + 1 = 32
), luego n=2
( 31 * 2 + 1 = 63
). En el último caso, el navegador se da cuenta de que no hay ningún elemento secundario número 63 , por lo que lo ignora, detiene el bucle y aplica el CSS al elemento secundario 1 y 32 .
Hago algo similar para los cuadros morados que se alternan en la página de derecha a izquierda:
.article:nth-child(16n + 2) { grid-column: -3 / -1; background: rgba(128, 0, 64, 0.8);}.article:nth-child(16n + 10) { grid-column: 1 / -2; background: rgba(128, 0, 64, 0.8);}
El primer selector es para los cuadros morados de la derecha. Se 16n + 2
asegura de que el estilo se aplique a cada 16º elemento de la cuadrícula, comenzando con el segundo elemento.
El segundo selector apunta a los cuadros de la derecha. Utiliza el mismo espaciado ( 16n
) pero con un desplazamiento diferente ( 10
). Como resultado, estos cuadros aparecen regularmente en el lado derecho de los elementos de la cuadrícula 10, 26, 42, etc.
En lo que respecta al estilo visual de estos elementos de la cuadrícula y su contenido, utilicé otro truco para reducir la repetición. Para estilos que comparten ambos cuadros (como background-color
, por ejemplo), se puede usar un único selector para apuntar a ambos:
.article:nth-child(8n + 2) { background: rgba(128, 0, 64, 0.8); /* Other shared syling */}
Esto se centrará en los elementos 2, 10, 18, 26, 34, 42, 50, etc. En otras palabras, selecciona los cuadros destacados de la izquierda y de la derecha.
Funciona porque 8n es exactamente la mitad de 16n
y porque las compensaciones utilizadas en los dos selectores separados tienen una diferencia de 8 (es decir, la diferencia entre +10 y +2 es 8)
Pensamientos finales
En este momento, CSS Grid se puede usar para crear cuadrículas responsivas flexibles con un código mínimo, pero esto tiene algunas limitaciones significativas en el posicionamiento de elementos sin el paso retrógrado de usar consultas de medios.
Sería fantástico poder especificar intervalos que no forzaran el desbordamiento en pantallas más pequeñas. En este momento, efectivamente le decimos al navegador: "Haga una cuadrícula responsiva, por favor", lo cual lo hace maravillosamente. Pero cuando continuamos diciendo: "Ah, y haga que este elemento de la cuadrícula abarque cuatro columnas", genera un sonido silbante en pantallas estrechas, priorizando la solicitud de extensión de cuatro columnas en lugar de la cuadrícula receptiva. Sería fantástico poder decirle a grid que priorice la capacidad de respuesta sobre nuestra solicitud de intervalo. Algo como esto:
.article { grid-column: span 3, autofit;}
Otro problema con las cuadrículas responsivas es la última fila. A medida que cambia el ancho de la pantalla, la última fila frecuentemente no se llenará. Pasé mucho tiempo buscando una manera de hacer que el último elemento de la cuadrícula abarque (y por lo tanto llene) las columnas restantes, pero parece que no puedes hacerlo en Grid en este momento. Sería bueno si pudiéramos especificar la posición inicial del elemento con una palabra clave como auto, que significa "Deje el borde izquierdo dondequiera que caiga". Como esto:
.article { grid-column: auto, -1;}
…lo que haría que el borde izquierdo se extendiera hasta el final de la fila.
Deja un comentario