Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS Grid auto height of elements? (Pinterest Layout) #19

Closed
wesbos opened this issue Jan 16, 2017 · 23 comments
Closed

CSS Grid auto height of elements? (Pinterest Layout) #19

wesbos opened this issue Jan 16, 2017 · 23 comments

Comments

@wesbos
Copy link

wesbos commented Jan 16, 2017

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

@rachelandrew
Copy link
Owner

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.

@keithjgrant
Copy link

keithjgrant commented Jan 16, 2017

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

@rachelandrew
Copy link
Owner

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.

@rachelandrew
Copy link
Owner

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.

@wesbos
Copy link
Author

wesbos commented Jan 16, 2017

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!

@wesbos wesbos closed this as completed Jan 16, 2017
@chriscoyier
Copy link

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.

@wesbos wesbos changed the title CSS Grid auto height of elements? CSS Grid auto height of elements? (Pinterest Layout) Jan 16, 2017
@rachelandrew
Copy link
Owner

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.

@inorganik
Copy link

inorganik commented Jan 16, 2017

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.

@abusedmedia
Copy link

@inorganik, even tough I do like jQuery, you cannot say it's 12 line of js

@inorganik
Copy link

@abusedmedia I removed the jQuery, now it's 13 lines 😃

@rachelandrew
Copy link
Owner

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

@luck2011
Copy link

luck2011 commented Nov 4, 2017

yes, column-direction please.

@carolineportugal
Copy link

yes, different item-height allowed in same row please.

@champramentio
Copy link

You can check this : http://w3bits.com/css-masonry/. Works like a charm for me

@luck2011
Copy link

luck2011 commented Apr 19, 2018

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

@janosh
Copy link

janosh commented Nov 14, 2018

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.

@klimashkin
Copy link

Just for reference, it's useful not only for building masonry tiles
w3c/csswg-drafts#1183

@xlcrr
Copy link

xlcrr commented Feb 27, 2019

For anyone using Vue, this plugin takes about a minute

https://github.com/paulcollett/vue-masonry-css

@Slooo
Copy link

Slooo commented May 21, 2020

column-count, but you need to sort the data.

https://codepen.io/slooo/pen/yLYZNKw

@kidwm
Copy link

kidwm commented Jul 3, 2020

FYI,
w3c/csswg-drafts#4650

@tymmesyde
Copy link

W3C draft: https://drafts.csswg.org/css-grid-3/

@tonypee
Copy link

tonypee commented Mar 5, 2024

here's an example of this using React to manage the column rendering.

  • auto calculates the number of columns (you can resize the window)
  • allows variable height
  • fills the empty rows where your content is not divisible by the col count - unlike the other example

https://stackblitz.com/edit/vitejs-vite-r9gmdc?file=src%2Fmain.tsx,src%2FApp.tsx,src%2FApp.css&terminal=dev

@aayushsingh7
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests