22 CSS Image Galleries12 / 22

Pure CSSMIT licensed

CSS Accordion Image Gallery

A compact row of vertical image strips that smoothly expand on hover or keyboard focus while their neighbours shrink to make room — the space-saving accordion gallery portfolios and hero sections search for.

Published Updated

Live Demo
Try it

The code

<section class="gl-08" aria-label="Accordion image gallery">
  <div class="gl-08__acc">
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/mountain?lock=801')"><span class="gl-08__lbl">Peaks</span></button>
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/forest?lock=802')"><span class="gl-08__lbl">Forests</span></button>
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/ocean?lock=803')"><span class="gl-08__lbl">Coast</span></button>
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/desert?lock=804')"><span class="gl-08__lbl">Desert</span></button>
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/city?lock=805')"><span class="gl-08__lbl">Cities</span></button>
  </div>
</section>
.gl-08,
.gl-08 *,
.gl-08 *::before,
.gl-08 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.gl-08 ::selection {
  background: oklch(0.62 0.16 20);
  color: #fff;
}

.gl-08 {
  --accent: oklch(0.62 0.16 20);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  padding: 40px 20px;
  background: #14110f;
  display: flex;
  align-items: center;
  justify-content: center;
}

.gl-08__acc {
  display: flex;
  gap: 8px;
  width: min(880px,100%);
  height: min(440px,60vh);
}

.gl-08__panel {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  border: none;
  cursor: pointer;
  border-radius: 12px;
  overflow: hidden;
  padding: 0;
  background: var(--img) center/cover no-repeat;
  filter: brightness(.72) saturate(.95);
  transition: flex-grow .5s cubic-bezier(.2,.7,.2,1),filter .4s;
}

.gl-08__panel:hover,
.gl-08__panel:focus {
  flex-grow: 4;
  filter: brightness(1) saturate(1.05);
}

.gl-08__panel::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg,rgba(10,6,4,.7),transparent 55%);
}

.gl-08__lbl {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 16px;
  z-index: 2;
  text-align: center;
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .06em;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  left: auto;
  right: 12px;
  bottom: auto;
  top: 16px;
  text-shadow: 0 1px 4px rgba(0,0,0,.7);
  transition: opacity .4s;
}

.gl-08__panel:hover .gl-08__lbl,
.gl-08__panel:focus .gl-08__lbl {
  writing-mode: horizontal-tb;
  transform: none;
  top: auto;
  bottom: 16px;
  left: 0;
  right: 0;
  font-size: 18px;
}

.gl-08__panel:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .gl-08__panel {
    transition: none;
  }
}
/* No JavaScript — flex-grow rises on :hover/:focus so the focused strip expands. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Image Gallerie from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Accordion Image Gallery
Source: https://codefronts.com/snippets/css-image-gallery/css-accordion-image-gallery/

A compact row of vertical image strips that smoothly expand on hover or keyboard focus while their neighbours shrink to make room — the space-saving accordion gallery portfolios and hero sections search for.
## HTML
```html
<section class="gl-08" aria-label="Accordion image gallery">
  <div class="gl-08__acc">
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/mountain?lock=801')"><span class="gl-08__lbl">Peaks</span></button>
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/forest?lock=802')"><span class="gl-08__lbl">Forests</span></button>
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/ocean?lock=803')"><span class="gl-08__lbl">Coast</span></button>
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/desert?lock=804')"><span class="gl-08__lbl">Desert</span></button>
    <button class="gl-08__panel" type="button" style="--img:url('https://loremflickr.com/700/900/city?lock=805')"><span class="gl-08__lbl">Cities</span></button>
  </div>
</section>
```
## CSS
```css
.gl-08,
.gl-08 *,
.gl-08 *::before,
.gl-08 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.gl-08 ::selection {
  background: oklch(0.62 0.16 20);
  color: #fff;
}

.gl-08 {
  --accent: oklch(0.62 0.16 20);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  padding: 40px 20px;
  background: #14110f;
  display: flex;
  align-items: center;
  justify-content: center;
}

.gl-08__acc {
  display: flex;
  gap: 8px;
  width: min(880px,100%);
  height: min(440px,60vh);
}

.gl-08__panel {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  border: none;
  cursor: pointer;
  border-radius: 12px;
  overflow: hidden;
  padding: 0;
  background: var(--img) center/cover no-repeat;
  filter: brightness(.72) saturate(.95);
  transition: flex-grow .5s cubic-bezier(.2,.7,.2,1),filter .4s;
}

.gl-08__panel:hover,
.gl-08__panel:focus {
  flex-grow: 4;
  filter: brightness(1) saturate(1.05);
}

.gl-08__panel::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg,rgba(10,6,4,.7),transparent 55%);
}

.gl-08__lbl {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 16px;
  z-index: 2;
  text-align: center;
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .06em;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  left: auto;
  right: 12px;
  bottom: auto;
  top: 16px;
  text-shadow: 0 1px 4px rgba(0,0,0,.7);
  transition: opacity .4s;
}

.gl-08__panel:hover .gl-08__lbl,
.gl-08__panel:focus .gl-08__lbl {
  writing-mode: horizontal-tb;
  transform: none;
  top: auto;
  bottom: 16px;
  left: 0;
  right: 0;
  font-size: 18px;
}

.gl-08__panel:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .gl-08__panel {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — flex-grow rises on :hover/:focus so the focused strip expands. */
```

How this works

The gallery is a flex row of equal panels (flex: 1). Each panel is a real <button> with a background image; on :hover and :focus its flex-grow rises so it widens while the siblings absorb the loss and narrow. A label pinned to the bottom fades and slides in as the panel opens.

Using a focusable <button> per panel means the accordion opens on keyboard focus exactly as it does on hover, so it's fully operable without a pointer.

Make it yours

  • Set the open width via the hovered panel's flex-grow (e.g. 1 → 4 for a bigger reveal).
  • Make it vertical by switching the row to flex-direction: column and expanding height.
  • Swap background images for inner <img>s if you need real alt text per panel.
  • Ease the open/close feel with the flex-grow transition timing.

Gotchas — read before shipping

  • Transitioning flex-grow is the canonical accordion technique but it does animate layout — keep panel counts modest and the easing short so it stays smooth.
  • Ensure the collapsed state still shows enough of each image/label to be recognisable and clickable.
  • Give each panel an accessible name (button text or aria-label) since background images have no alt.

Browser support

ChromeSafariFirefoxEdge
111+15.4+113+111+

Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome 29+.

Flexbox flex-grow transitions — supported across all current browsers.

Techniques used in this demo

Search CodeFronts

Loading…