Diseño de mampostería solo CSS
Necesito implementar un diseño de mampostería. Sin embargo, por varias razones no quiero usar JavaScript para hacerlo.
Parámetros:
- Todos los elementos tienen el mismo ancho.
- Los elementos tienen una altura que no se puede calcular en el lado del servidor (una imagen más varias cantidades de texto)
- Puedo vivir con un número fijo de columnas si es necesario
Existe una solución trivial para esto que funciona en los navegadores modernos: la column-count
propiedad.
El problema con esa solución es que los elementos están ordenados en columnas:
Si bien necesito que los elementos estén ordenados en filas, al menos aproximadamente:
Enfoques que he probado y que no funcionan:
- Fabricación de objetos
display: inline-block
: desperdicia espacio vertical. - Hacer artículos
float: left
: jajaja, no.
Ahora podría cambiar la representación del lado del servidor y reordenar los elementos dividiendo la cantidad de elementos por la cantidad de columnas, pero eso es complicado y propenso a errores (según cómo los navegadores deciden dividir la lista de elementos en columnas), así que me gustaría para evitarlo si es posible.
¿Existe alguna magia de Flexbox que haga esto posible?
Actualización 2021
CSS Grid Layout Level 3 incluye una masonry
función.
El código se verá así:
grid-template-rows: masonry
grid-template-columns: masonry
A partir de marzo de 2021, solo está disponible en Firefox (después de activar la bandera).
- https://drafts.csswg.org/css-grid-3/#masonry-layout
- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout
finalizar la actualización; respuesta original a continuación
Caja flexible
Un diseño dinámico de mampostería no es posible con flexbox, al menos no de una manera limpia y eficiente.
Flexbox es un sistema de diseño unidimensional. Esto significa que puede alinear elementos a lo largo de líneas horizontales O verticales. Un elemento flexible se limita a su fila o columna.
Un verdadero sistema de cuadrícula es bidimensional, lo que significa que puede alinear elementos a lo largo de líneas horizontales Y verticales. Los elementos de contenido pueden abarcar filas y columnas simultáneamente, lo que los elementos flexibles no pueden hacer.
Es por eso que flexbox tiene una capacidad limitada para construir redes. También es una razón por la que el W3C ha desarrollado otra tecnología CSS3, Grid Layout .
row wrap
En un contenedor flexible con flex-flow: row wrap
, los elementos flexibles deben ajustarse a nuevas filas .
Esto significa que un elemento flexible no puede incluirse debajo de otro elemento en la misma fila .
Observe arriba cómo el div #3 se ajusta debajo del div #1 , creando una nueva fila. No puede ajustarse debajo del div #2 .
Como resultado, cuando los elementos no son los más altos de la fila, quedan espacios en blanco, lo que crea huecos antiestéticos.
column wrap
Si cambia a flex-flow: column wrap
, es más factible un diseño tipo cuadrícula. Sin embargo, un contenedor en dirección de columna tiene cuatro problemas potenciales desde el principio:
- Los elementos flexibles fluyen verticalmente, no horizontalmente (como es necesario en este caso).
- El contenedor se expande horizontalmente, no verticalmente (como el diseño de Pinterest).
- Requiere que el contenedor tenga una altura fija, para que los artículos sepan dónde envolverse.
- Al momento de escribir este artículo, tiene una deficiencia en todos los navegadores principales donde el contenedor no se expande para acomodar columnas adicionales .
Como resultado, un contenedor en dirección de columna no es una opción en este caso, ni en muchos otros.
Cuadrícula CSS con dimensiones de elementos indefinidas
Grid Layout sería una solución perfecta a su problema si las distintas alturas de los elementos de contenido pudieran estar predeterminadas . Todos los demás requisitos están dentro de la capacidad de Grid.
Se debe conocer el ancho y el alto de los elementos de la cuadrícula para cerrar los espacios con los elementos circundantes.
Entonces Grid, que es lo mejor que CSS tiene para ofrecer para construir un diseño de mampostería que fluya horizontalmente, se queda corto en este caso.
De hecho, hasta que llegue una tecnología CSS con la capacidad de cerrar las brechas automáticamente, CSS en general no tiene solución. Algo como esto probablemente requeriría redistribuir el documento, por lo que no estoy seguro de cuán útil o eficiente sería.
Necesitarás un guión.
Las soluciones de JavaScript tienden a utilizar posicionamiento absoluto, que elimina elementos de contenido del flujo de documentos para reorganizarlos sin espacios. Aquí hay dos ejemplos:
- Albañilería Desandro
Masonry es una biblioteca de diseño de cuadrícula de JavaScript. Funciona colocando elementos en una posición óptima según el espacio vertical disponible, algo así como un albañil colocando piedras en una pared.
fuente: http://masonry.desandro.com/
- Cómo construir un sitio que funcione como Pinterest
[Pinterest] es realmente un sitio genial, pero lo que encuentro interesante es cómo se distribuyen estos tableros de anuncios... Entonces, el propósito de este tutorial es recrear este efecto de bloque responsivo nosotros mismos...
fuente: https://benholland.me/javascript/2012/02/20/how-to-build-a-site-that-works-like-pinterest.html
Cuadrícula CSS con dimensiones de elementos definidas
Para diseños donde se conoce el ancho y alto de los elementos de contenido, aquí hay un diseño de mampostería que fluye horizontalmente en CSS puro:
grid-container {
display: grid; /* 1 */
grid-auto-rows: 50px; /* 2 */
grid-gap: 10px; /* 3 */
grid-template-columns: repeat(auto-fill, minmax(30%, 1fr)); /* 4 */
}
[short] {
grid-row: span 1; /* 5 */
background-color: green;
}
[tall] {
grid-row: span 2;
background-color: crimson;
}
[taller] {
grid-row: span 3;
background-color: blue;
}
[tallest] {
grid-row: span 4;
background-color: gray;
}
grid-item {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3em;
font-weight: bold;
color: white;
}
<grid-container>
<grid-item short>01</grid-item>
<grid-item short>02</grid-item>
<grid-item tall>03</grid-item>
<grid-item tall>04</grid-item>
<grid-item short>05</grid-item>
<grid-item taller>06</grid-item>
<grid-item short>07</grid-item>
<grid-item tallest>08</grid-item>
<grid-item tall>09</grid-item>
<grid-item short>10</grid-item>
<grid-item tallest>etc.</grid-item>
<grid-item tall></grid-item>
<grid-item taller></grid-item>
<grid-item short></grid-item>
<grid-item short></grid-item>
<grid-item short></grid-item>
<grid-item short></grid-item>
<grid-item tall></grid-item>
<grid-item short></grid-item>
<grid-item taller></grid-item>
<grid-item short></grid-item>
<grid-item tall></grid-item>
<grid-item short></grid-item>
<grid-item tall></grid-item>
<grid-item short></grid-item>
<grid-item short></grid-item>
<grid-item tallest></grid-item>
<grid-item taller></grid-item>
<grid-item short></grid-item>
<grid-item tallest></grid-item>
<grid-item tall></grid-item>
<grid-item short></grid-item>
</grid-container>
Demostración de jsFiddle
Cómo funciona
Establezca un contenedor de cuadrícula a nivel de bloque. (
inline-grid
sería la otra opción)La
grid-auto-rows
propiedad establece la altura de las filas generadas automáticamente. En esta cuadrícula, cada fila tiene 50 píxeles de alto.La
grid-gap
propiedad es una abreviatura degrid-column-gap
ygrid-row-gap
. Esta regla establece un espacio de 10 píxeles entre los elementos de la cuadrícula. (No se aplica al área entre los artículos y el contenedor).La
grid-template-columns
propiedad establece el ancho de las columnas definidas explícitamente.La
repeat
notación define un patrón de columnas (o filas) repetidas.La
auto-fill
función le dice a la cuadrícula que alinee tantas columnas (o filas) como sea posible sin desbordar el contenedor. (Esto puede crear un comportamiento similar al del diseño flexibleflex-wrap: wrap
).La
minmax()
función establece un rango de tamaño mínimo y máximo para cada columna (o fila). En el código anterior, el ancho de cada columna será un mínimo del 30% del contenedor y un máximo del espacio libre disponible.La
fr
unidad representa una fracción del espacio libre en el contenedor de la cuadrícula. Es comparable aflex-grow
la propiedad de flexbox.Con
grid-row
yspan
le decimos a los elementos de la cuadrícula cuántas filas deben abarcar.
Esta es una técnica descubierta recientemente que involucra flexbox: https://tobiasahlin.com/blog/masonry-with-css/ .
El artículo tiene sentido para mí, pero no he intentado usarlo, por lo que no sé si hay alguna salvedad aparte de la mencionada en la respuesta de Michael.
Aquí hay una muestra del artículo, haciendo uso de la order
propiedad, combinada con :nth-child
.
Fragmento de pila
.container {
display: flex;
flex-flow: column wrap;
align-content: space-between;
/* Your container needs a fixed height, and it
* needs to be taller than your tallest column. */
height: 960px;
/* Optional */
background-color: #f7f7f7;
border-radius: 3px;
padding: 20px;
width: 60%;
margin: 40px auto;
counter-reset: items;
}
.item {
width: 24%;
/* Optional */
position: relative;
margin-bottom: 2%;
border-radius: 3px;
background-color: #a1cbfa;
border: 1px solid #4290e2;
box-shadow: 0 2px 2px rgba(0,90,250,0.05),
0 4px 4px rgba(0,90,250,0.05),
0 8px 8px rgba(0,90,250,0.05),
0 16px 16px rgba(0,90,250,0.05);
color: #fff;
padding: 15px;
box-sizing: border-box;
}
/* Just to print out numbers */
div.item::before {
counter-increment: items;
content: counter(items);
}
/* Re-order items into 3 rows */
.item:nth-of-type(4n+1) { order: 1; }
.item:nth-of-type(4n+2) { order: 2; }
.item:nth-of-type(4n+3) { order: 3; }
.item:nth-of-type(4n) { order: 4; }
/* Force new columns */
.break {
flex-basis: 100%;
width: 0;
border: 1px solid #ddd;
margin: 0;
content: "";
padding: 0;
}
body { font-family: sans-serif; }
h3 { text-align: center; }
<div class="container">
<div class="item" style="height: 140px"></div>
<div class="item" style="height: 190px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 120px"></div>
<div class="item" style="height: 160px"></div>
<div class="item" style="height: 180px"></div>
<div class="item" style="height: 140px"></div>
<div class="item" style="height: 150px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 140px"></div>
<div class="item" style="height: 190px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 120px"></div>
<div class="item" style="height: 160px"></div>
<div class="item" style="height: 180px"></div>
<div class="item" style="height: 140px"></div>
<div class="item" style="height: 150px"></div>
<div class="item" style="height: 170px"></div>
<div class="item" style="height: 170px"></div>
<span class="item break"></span>
<span class="item break"></span>
<span class="item break"></span>
</div>