Accordion-Style Image Galleries with CSS Flexbox

Category: Accordion , Gallery | June 30, 2025
Authordarko-komericki
Last UpdateJune 30, 2025
LicenseMIT
Views183 views
Accordion-Style Image Galleries with CSS Flexbox

This is a responsive gallery that transforms a series of image cards into an interactive, accordion-style layout where images smoothly expand to full width on hover.

Features:

  • Smooth flex-basis transitions using cubic-bezier timing functions
  • Responsive design that adapts to different container widths
  • Opacity-based visual hierarchy for focused attention
  • Event delegation for optimized JavaScript performance
  • Staggered margin positioning for visual depth
  • Mobile-friendly touch interaction support

Use Cases

  • Portfolio Galleries: A clean way for developers and designers to show project thumbnails. Hovering gives a larger, more detailed preview.
  • Product Feature Tours: An e-commerce site can display multiple product shots or highlight key features. A user can quickly scan them and hover to expand the one they’re interested in.
  • Team “About Us” Pages: You can use it to display headshots of team members. Hovering over a photo could expand the card to show their name, title, and social links.

How to use it:

1. Create a main container element with individual card elements for each image. Each card requires an inner wrapper and picture element to maintain proper aspect ratios and positioning:

<div class="container">
  <div class="card">
    <div class="card__inner">
      <picture>
          img src="your-image-url.jpg" alt="Description">
      </picture>
    </div>
  </div>
  <!-- Repeat card structure for additional images -->
</div>

2. Apply the necessary CSS styles to the container & cards. The container uses flexbox properties to distribute space equally among cards while enabling dynamic resizing during hover interactions:

.container {
  display: flex;
  flex: 1;
  max-width: 1440px;
  padding: 0 3rem;
  margin: 0 auto;
  overflow: auto;
  align-items: flex-start;
  justify-content: center;
}
.card {
  flex: 1 1 1%;
  position: relative;
  opacity: 0.2;
  transition: flex 600ms cubic-bezier(0.25, 1, 0.5, 1), opacity 250ms ease;
}
.card:hover {
  flex-basis: 30%;
}
.card__inner {
  margin: 0.25rem;
  background: #fff;
  border-radius: 8px;
  display: flex;
  justify-content: center;
  align-items: center;
  overflow: hidden;
}
.card picture {
  width: 100%;
  height: 0;
  padding-bottom: 600px;
  overflow: hidden;
  position: relative;
}
.card picture img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.card.is-active, .card:hover {
  opacity: 1;
}
.card:nth-child(2), .card:nth-child(5), .card:nth-child(9) {
  margin-top: 0;
}
.card:nth-child(2), .card:nth-child(4), .card:nth-child(6), .card:nth-child(8), .card:nth-child(10) {
  margin-top: 2.5%;
}
.card:nth-child(3), .card:nth-child(7) {
  margin-top: 5%;
}

3. The necessary JavaScript. When the mouse enters a card, the script identifies it and adds an .is-active class. At the same time, it removes that class from all other cards. The CSS rule .card { opacity: 0.2; } combined with .card.is-active, .card:hover { opacity: 1; } creates the “spotlight” where the active card is bright and the others are dimmed. When the mouse leaves, the script adds .is-active back to all cards.

const cards = document.querySelectorAll(".card");
// Add "is-active" class to all cards initially
cards.forEach(card => card.classList.add("is-active"));
// Use event delegation for better performance
document.addEventListener("mouseenter", event => {
  const card = event.target.closest(".card");
  if (card) {
    cards.forEach(c => c.classList.remove("is-active"));
    card.classList.add("is-active");
  }
}, true);
document.addEventListener("mouseleave", event => {
  const card = event.target.closest(".card");
  if (card) {
    cards.forEach(c => c.classList.add("is-active"));
  }
}, true);

You Might Be Interested In:


Leave a Reply