I have a responsive grid in which each item has its own details section, which is hidden by default. A click on the item adds a class to the details section to show it.
The HTML/CSS looks roughly like this:
<div class="grid">
<div class="item">Item 1</div>
<div class="details">Item 1 Details</div>
<!-- ... -->
<div class="item">Item n</div>
<div class="details">Item n Details</div>
</div>
.grid {
display: grid;
grid-auto-flow: dense;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.item {
/* nothing relevant */
}
.details {
grid-column: 1 / -1;
max-height: 0;
padding: 0;
opacity: 0;
overflow: hidden;
}
.details.selected {
max-height: unset;
padding: 1rem 2rem;
opacity: 1;
}
The details span an entire row, and the dense auto-flow allows other items to fill the gaps.
This works well and looks great with enough items.
However, let’s assume a width of 1000px
, meaning 5 items can fit next to each other. If there are only 1 or 2 items, there is too much empty space to their right. I would like to center these items, but with this current CSS, the grid fills in empty cells. This seems to be caused by grid-column: 1/ -1
.
So, this is how it currently looks with only 2 items:
This is how I would like it to look:
In both cases, with more than 5 items it would look like this, which is fine. I’d also be fine with the bottom row being centered too, in case there’s a flex solution.
How can I achieve this behavior?
It doesn’t need to be grid. Can be flex too, or really any other solution, if the dense
behavior can be achieved with it.
Somewhat related question, not my focus right now, but would be nice to have:
I would like to add a fade effect to the details, which is why it uses padding
, max-height
and opacity
instead of just setting it to display: none
. If one item is selected and I select another item in the same row, both details containers are displayed for the transition duration, which causes weird shifts in the grid layout.
JS solutions would be okay. It doesn’t need to be pure CSS.
2
Answers
I’m not sure to understand your goal…
This snippet gives you the lest layout provided, let me know if that’s not the good one…
The image with two items on one row resembles a flex box layout with the following property settings:
Depending on the width of the browser’s viewport, the layout when two elements are wrapped onto a row by themselves will resemble what you’re looking to achieve in the layout:
To get the expanding/collapsible
.details
element, wrap each.item
element in a wrapper and include within that wrapper the.details
element.The
position
of the.details
element is setabsolute
. Its placement would be relative to the grid element and not its parent.item-wrapper
in order to stretch the.details
across the width of the grid.The
.max-height
technique referenced below (or the manual setting of.details
height) can be applied to expand/collapse the.details
element.I’ve used
max-height
based on this Stack Overflow post to expand/collapse for my projects. It works well as a CSS-only solution. It doesn’t work well, as you’ve stated, in a responsive grid layout, causing "weird shifts".To get a smoother expand/collapse transition, the code snippet below manually calculates
height
of the content within the.details
element when the content height is not the same. Themax-height
CSS-only solution works well for.details
content that’s the same value set in a CSS style rule.The snippet uses
.scrollHeight
but like other properties that return an element’s dimension, it will force layout/reflow. This article explains more.CSS animation "lets the browser optimize performance and efficiency" versus multiple CSS transitions used in the snippet.
The solution relies on CSS variables to share values — that affect content height — between JavaScript and CSS style rules. I didn’t optimize the code but provides a minimum fade effect demo.