Recently I had to work on a relatively simple piece of UI that I’ve seen many times around the web. In essence it’s a ‘grid’ of items that can be clicked to expand a full width section beneath them with more content.
I couldn’t find a name for this kind of elements so, for the sake of this post, I’ll name it ‘accordion grid’ but please let me know in the comments if there’s a more widely known way to call it.
onresize event to position each box in the right place. However, I was convinced that a CSS only option was out there…
Enter CSS Grid
CSS Grid is a new way to manipulate the way content is laid out in our websites, unlike floats, flexbox or tables, it gives us full control over rows and columns without forcing us to create extra markup just to change the way they look so it sounds like a good candidate to solve the ‘accordion grid’ problem.
Support is pretty good across all modern browsers so it was a perfect opportunity to try it out.
We’ll start with a mobile first approach, each grid item will be stacked on top of its content on smaller screens. There’s nothing special about the CSS at this point, only that I’m using the
:focus pseudo-selector to animate each section, but so far it looks like a regular accordion.
Spicing it up with CSS Grids
Now the fun part, on larger screens I want to enable the ‘grid accordion’ so we can start by defining the parent
.grid element as
display: grid. Also, I want to show 3 items per row which is a good use case for the new
As you can see the
fr unit combined with the
grid-template-column property will split our grid into equally sized sections, which is exactly what I wanted, however it’s placing all child elements (grid items and their description) next to each other. What I want is for all descriptions to span the full width of the grid right below their items.
Luckily CSS Grids make this type of layouts pretty easy, we can use the
grid-template-areas property to create a custom grid layout and name each section to our liking, here’s the trick though: if we give sections the same name we can make elements span multiple grid items, here’s how the ‘accordion grid’ template areas would look like:
grid-template-areas has a very ASCII-ish look to it, you can almost see it as a ‘blueprint’ of sorts of your grid layout. The ‘grid accordion’ looks exactly the same though, this is because by default grid items will be placed next to each other from left to right so there’s no change in this case, but we can specify what elements we want to place in each section, no matter their order in the DOM.
Now, we can use the
grid-area property to specify what section a specific item can be placed in, here I’m telling the grid to place any
.grid__description element in the
desc area, and since we gave the bottom 3 grid areas this same name, it will span the full width of the grid.
Also since all descriptions have the same class name they’re technically placed on top of each other, kind of like when using
position: absolute, with the added benefit that it doesn’t take them out of the document flow so surrounding elements will react to changes in their size. We can also add multiple rows and everything should work as expected.
I’m sure there are ways to improve this, specially to make it more accessible friendly, but it’s a good starting point to iterate upon.