Animating a CSS Grid template to create a case carousel

Did you know you can animate CSS Grid layouts? In this video, we'll show you how to create a case carousel using CSS Grid and CSS animations.

🧑‍💻 🎥
... Almost ready!

In CSS not all properties are animatable. Many of them swap at exactly 50% of the animation duration. These properties are called discrete properties. One of these properties that might seem hard to animate, are the CSS template layouts. But don’t be deceived — animating CSS Grid layouts is indeed possible! Today, we’ll explore how to do just that.

Creating our first CSS Grid layout

In this tutorial we will be combining CSS grid layouts, and animating them based on whether a child element is hovered, by using the css :has() pseudo selector. Using this technique we will create a basic case carousel.

In order to understand the basics of this animation, we define our grid first.

.wrapper {
display: grid;
gap: 12px;
grid-template-columns: 4fr 1fr 1fr;
}
.slide {
background: rgba(255, 255, 255, 0.3);
border-radius: 24px;
}
<div class="wrapper">
<div class="slide"></div>
<div class="slide"></div>
<div class="slide"></div>
</div>

By defining a grid with grid-template-columns set to 4fr 1fr 1fr, we’re creating a grid with 3 columns. The first column will take up 4 times the space of the other two columns. This is our default active slide.

Changing the parent when children are hovered

If we want to change the columns when a child is hovered, we can use the :has() pseudo selector. A new selector which already is supported in all major browsers!

In essence it works as follows.

.wrapper:has(.slide:hover) {
background-color: red;
}

Inside the parenthesis of a :has() selector, you can define any valid CSS selector. The browser will then test to see if that element is present within the element that you added the :has() selector to. If it is, the styles will be applied to the element that has the :has selector — in this case .wrapper.

That is why you see the background turn red when you hover any of its children, but not when you hover the gap between the elements. At that moment there isn’t any child that is hovered, thus the background does not turn red.

Removing the gap

As you noticed in the example above, the :has() selector won’t be triggered when you hover the gap. Meaning that if we would expand a certain slide, and then move over the gap, it would quickly collapse and you would might suddenly hover a different child.

Let’s take a look at what that weird behavior would look like if the gap was a bit wider. Try moving your cursor from the third child to the second. You’ll probably end up showing the first child instead.

To show the correct slide, we need to remove the gap and solve this with padding instead.

.wrapper {
display: grid;
grid-template-columns: 4fr 1fr 1fr;
}
.slide {
padding: 0 12px;
}
.slide .content {
background: rgba(255, 255, 255, 0.3);
border-radius: 24px;
}
.wrapper:has(.slide:hover) {
background-color: red;
}
<div class="wrapper">
<div class="slide">
<div class="content"></div>
</div>
<div class="slide">
<div class="content"></div>
</div>
<div class="slide">
<div class="content"></div>
</div>
</div>

Animating the grid template

You notice when you leave the slide’s .content div now, you’re actually still hovering the .slide div, meaning we keep seeing the hover state.

Now we know how we can change the parent’s style when hovering a child, we can also change the template columns instead of the background color.

.wrapper {
display: grid;
11 collapsed lines
grid-template-columns: 4fr 1fr 1fr;
}
.slide {
padding: 0 12px;
}
.slide .content {
background: rgba(255, 255, 255, 0.3);
border-radius: 24px;
}
.wrapper:has(.slide:nth-child(1):hover) {
grid-template-columns: 4fr 1fr 1fr;
}
.wrapper:has(.slide:nth-child(2):hover) {
grid-template-columns: 1fr 4fr 1fr;
}
.wrapper:has(.slide:nth-child(3):hover) {
grid-template-columns: 1fr 1fr 4fr;
}

This way we are changing the layout depending on which child is hovered:

Animating that change.

The final missing piece is animating this grid change. And since the CSS grid layouts can be animated, it is as simple as adding a transition property to the .wrapper element.

.wrapper {
transition: grid-template-columns 800ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

Learning more

Of course this tutorial only touched on the basics of this grid animation. Make sure to watch the video at the top of this article for a more in depth explanation, as well as adding more features to this carousel!