Diseño de mampostería solo CSS

134

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

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

Parámetros:

  • Todos los elementos tienen el mismo ancho.
  • Los elementos tienen una altura que no se puede calcular del lado del servidor (una imagen más varias cantidades de texto)
  • Puedo vivir con un número fijo de columnas si tengo que

Hay 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 en línea recta, el cuadro superior en la siguiente columna es 5, y así sucesivamente.

Si bien necesito que los elementos se ordenen en filas, al menos aproximadamente:

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

Enfoques que he probado que no funcionan:

Ahora podría cambiar la representación del lado del servidor y reordenar los elementos dividiendo el número de elementos por el número de columnas, pero eso es complicado, propenso a errores (en función de cómo los navegadores deciden dividir la lista de elementos en columnas), por lo que me gustaría para evitarlo si es posible.

¿Hay alguna magia flexbox novedosa que lo haga posible?

Pekka
fuente
77
No puedo pensar en una forma que no dependa de alturas predefinidas. Si reconsidera JS, eche un vistazo a stackoverflow.com/questions/13518653/… donde implemento una solución que es bastante simple.
Gabriele Petrioli
1
@Gaby implementando su solución ahora mismo, ¡gracias!
Pekka
44
Me doy cuenta de que dijiste solo CSS. Solo quiero mencionar que Masonry ya no requiere jQuery, la biblioteca minificada tiene menos de 8kb , y se puede inicializar solo con html. Solo como referencia jsfiddle.net/wp7kuk1t
I haz kode
1
Si puede determinar la altura de los elementos con anticipación, conociendo la altura de la línea, el tamaño de la fuente (tendría que servir una fuente específica y hacer algunos cálculos inteligentes), la altura de la imagen, el margen vertical y el relleno, puede hacer esto. De lo contrario, no puede hacer esto usando solo CSS. También podría usar algo como PhantomJS para pre-renderizar cada elemento y obtener la altura de ese elemento, pero se agregaría una sobrecarga / latencia significativa.
Timothy Zorn

Respuestas:

157

Flexbox

Un diseño de mampostería dinámico 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 cual 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 ajustarse debajo de otro elemento en la misma fila .

Observe arriba cómo div # 3 se ajusta debajo de div # 1 , creando una nueva fila. No puede envolverse debajo del div # 2 .

Como resultado, cuando los elementos no son los más altos de la fila, queda espacio en blanco, creando huecos antiestéticos.


column wrap

Si cambia a flex-flow: column wrap, un diseño similar a una cuadrícula es más alcanzable. Sin embargo, un contenedor de dirección de columna tiene cuatro problemas potenciales desde el principio:

  1. Los elementos flexibles fluyen verticalmente, no horizontalmente (como necesita 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 envolver.
  4. Al momento de escribir este artículo, tiene una deficiencia en todos los principales navegadores donde el contenedor no se expande para acomodar columnas adicionales .

Como resultado, un contenedor de dirección de columna no es una opción en este caso, y en muchos otros casos.


CSS Grid con dimensiones de elementos indefinidas

El diseño de cuadrícula sería una solución perfecta para su problema si las diferentes alturas de los elementos de contenido pudieran predeterminarse . Todos los demás requisitos están dentro de la capacidad de Grid.

Se debe conocer el ancho y la altura de los elementos de la cuadrícula para cerrar las brechas con los elementos circundantes.

Entonces, Grid, que es el mejor CSS que tiene para ofrecer para construir un diseño de mampostería de flujo horizontal, se queda corto en este caso.

De hecho, hasta que llegue una tecnología CSS con la capacidad de cerrar automáticamente las brechas, CSS en general no tiene solución. Algo como esto probablemente requeriría refluir 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 un posicionamiento absoluto, que elimina elementos de contenido del flujo de documentos para reorganizarlos sin espacios. Aquí hay dos ejemplos:


CSS Grid 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>

jsFiddle demo


Cómo funciona

  1. Establecer 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 mide 50 px 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 repetidas (o filas).

    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 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 de cualquier espacio libre disponible.

    La frunidad representa una fracción del espacio libre en el contenedor de la cuadrícula. Es comparable a la flex-growpropiedad de flexbox .

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


Soporte de navegador para CSS Grid

  • Chrome: soporte completo a partir del 8 de marzo de 2017 (versión 57)
  • Firefox: soporte completo a partir del 6 de marzo de 2017 (versión 52)
  • Safari: soporte completo a partir del 26 de marzo de 2017 (versión 10.1)
  • Edge: soporte completo a partir del 16 de octubre de 2017 (versión 16)
  • IE11: no se admite la especificación actual; admite versión obsoleta

Aquí está la imagen completa: http://caniuse.com/#search=grid


Genial función de superposición de cuadrícula en Firefox

En las herramientas de desarrollo de Firefox, cuando inspeccionas el contenedor de cuadrícula, hay un pequeño icono de cuadrícula en la declaración CSS. Al hacer clic, muestra un esquema de su cuadrícula en la página.

Más detalles aquí: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts

Michael Benjamin
fuente
55
Fantástica respuesta! Con la solución "Cuadrícula CSS con dimensiones de elementos definidas", ¿es posible expresar la altura de una celda como un porcentaje del ancho de la celda? Esto sería útil para mostrar imágenes, donde se conoce la relación de aspecto. Queremos mantener la relación de aspecto en todo momento.
Oliver Joseph Ash
Gracias. Con respecto al problema de la relación de aspecto de las imágenes, el método del "porcentaje de ancho de celda" (¿el truco del relleno del porcentaje?), No es confiable en Grid o Flexbox, para el caso. Mira aquí y aquí . @OliverJosephAsh
Michael Benjamin
Sí, me refiero al truco de porcentaje de relleno. ¿Es posible utilizar alguna de las soluciones alternativas en combinación con su solución de diseño de mampostería? Para el contexto, estamos buscando implementar un diseño de mampostería solo CSS para las imágenes en unsplash.com .
Oliver Joseph Ash
1
Desafortunadamente, usar JS para esto no es una opción. Queremos habilitar la representación del lado del servidor por razones de rendimiento y SEO. Esto significa que el diseño se debe representar antes de que JavaScript del lado del cliente se haya descargado, analizado y ejecutado. Dado que esto parece ser imposible, ¡creo que tendremos que hacer un intercambio en algún punto de la línea! Gracias por su ayuda :-)
Oliver Joseph Ash
1
Actualización de especificaciones: en flexbox, los márgenes porcentuales y los rellenos ahora están configurados para resolver nuevamente el tamaño en línea del contenedor. drafts.csswg.org/css-flexbox/#item-margins @OliverJosephAsh
Michael Benjamin
13

Esta es una técnica recientemente descubierta que involucra flexbox: https://tobiasahlin.com/blog/masonry-with-css/ .

El artículo tiene sentido para mí, pero no he tratado de usarlo, así que no sé si hay algunas advertencias, aparte de las mencionadas 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>

Oliver Joseph Ash
fuente
En primer lugar, las respuestas de solo enlace son malas, como cuando dicho enlace muere, también lo hace la respuesta. En segundo lugar, si lee la respuesta dada con más atención, encontrará que lo que se menciona en su enlace está cubierto. En pocas palabras, existen demasiadas limitaciones al usar Flexbox solo, a menos que lo mencionado anteriormente no sea un problema.
Ason
3
He leído la respuesta aceptada muy a fondo; incluso verás algunos comentarios que agregué al final. El enfoque de flexbox al que me vinculé es único y no está cubierto por esa respuesta. No quiero repetir el artículo, porque es muy complicado, y el artículo hace un gran trabajo cubriendo eso.
Oliver Joseph Ash
Lo único que el enlace hace diferente es hacer uso de la orderpropiedad, haciendo que parezca que los elementos fluyen de izquierda a derecha. Aún así, su truco principal es flex-flow: column wrap, que se menciona en la respuesta anterior. Además, no puede hacer fluir los elementos de la misma manera que lo hace una mampostería real, por ejemplo, si el tercer y cuarto elemento encajarían en la tercera columna, lo harían, el vinculado no lo hace. Pero nuevamente, como dije, todo depende de cuáles sean los requisitos y, en este caso (esta pregunta), no funcionará como se solicitó.
Ason
Además, no se trata de "no desea repetir el artículo" , una respuesta debe contener la parte esencial del código, por lo que seguirá siendo válida cuando el recurso vinculado muera.
Ason
3
Lo actualicé, agregué una muestra del artículo + un voto positivo.
Ason