Originally posted in my WordPress blog

https://codepen.io/PseudoMon/pen/PoqeNGV

I have no idea what to call this thing. I saw something like this in a website today and just thought it looked neat. I have a couple of hours free, so I figured, it wouldn't hurt to try making it myself. The website where I saw this effect being used was slow and cumbersome, but for this one I try to make it as minimalistic as possible.

In case you're too lazy to run the pen and see it in action: it's a row of images, but you only see a vertical slice of each image. When you hover on an image, it'll widen so you can see see the full picture. The other slices will become tighter and slide away to give it enough space. It's kind of difficult to describe, but I think it looks really neat.

The HTML is straightforward; the CSS does most of the heavy lifting. The code is pretty clean, but here's a breakdown if you need it:

I use a couple of modern CSS technology for this trick: 1) Flexbox, 2) Object Fit, 3) Calculate, 4) Variable.

Flexbox

Setting the outer div with display: flex would ensure that the images will stay in one row. If you just leave it with the default block display, you can technically get the same effect, but some of the images might fall into the next line. It's just messy and unpredictable.

I tried making this with grid, at first, to take advantage of its fractional sizing. But grid doesn't work well if you want to have dynamic non-uniform grid size.

Object Fit

An image with the object-fit property can be treated like a background image i.e. it can use that loveliest of size setting: cover and contain. We use the former, which will cause the image to enlarge so it'll cover the entire surface it's assigned to, while keeping the aspect ratio and hiding the parts that go beyond that surface. contain does the opposite: it'll make the picture as small as it needs to be so the entire picture can be visible in the surface it's assigned to, even if it leaves some whitespaces.

By setting the image's height to 100% and the width to a slice (we'll get to that later) and then setting object-fit: cover, we'll have our image as slices in the gallery. When the image is hovered, its width will expand, allowing us to see the parts that were previously hidden.

Calculate

Calc() is a wonderful feature where instead of hard-coding every number you need, you can just let the browser calculate it. The usage here is pretty simple.

When left alone, each slice of image in the gallery will be set to have the same width, whatever that width may be. Since we have 6 images in the gallery, and we want the gallery to span the entire width, the width for each slide is 1/6 of the gallery width. So: each image's width is width: calc(100% / 6);

When an image slice is hovered, it'll widen so you can see the entire picture. Or, well, not quite the entire picture. Here I made it so instead of taking up 1/6 of the width, it takes up 35%, which is good enough for you to see the full image. Feel free to adjust that.

If one of the image is expanded, but all the other slices still have the same width, the gallery will expand beyond its allocated 100%, so we'll have to change that. The span that the other, unhovered, slices can work with is now 100% subtracted by the amount taken up by the hovered slice. So: calc(100% - 35%). They then have to divide it between the remaining 5 slices. The result: width: calc(calc(100% - 35%) / 5).

The JavaScript

I've tried making this a pure CSS trick, but alas, I cannot. If only there's a way to select the parent of an element that's hovered... but alas, there isn't yet.

The base class for the gallery is set for when none of the image is hovered i.e. with each image slice taking up a fraction of the gallery's width. And then there's the hovered subclass that will be applied whenever any of the image is hovered. It'll set each image's width to the calculation we've discussed above. The hovered image will have its own width that overrides this subclass.

We just need JavaScript to apply that subclass. The script creates an event listener for every image in the gallery. When any of them is hovered (mouseover), it'll apply the hovered subclass to the gallery. When the mouse leaves any of the image (mouseout), it'll take out the subclass, thus returning it to normal. But if the mouse leaves it only to hover over another image, the subclass will be applied again.

[code language="js"] posterSlideImgs.forEach(img => { img.addEventListener('mouseout', () => posterSlide.className = "poster-slide") }) posterSlideImgs.forEach(img => { img.addEventListener('mouseover', () => posterSlide.className = "poster-slide hovered") }) } [/code]

CSS Variable

This is technically not required, but I like it. It reduce the amount of hard-coding we do.

You'd notice that the number of images in the gallery will change the width of each slice. The code I've discussed so far assumes that we'll always have 6 images in the gallery, but this works just as well if you have 7 or 10 or 100, since we use calc() to calculate the width.

Instead of having to change that hard-coded number when I add new images or take out an existing one, I replaced the number of images with the variable --slice-n. So the width for the base image slice is width: calc(100% / var(--slice-n)).

When an image is hovered, our calculation changed. Taking out the hovered image out of the calculation, we're now dividing the width left with the number of images left. In our discussion so far, the number of images left is 5. Using the variable, it's calc(var(--slice-n) - 1). So the final width for each image slice is width: calc( calc(100% - 35%) / calc(var(--slice-n) - 1) ).

Note that I set the --slice-n variable just to the containing div. This way, you can have multiple galleries in one page, and each gallery will have its own --slice-n.

Mobile Considerations

It's a neat effect, but it's really only usable on devices that use a mouse, since it relies on hovering. If you use this on a responsive website, make sure you have an alternative for mobile users!

Credits

The images I used for this example are posters of anime movies released in Indonesia by Moxienotion. I figured that copyright-wise this constitutes a Fair Use.

I've only watched two of the films featured here, but anime is good. Watch anime, folks.