Cartoon style illustration of Addy, young man with long blonde hair in a blue hoodie
addy.zone
digital garden 🌱

“Bento box” responsive grid interactive image gallery

This fun “bento box” style gallery is made in pure CSS, made possible by the new :has() selector!

See the Pen Untitled by Addy (@addy-codes) on CodePen.

When I first tried to build this out, I was set on a CSS grid based solution. However, there didn’t seem to be an effective way to dynamically break the images out or adjust it on the fly to give the desired hover expand effect.

In the end I settled on more absolute positioning, but using percentage widths it is still perfectly responsive. If you have a smarter solution I’d love to know in the comments! 😄

Grab the code

<div class="image-grid">
    <div class="image-wrapper image-1">
        <img src="https://placecats.com/800/600/" alt="Image 1">
    </div>
    <div class="image-wrapper image-2">
        <img src="https://placecats.com/800/700/" alt="Image 2">
    </div>
    <div class="image-wrapper image-3">
        <img src="https://placecats.com/1000/600/" alt="Image 3">
    </div>
    <div class="image-wrapper image-4">
        <img src="https://placecats.com/900/600/" alt="Image 4">
    </div>
    <div class="image-wrapper image-5">
        <img src="https://placecats.com/800/500/" alt="Image 5">
    </div>
</div>
.image-grid {
    margin: 0 auto;
    position: relative;
    width: 100%;
    aspect-ratio: 3 / 2;
    background: #fff;
    overflow: hidden;

    .image-wrapper {
        position: absolute;
        border-radius: 0.5rem;
        overflow: hidden;
        transition: all 0.4s ease;
        z-index: 1;

        img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            border-radius: 0.5rem;
            transition: all 0.4s ease;
        }
    }

    .image-1 { left: 0%; top: 0%; width: 31.5%; height: 30.5%; }
    .image-2 { left: 0%; top: 34.5%; width: 31.5%; height: 65%; }
    .image-3 { left: calc(31.5% + 2.7%); top: 0%; width: 31.5%; height: 100%; }
    .image-4 { left: calc(31.5% + 2.7% + 31.5% + 2.7%); top: 0%; width: 31.5%; height: 65%; }
    .image-5 { left: 68.4%; top: 69%; width: 31.5%; height: 30.5%; }

    &:has(.image-wrapper:hover) {
        .image-wrapper {
            transform: scale(0.1);
            opacity: 0.3;
            pointer-events: none;

            &:hover {
                transform: none;
                opacity: 1;
                pointer-events: auto;
                z-index: 10;
                left: 0; top: 0; width: 100%; height: 100%;
            }
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

twenty − 17 =