I'm working on a web layout where I have a section containing visible and hidden content. I've implemented a button to expand the hidden content smoothly using a transition effect. However, I'm facing two main issues:
While the expandable functionality works, the transition effect isn't as smooth as I'd like it to be. I've applied a CSS transition to change the grid-template-rows property, but it's not providing the desired smoothness. How can I achieve a smoother transition effect when expanding the hidden content?
Grid Row-Gap Reservation: I'm using a grid layout for the hidden content, and even when elements are hidden, the row-gap seems to be reserving extra space for them, causing a visual gap in the layout. I want to prevent this row-gap reservation for hidden elements. How can I modify my CSS to ensure that the row-gap is only applied to visible elements and not to hidden ones?
Here's a simplified version of my HTML, CSS, and JavaScript code: https://codepen.io/korneliuszburian/pen/LYaqyvN
document.addEventListener("DOMContentLoaded", () => {
const button = document.querySelector(".btn__expand");
let currentIndex = 0;
let hiddenGroups = document.querySelectorAll(".cards--hidden");
button.addEventListener("click", function() {
if (currentIndex < hiddenGroups.length) {
hiddenGroups[currentIndex].classList.remove("cards--hidden");
hiddenGroups[currentIndex].classList.add("cards--active");
currentIndex++;
}
if (currentIndex >= hiddenGroups.length) {
button.style.display = "none";
}
});
});
body {
background-color: #f0f0f0;
font-family: Arial, sans-serif;
padding: 20px;
}
.visible,
.cards--hidden {
margin-bottom: 20px;
padding: 15px;
background-color: #fff;
border: 1px solid #000;
box-shadow: 5px 5px 0px #000;
}
.wrapper {
border: 2px solid #000;
padding: 10px;
min-height: 0;
overflow: hidden;
}
.items {
margin-bottom: 10px;
padding: 10px;
background-color: #e0e0e0;
border: 1px solid #000;
}
.item__title h2 {
font-size: 20px;
margin: 0 0 5px 0;
color: #000;
}
.item__title p {
font-size: 16px;
margin: 0;
color: #333;
}
.cards--hidden {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 300ms ease-in-out;
overflow: hidden;
}
.cards--active {
grid-template-rows: 1fr;
}
.btn__expand {
cursor: pointer;
}
<section>
<div class="visible">
<div class="wrapper">
<div class="items">
<div class="item__title">
<h2>Visible Item 1 Title</h2>
<p>Description for Visible Item 1</p>
</div>
</div>
<div class="items">
<h2>Visible Item 2 Title</h2>
<p>Description for Visible Item 2</p>
</div>
<div class="items">
<h2>Visible Item 3 Title</h2>
<p>Description for Visible Item 3</p>
</div>
</div>
</div>
<div class="cards cards--hidden">
<div class="wrapper">
<div class="items">
<div class="item__title">
<h2>Hidden Item 1 Title</h2>
<p>Description for Hidden Item 1</p>
</div>
</div>
<div class="items">
<h2>Hidden Item 2 Title</h2>
<p>Description for Hidden Item 2</p>
</div>
<div class="items">
<h2>Hidden Item 3 Title</h2>
<p>Description for Hidden Item 3</p>
</div>
</div>
</div>
<div class="cards cards--hidden">
<div class="wrapper">
<div class="items">
<div class="item__title">
<h2>Hidden Item 1 Title</h2>
<p>Description for Hidden Item 1</p>
</div>
</div>
<div class="items">
<h2>Hidden Item 2 Title</h2>
<p>Description for Hidden Item 2</p>
</div>
<div class="items">
<h2>Hidden Item 3 Title</h2>
<p>Description for Hidden Item 3</p>
</div>
</div>
</div>
<div class="btn__expand">
<span class="btn__label">Expand all data</span>
</div>
</section>
You only added display: grid
to .cards--hidden
, while it changes to .cards--visible
, the container becomes block
, so grid-template-rows: 1fr
losts its effect and its transition is also lost.
You could move these rules to the general selector .cards
, to allow the transition work as intended
.cards {
display: grid;
transition: grid-template-rows 300ms ease-in-out;
}
document.addEventListener("DOMContentLoaded", () => {
const button = document.querySelector(".btn__expand");
let currentIndex = 0;
let hiddenGroups = document.querySelectorAll(".cards--hidden");
button.addEventListener("click", function() {
if (currentIndex < hiddenGroups.length) {
hiddenGroups[currentIndex].classList.remove("cards--hidden");
hiddenGroups[currentIndex].classList.add("cards--active");
currentIndex++;
}
if (currentIndex >= hiddenGroups.length) {
button.style.display = "none";
}
});
});
body {
background-color: #f0f0f0;
font-family: Arial, sans-serif;
padding: 20px;
}
.visible,
.cards--hidden {
padding: 15px;
background-color: #fff;
border: 1px solid #000;
box-shadow: 5px 5px 0px #000;
}
.wrapper {
border: 2px solid #000;
padding: 10px;
min-height: 0;
overflow: hidden;
}
.items {
margin-bottom: 10px;
padding: 10px;
background-color: #e0e0e0;
border: 1px solid #000;
}
.item__title h2 {
font-size: 20px;
margin: 0 0 5px 0;
color: #000;
}
.item__title p {
font-size: 16px;
margin: 0;
color: #333;
}
.cards {
display: grid;
transition: grid-template-rows 300ms ease-in-out;
}
.cards--hidden {
grid-template-rows: 0fr;
overflow: hidden;
}
.btn__expand {
cursor: pointer;
margin-top: 20px;
}
/* hide the wrapper content */
.cards--hidden .wrapper {
padding: 0;
border: none;
}
/* add gap to visible items */
.visible, .cards--active {
grid-template-rows: 1fr;
margin-bottom: 20px;
}
<section>
<div class="visible">
<div class="wrapper">
<div class="items">
<div class="item__title">
<h2>Visible Item 1 Title</h2>
<p>Description for Visible Item 1</p>
</div>
</div>
<div class="items">
<h2>Visible Item 2 Title</h2>
<p>Description for Visible Item 2</p>
</div>
<div class="items">
<h2>Visible Item 3 Title</h2>
<p>Description for Visible Item 3</p>
</div>
</div>
</div>
<div class="cards cards--hidden">
<div class="wrapper">
<div class="items">
<div class="item__title">
<h2>Hidden Item 1 Title</h2>
<p>Description for Hidden Item 1</p>
</div>
</div>
<div class="items">
<h2>Hidden Item 2 Title</h2>
<p>Description for Hidden Item 2</p>
</div>
<div class="items">
<h2>Hidden Item 3 Title</h2>
<p>Description for Hidden Item 3</p>
</div>
</div>
</div>
<div class="cards cards--hidden">
<div class="wrapper">
<div class="items">
<div class="item__title">
<h2>Hidden Item 1 Title</h2>
<p>Description for Hidden Item 1</p>
</div>
</div>
<div class="items">
<h2>Hidden Item 2 Title</h2>
<p>Description for Hidden Item 2</p>
</div>
<div class="items">
<h2>Hidden Item 3 Title</h2>
<p>Description for Hidden Item 3</p>
</div>
</div>
</div>
<div class="btn__expand">
<span class="btn__label">Expand all data</span>
</div>
</section>