Animated Grid Tracks with :has()

Grid of four segments with brightly coloured backgrounds and cupcake photo in the first section
Screenshot from a Codepen demo. Hovering on a grid item expands the grid tracks.

Somehow it’s already three months since I wrote about the CSS :has() pseudo-class landing in Safari Technology preview. The latest Safari (proper) release has just shipped with support, and can be enabled with a flag in the next Chrome release.

Meanwhile, I’ve been playing around with some ideas on how we can use :has() for styling a gallery or product grid layout. One of the cool things about the selector is that it allows us to not only style a parent element based on any one of its children, but to also style that child’s siblings too. So, building a menu, we could apply some styles to other elements in the menu when one of the links is hovered:

/* Style the list border when link is hovered */
ul:has(a:hover) {
border: 2px solid whitesmoke;
}

/* Style any other links in the list when a link is hovered */
ul:has(a:hover) a:not(:hover) {
opacity: 0.5;
}
Menu bar with item hovered
Menu bar in its original state, and with item hovered

(See the demo)

That extends nicely to a grid design, and combined with animated grid tracks could make for some exciting layout possibilities. What if, when the user hovers on a grid item, we could change the size of our grid tracks?

The ability to animate grid tracks (grid-template-rows and grid-template-columns) is actually written into the CSS Grid specification, and has been supported in Firefox since 2019. But, sad to say, no other browsers currently support grid track animation. Chen Hui-Jing wrote about it at the time, and included a cool demo by Olivia Ng of an expanding menu bar:

See the Pen CSS Grid: In-flight Entertainment Screen by Olivia Ng (@oliviale) on CodePen.

But perhaps one of the reasons why we didn’t see more people playing around with animated grid tracks at the time (aside from the limited browser support) was because one of the more obvious use cases is expanding a grid track when hovering on an item within the grid. Until :has() arrived, we didn’t have a way to style the grid based on the hover state of a child — we would have to use Javascript.

This demo combines animated grid tracks with the :has() selector. When you hover over a grid item, the item’s grid tracks expand to take up a greater proportion of the space, and the labels of the sibling items fade out. You’ll need to view it in Safari to see this in action — in other browsers you’ll still see a hover effect on the item you’re hovering, just not on the grid and the item’s siblings.

See the Pen grid / :has + animated grid tracks (Safari only) by Michelle Barker (@michellebarker) on CodePen.

Polyfilling

Unfortunately, although :has() is supported in Safari, animated grid tracks are not. To get around this in the above demo, I’m using the Greensock library to animate custom properties for the grid tracks. It’s not perfect — to be honest, I’ve gone for a minimal-code outcome — but hopefully illustrates what’s possible while we cross our fingers for wider support!