You might not need Javascript for that: Netflix Style Hover Effects

Update: A new version of this post is available on CSS Tricks!

My introduction to web development came from Free Code Camp, a platform that teaches students the fundamentals of HTML, CSS, and Javascript through a number of exercises and projects. Through the completion of these challenges, campers can earn certificates in Front End Development, Data Visualization, and Backend Development. Although people join FCC for many different reasons, a large portion are looking to gain a job in web development and the forums feature many success stories in this endeavour.

I became a professional developer after completing the Front End Certificate, but still work on many of the projects to sharpen my skills. Since I am not overly concerned with the certificates, I follow the project user stories only loosely – often adding or removing features as I see fit. I added a backend component to the Wikipedia Viewer Project,  and elected to complete the Simon, Game of Life, and Voting App projects with Vue instead of React.

I am currently working on the Book Trading Club project, and have decided to create a carousel to display the books that are available to be traded. Specifically, I would like to (mostly) replicate the Netflix slider, and have been searching for ways to do so.

A Note on Carousels

As many UI experts will tell you, carousels are a notoriously underused component on the web – users just don’t click on them very often. In addition to this, they are hard to get right. They are often buggy, and require special care for mobile. Even my favourite slider library, Slick, comes with its own list of problems – it requires jQuery as a dependency, and has issues playing nice with bundlers like Browserify and Webpack.

Despite all these issues, Netflix has successfully integrated the carousel as the core component of their browse page (perhaps one of the most clicked pages on the web?), and I think that it looks great.

The Challenge

The Netflix slider is no ordinary slider, and offers a lot of functionality when the user hovers over a slide. Specifically, it enlarges the hovered slide and pushes the neighbouring items outward. The purpose of this post is to demonstrate how to create this effect without the need of Javascript (although the carousel itself will be JS dependent).

A Trivial Solution

This was my first attempt:


The idea here is to take advantage of Flexbox, which will automatically resize sibling elements when the hovered slide grows to width: 150%; Setting align-items: center; on the parent will prevent each item from stretching to equal heights, but the height of the parent itself still grows to fit the hovered item, creating unwanted vertical movement.

Another problem with this approach is that it relies on animating width, which is generally less performant than animating transforms.

Finally, this doesn’t actually replicate the Netflix UI properly. Under close inspection you will notice that the Netflix slider row lengthens when a single item grows, and the other slides are allowed to move outside of the viewport.


A Better Solution

If the goal is to animate transform instead of width than the sibling items will need to be moved manually. Additionally, there needs to be a way to push items that come before the hovered slide leftward, and items that come after the hovered slide rightward.

At first glance this seems impossible given the behaviour of sibling selectors in CSS – only elements that come after the selected in the DOM can be accessed. For example, the code .box:hover ~ .box will only select elements on the right of the target slide. How do we move elements on the left?

The answer is subtraction. By creating another selector that targets all of the items inside the parent container, we can move those items left, overwrite the sibling’s of the hovered element to move right, and set the target element itself to scale.

The math of the operation is straight forward. A scaling of 150% will cause an expansion of an extra 50% horizontally, or 25% on either side. The translation needs to match this. Here is the code of everything brought together.


Code Savings

Generally speaking, I dislike handling hover states in Javascript because they typically require a lot of code to implement. Unlike a click or scroll event, a hover event probably needs more than one callback to function. A class may be added for a mouseenter event, which needs to be removed for a subsequent mouseleave event. CSS based hover events can really simplify codebases.