Cree un diseño CSS en zigzag usando técnicas de cuadrícula + transformación
La mayoría de los diseños de cuadrículas están ordenados de forma ordenada y perfectamente alineados, como soldados en formación. Pero a veces quieres algo más rítmico: un diseño donde los elementos caen en cascada en diagonal, como el agua que fluye por una cascada.
Este es el diseño en zigzag. Construirlo requiere un pequeño truco que revela algunas cosas interesantes sobre cómo funcionan realmente las transformaciones CSS.
Estrategia
Antes de escribir una línea de CSS, consideremos el enfoque.
La primera idea que me vino a la mente: montar un contenedor flexible flex-direction: column y flex-wrap: wrappor lo que los artículos fluyen hacia abajo y luego se empaquetan en la segunda columna. Generalmente pensamos en flex-wrap Las propiedades están en filas, pero lo bueno de Flexbox es que funciona en cualquier dirección.
Hay dos problemas que hacen que este enfoque sea incómodo:
- Necesitas una altura fija. Debes decirle al contenedor “Eres
500pxalto” para terminar. Queda crujiente. - El orden de tabulación está roto. Los elementos fluyen a lo largo de la primera columna (es decir, 1, 2, 3) y luego saltan a la segunda columna (es decir, 4, 5, 6). Eso no es una cascada. Esos son dos cubos.
Para ser justos, Cuadrícula CSS El método que estamos a punto de construir tiene su propio valor codificado. Resolveremos este problema. pero evita pestaña El problema del pedido está completamente resuelto, lo cual es una victoria significativa.
plano de red
Esto es lo que quiero hacer:
- Crea una cuadrícula de dos columnas con elementos uno al lado del otro, nada especial.
- Seleccione todos los elementos de la segunda columna, los elementos pares.
- Muévelos hacia abajo a la mitad de su altura para crear un diseño escalonado.
En esta transformación es donde ocurre la magia. Vamos a construirlo.
red
Empezamos con un envoltorio y cinco artículos. Aún no hay nada en el documento, sólo una hoja de papel en blanco.
<div class="wrapper">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
*,
*::before,
*::after {
box-sizing: border-box;
}
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
max-width: 800px;
margin: 0 auto;
}
.item {
height: 100px;
border: 2px solid;
}
estamos aplicando box-sizing: border-box Globalmente, porque sin él, estos proyectos no son realmente 100px Alto: agregar bordes los hará un poco más altos. Esto será importante más adelante.
cambiar
Ahora viene la parte divertida. Tomemos cada elemento par y tradujémoslo:
.item:nth-child(even of .item) {
transform: translateY(50%);
}
Una nota rápida sobre los selectores. puedes buscar .item:nth-of-type(even) Aquí, en esta demostración, se produce el mismo resultado porque todos los elementos secundarios son del mismo tipo de elemento. pero nth-of-type Seleccione por nombre de etiqueta, no por categoría. Entonces, si mezclas diferentes tipos de elementos en un contenedor, coincidirá de maneras que no esperarías. :nth-child(even of .item) Más preciso porque filtra explícitamente por categorías y es compatible con los navegadores modernos.
Los zigzags aparecen inmediatamente. Pero hagamos una pausa aquí porque está sucediendo algo sutil que vale la pena entender.
Los porcentajes de transformación son diferentes.
La forma en que funcionan los porcentajes en las transiciones es completamente diferente a la forma en que funcionan en cualquier otro lugar en CSS.
En el diseño de flujo, el diseño de posicionamiento o cualquier modo de diseño, el porcentaje se refiere al espacio disponible para los padres. si escribes width: 50% En el elemento dentro del contenedor dirías: El contenedor es así de ancho. dame la mitad
Las transformaciones no funcionan así. En la conversión, el porcentaje se refiere al propio elemento. entonces translateY(50%) No significa “reducir la mitad del espacio disponible”. Significa “bajar la mitad de la altura”. Si el elemento es 200px alto, se mueve hacia abajo 100px.
En realidad, este es el mismo comportamiento del sistema de coordenadas que se ve en los individuos. translate(), scale()y rotate() Propiedades CSS. Todo esto se aplica en el propio espacio de coordenadas del elemento, es decir, después del diseño. El navegador primero presenta todo, incluido el posicionamiento y el tamaño (básicamente, todo el modelo de caja) y luego aplica transformaciones relativas al elemento mismo. es por eso scale(2) Crece hacia afuera desde el centro del elemento, en lugar de desde la esquina superior izquierda de la página.
Es exactamente por eso que este truco funciona. Cada elemento par se mueve hacia abajo en relación con su propio tamaño, no con el tamaño del contenedor. Zigzag mantiene proporciones sin importar la altura del artículo.
Los resultados parecen cercanos. Pero eso no es del todo cierto.
problema de brecha
Podemos exponer defectos a través del inicio. gap Haz algo ridículo, como, 100px. Cuando hacemos esto, los elementos pares claramente no están donde deberían estar. Necesitan ir un poco más lejos para tener en cuenta el espacio vertical entre filas.
Aquí está la solución. Primero, almacenemos el espacio en una propiedad personalizada de CSS para que podamos hacer referencia a él en varios lugares:
.wrapper {
--gap: 16px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--gap);
max-width: 800px;
margin: 0 auto;
}
.item:nth-child(even of .item) {
transform: translateY(calc(50% + var(--gap) / 2));
}
Lo que traducimos es 50% La altura del elemento más la mitad del espacio. Dividimos la diferencia entre 2 Dado que solo necesitamos cubrir la mitad de la distancia entre filas, el valor total lo llevaría demasiado lejos.
Establezca la brecha en 16pxluce genial. configúrelo en 100pxtodavía se ve genial. Independientemente del valor, las matemáticas son válidas.
sorpresa desbordante
Hemos resuelto el rompecabezas central. Pero hay un problema oculto esperando a emerger.
Agreguemos un borde al contenedor para ver sus límites:
.wrapper {
border: 2px solid red;
}
Con cinco proyectos, todo pinta bien. El contenedor contiene todos sus hijos. Sin desbordamiento. Ningún problema.
Ahora agregue un sexto elemento:
<div class="wrapper">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
El sexto término es un número par. Fue traducido. Se desborda directamente del contenedor.
¿Por qué? Porque la transformación no afecta el diseño. En lo que respecta al motor de diseño del navegador, el sexto elemento todavía se encuentra en su posición original, sin traducir. El contenedor cambia de tamaño según la posición original. La transformación mueve visualmente los píxeles, pero el padre no sabe que se ha movido nada.
Sorprendemos a los navegadores.
Solución: Reservar espacio
La solución más sencilla es añadir padding-bottom (o padding-block-end) a la envoltura, suficiente para acomodar el exceso. El relleno debe coincidir con la traducción: la mitad de la altura del elemento más la mitad del espacio.
Dado que el porcentaje de relleno hace referencia al ancho del padre (no a la altura del niño), no podemos usar el mismo porcentaje. 50% Aquí está el truco. En lugar de eso, almacenamos la altura del elemento como una variable:
.wrapper {
--gap: 16px;
--item-height: 100px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--gap);
margin: 0 auto;
max-width: 800px;
padding-bottom: calc(var(--item-height) / 2 + var(--gap) / 2);
}
.item {
border: 2px solid;
height: var(--item-height);
}
Ahora déjame empezar diciendo: --item-height: 100px es un valor codificado. Esta es la misma fragilidad que señalé en el enfoque de flexbox: necesita una altura de contenedor fija para envolver. Ambos métodos requieren que conozcas las dimensiones de antemano. La diferencia aquí es que bloquea la altura del elemento en lugar de la altura del contenedor, y el resto del diseño (estructura de columnas, cálculo de espacios, orden de origen) permanece flexible. Esto es una compensación, no un factor decisivo, pero vale la pena ser honesto al respecto.
El envoltorio deja ahora mucho espacio en la parte inferior. Sin desbordamiento. Sin sorpresas.
Una nota sobre accesibilidad
Este enfoque mantiene los proyectos en su orden natural de origen, lo cual es más importante de lo que parece a primera vista.
Los lectores de pantalla no se ven afectados. La transformación es puramente visual. El orden DOM sigue siendo 1-6, que es exactamente como los anuncian las tecnologías de asistencia. A diferencia del método de ajuste de columnas de Flexbox, donde el orden visual y el orden DOM pueden diferir, el reordenamiento no será sorprendente.
El orden de enfoque también permanece sin cambios. Cuando alguien busca elementos a través de etiquetas, el enfoque sigue el orden de origen, no la posición de visualización visual del elemento. En nuestro zigzag, el flujo visual y el orden de las fuentes caen en cascada de izquierda a derecha y de arriba a abajo, por lo que son naturalmente consistentes. Si su diseño se vuelve lo suficientemente complejo como para que el orden visual y de origen comience a divergir, entonces debe pensar más detenidamente en la gestión del enfoque.
Respete las preferencias de ejercicio. El zigzag en sí es estático: no animaremos la transformación. Sin embargo, si decide animar elementos en una posición escalonada (por ejemplo, al cargar la página), ajuste esa animación en prefers-reduced-motion Controlar:
/* animates when user has no motion preference */
@media (prefers-reduced-motion: no-preference) {
.item {
animation: slide-in 0.3s ease-out both;
}
}
En este ejemplo, lo configuramos para que los usuarios que no tienen preferencia por el movimiento sean los únicos que obtengan la animación. Pero normalmente, Puedes hacer lo contrario. De todos modos, el diseño sigue funcionando.
demostración final
de nuevo:
en conclusión
El diseño en zigzag consta en realidad de tres ideas apiladas una encima de la otra:
- La cuadrícula de dos columnas nos proporciona la base.
translateY(50%)Crea un escalonamiento y funciona porque el porcentaje de transformación hace referencia al elemento en sí, no al elemento principal.padding-bottomReserve espacio para los elementos traducidos, ya que las transformaciones mueven píxeles sin notificar al motor de diseño.
Cambia la brecha. Cambiar la altura del artículo. Añade más elementos. El zigzag se mantiene sin cambios.