Diseño de mampostería solo CSS

Resuelto Pekka asked hace 7 años • 0 respuestas

Necesito implementar un diseño de mampostería. Sin embargo, por varias razones no quiero usar JavaScript para hacerlo.

Una cuadrícula de múltiples columnas de rectángulos de diferente altura.

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-countpropiedad.

El problema con esa solución es que los elementos están ordenados en columnas:

Comenzando desde el cuadro superior izquierdo, están numerados del 1 al 4 hacia abajo, el cuadro superior en la siguiente columna es el 5, y así sucesivamente.

Si bien necesito que los elementos estén ordenados en filas, al menos aproximadamente:

Comenzando desde el cuadro superior izquierdo, están numerados del 1 al 6 en línea recta, pero debido a que el cuadro 5 es el más corto, el cuadro debajo es 7, ya que tiene la apariencia de estar en una fila más alta que el siguiente cuadro en el extremo izquierdo.

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?

Pekka avatar Jun 06 '17 03:06 Pekka
Aceptado

Actualización 2021

CSS Grid Layout Level 3 incluye una masonryfunció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:

  1. Los elementos flexibles fluyen verticalmente, no horizontalmente (como es necesario en este caso).
  2. El contenedor se expande horizontalmente, no verticalmente (como el diseño de Pinterest).
  3. Requiere que el contenedor tenga una altura fija, para que los artículos sepan dónde envolverse.
  4. 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>
Expandir fragmento

Demostración de jsFiddle


Cómo funciona

  1. Establezca un contenedor de cuadrícula a nivel de bloque. ( inline-gridsería la otra opción)

  2. La grid-auto-rowspropiedad establece la altura de las filas generadas automáticamente. En esta cuadrícula, cada fila tiene 50 píxeles de alto.

  3. La grid-gappropiedad es una abreviatura de grid-column-gapy grid-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).

  4. La grid-template-columnspropiedad establece el ancho de las columnas definidas explícitamente.

    La repeatnotación define un patrón de columnas (o filas) repetidas.

    La auto-fillfunció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 flexible flex-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 frunidad representa una fracción del espacio libre en el contenedor de la cuadrícula. Es comparable a flex-growla propiedad de flexbox.

  5. Con grid-rowy spanle decimos a los elementos de la cuadrícula cuántas filas deben abarcar.

Michael Benjamin avatar Jul 19 '2017 21:07 Michael Benjamin

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 orderpropiedad, 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>
Expandir fragmento

Oliver Joseph Ash avatar Jun 15 '2019 16:06 Oliver Joseph Ash