CSS only accordion grid

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.

The ’accordion grid’ element

As simple as it looks it ended up being more complicated to implement than I though, specially because I needed it to stack vertically on smaller screens. Every solution I had in mind involved some crazy amount of CSS and flexbox hacks or even worse having to rely on Javascript to listen to the window’s 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.

Initial layout

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.

Mobile first look of the ‘accordion grid’

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 fr unit.

Almost there…

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:

No change, what?

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

That’s more like it 🙂

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.

Our finished product

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.


That’s it, I’m surprised by how much you can do with only 4 lines of CSS grids and no javascript, really looking forward to using them again! 👋🏽