Closed wesbos closed 7 years ago
That's not something grid is designed for. Grid is two dimensional so you are always working in both rows and columns at the same time. You can't use grid to do a "masonry" style layout like that. You could place items in that way if you had a lot of rows and managed how many each spanned, but you can't use auto-placement to get that kind of layout.
Yeah, that layout looks very flexboxy to me, with flex-direction: column
and flex-wrap: wrap
. The tricky part would be determining the appropriate height for the flex container, since it needs to be constrained in order to produce wrapping.
edit of course, that changes the order of the items
Neither flexbox or Grid can really do this masonry thing, it's a bit of a different type of layout.
I'd love to come up with a solution for it, I think it fits better into flexbox type layout than grid type layout as it is a content out type of construction. However we don't have the tools currently to do it. That doesn't mean that a future level couldn't include such a feature, and that's part of why I'm collecting these use cases and seeing which things we can't do. As that's how we figure out what we need next.
And with grid you can fake that sort of layout by spanning rows, so you can get the look of that - just not with auto-placement. I've got a bit of that going on here: http://codepen.io/rachelandrew/pen/QKwvxJ
So if you know how many columns you have you can do quite a lot by spanning different numbers of rows.
thanks a lot for the response - sad to hear we still can't do this layout with just CSS.
I think CSS columns come closest to it, but the content goes top to bottom rather than left to right. I wonder if maybe a direction on CSS columns would be something worth thinking about.
Thanks again!
It does seem like CSS columns is the closest thing. In the current design of CSS-Tricks, I went for columns because I wanted to give the cards/masonry look a go, but keeping it as simple as possible. By far the biggest complaint is the up-down chronological-ness rather than left-right. Maybe someday column-direction
can be a thing, or something.
I'm not sure I like column-direction
as a column kinda needs to be ... a column :D especially once you add to the mix the fact that not all languages run left to right and top to bottom.
However I do think this kind of layout needs to be a thing in CSS so these ideas and use cases are excellent and very useful.
Here's a pen doing it with flexbox and only 12 lines of javascript. I know the idea was not using javascript, but I think 12 lines is a small price to pay.
@inorganik, even tough I do like jQuery, you cannot say it's 12 line of js
@abusedmedia I removed the jQuery, now it's 13 lines 😃
@programmer5000-com I think you misunderstand what this is. This is just a Q&A about grid, the Issue is closed because the original Q was answered. Linked in the thread is an issue on the CSS WG drafts if you wanted to follow that.
yes, column-direction
please.
yes, different item-height allowed in same row please.
You can check this : http://w3bits.com/css-masonry/. Works like a charm for me
@champramentio CSS3 grid was created for static lists, like the link you gave. When new items are added into the list(like paginations), it will fail.
Here's @inorganik's solution rewritten in modern JS (shaves off 4 lines, down to 9):
const numCols = 3
const colHeights = Array(numCols).fill(0)
const container = document.getElementById('container')
Array.from(container.children).forEach((child, i) => {
const order = i % numCols
child.style.order = order
colHeights[order] += parseFloat(child.clientHeight)
})
container.style.height = Math.max(...colHeights) + 'px'
Using this approach I had a lot of trouble with the flex
items not respecting the parent container width, however, so I really wanted to make it happen with grid
instead. If you're using react
and styled-components
, the suggested solution by @rachelandrew, i.e. having lots of small rows and managing how many each grid item spans, is quite easy to implement:
// masonry/index.js
import React, { Component, createRef } from 'react'
import { Parent, Child } from './styles'
export default class Masonry extends Component {
static defaultProps = {
rowHeight: 40, // in pixels
colWidth: `17em`,
}
state = { spans: [] }
ref: createRef()
// sums up the heights of all child nodes for each grid item
sumUp = (acc, node) => acc + node.scrollHeight
computeSpans = () => {
const { rowHeight } = this.props
const spans = []
Array.from(this.ref.current.children).forEach(child => {
const childHeight = Array.from(child.children).reduce(this.sumUp, 0)
const span = Math.ceil(childHeight / rowHeight)
spans.push(span + 1)
child.style.height = span * rowHeight + `px`
})
this.setState({ spans })
}
componentDidMount() {
this.computeSpans()
window.addEventListener('resize', this.computeSpans)
}
componentWillUnmount() {
window.removeEventListener('resize', this.computeSpans)
}
render() {
return (
<Parent ref={this.ref} {...this.props}>
{this.props.children.map((child, i) => (
<Child key={i} span={this.state.spans[i]}>
{child}
</Child>
))}
</Parent>
)
}
}
and the styled components:
// masonry/styles.js
import styled from 'styled-components'
export const Parent = styled.div`
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(${props => props.colWidth}, 1fr)
);
grid-auto-rows: calc(${props => props.rowHeight}px - 2em);
grid-gap: 2em;
`
export const Child = styled.div`
grid-row: span ${props => props.span};
height: max-content;
`
Then simply use it like so:
// blog.js
import React from 'react'
import Masonry from './masonry'
import Post from './post'
const Blog = ({ posts }) => (
<Masonry>
{posts.map(post => (
<Post key={post.slug} {...post} />
))}
</Masonry>
)
The above was copied here from this blog post.
Just for reference, it's useful not only for building masonry tiles https://github.com/w3c/csswg-drafts/issues/1183
For anyone using Vue, this plugin takes about a minute
column-count, but you need to sort the data.
W3C draft: https://drafts.csswg.org/css-grid-3/
here's an example of this using React to manage the column rendering.
Thank me later😋 https://www.smashingmagazine.com/native-css-masonry-layout-css-grid/
first- thanks so much for your work on everything grid!
I'm looking to make a layout like this.
Is this possible with grid? Rather than have the row's items stretch to the height of the largest one, I'd like the one underneath it to come up. I've looked at all your examples and I've modified this one to have one high item:
http://codepen.io/wesbos/pen/LxRaye