22 CSS Image Carousels08 / 22

Pure CSSMIT licensed

Portfolio Overlay Gallery

A case-study / portfolio rail where each project screenshot reveals its title and category on hover, sliding up from the bottom edge. The classic agency showcase — pure CSS transitions, keyboard-accessible via focus-within.

Published

Live Demo
Try it

The code

<section class="car-08" aria-label="Selected work">
  <ul class="car-08__rail" tabindex="0" role="group" aria-label="Portfolio projects, scrollable">
    <li class="car-08__card"><a class="car-08__link" href="#"><span class="car-08__frame"><img src="https://picsum.photos/seed/work-1/640/800" width="640" height="800" alt="Fintech dashboard redesign"></span><span class="car-08__cap"><span class="car-08__tag">Product</span><span class="car-08__title">Ledger — Fintech Dashboard</span></span></a></li>
    <li class="car-08__card"><a class="car-08__link" href="#"><span class="car-08__frame"><img src="https://picsum.photos/seed/work-2/640/800" width="640" height="800" alt="Coffee brand identity"></span><span class="car-08__cap"><span class="car-08__tag">Branding</span><span class="car-08__title">Cùrrent — Coffee Identity</span></span></a></li>
    <li class="car-08__card"><a class="car-08__link" href="#"><span class="car-08__frame"><img src="https://picsum.photos/seed/work-3/640/800" width="640" height="800" alt="Travel app case study"></span><span class="car-08__cap"><span class="car-08__tag">Mobile</span><span class="car-08__title">Wander — Travel App</span></span></a></li>
    <li class="car-08__card"><a class="car-08__link" href="#"><span class="car-08__frame"><img src="https://picsum.photos/seed/work-4/640/800" width="640" height="800" alt="E-commerce replatform"></span><span class="car-08__cap"><span class="car-08__tag">Web</span><span class="car-08__title">Maison — E-commerce</span></span></a></li>
  </ul>
</section>
.car-08,
.car-08 *,
.car-08 *::before,
.car-08 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.car-08 ::selection {
  background: #e6ff4d;
  color: #111;
}

.car-08 {
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  padding: 40px 12px;
  background: #111318;
  display: flex;
  align-items: center;
  justify-content: center;
}

.car-08__rail {
  list-style: none;
  display: flex;
  gap: 18px;
  width: min(820px,100%);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding: 14px;
  padding: 14px;
  scrollbar-width: thin;
  scrollbar-color: #333842 transparent;
}

.car-08__rail:focus-visible {
  outline: 3px solid #e6ff4d;
  outline-offset: 2px;
  border-radius: 16px;
}

.car-08__card {
  flex: 0 0 clamp(220px,72%,260px);
  scroll-snap-align: start;
}

.car-08__link {
  display: block;
  position: relative;
  border-radius: 16px;
  overflow: hidden;
  text-decoration: none;
  background: #1a1d24;
  box-shadow: 0 20px 40px -30px rgba(0,0,0,.9);
}

.car-08__frame {
  display: block;
  aspect-ratio: 4/5;
  overflow: hidden;
}

.car-08__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .5s ease;
}

.car-08__link:hover .car-08__frame img,
.car-08__link:focus-visible .car-08__frame img {
  transform: scale(1.07);
}

.car-08__cap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 44px 18px 18px;
  background: linear-gradient(transparent,rgba(10,11,14,.9));
  transform: translateY(101%);
  opacity: 0;
  transition: transform .4s cubic-bezier(.2,.8,.2,1),opacity .4s;
}

.car-08__link:hover .car-08__cap,
.car-08__link:focus-within .car-08__cap {
  transform: translateY(0);
  opacity: 1;
}

.car-08__tag {
  display: inline-block;
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #e6ff4d;
  margin-bottom: 6px;
}

.car-08__title {
  display: block;
  font-size: 16px;
  font-weight: 700;
  color: #fff;
}

@media (prefers-reduced-motion: reduce) {
  .car-08__frame img,
    .car-08__cap {
    transition: none;
  }

  .car-08__cap {
    transform: none;
    opacity: 1;
  }
}
/* No JavaScript — hover/focus-within slides the caption up (translateY) and zooms the image (transform). */
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 Carousel 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: Portfolio Overlay Gallery
Source: https://codefronts.com/snippets/css-image-carousel/portfolio-overlay-gallery/

A case-study / portfolio rail where each project screenshot reveals its title and category on hover, sliding up from the bottom edge. The classic agency showcase — pure CSS transitions, keyboard-accessible via focus-within.
## HTML
```html
<section class="car-08" aria-label="Selected work">
  <ul class="car-08__rail" tabindex="0" role="group" aria-label="Portfolio projects, scrollable">
    <li class="car-08__card"><a class="car-08__link" href="#"><span class="car-08__frame"><img src="https://picsum.photos/seed/work-1/640/800" width="640" height="800" alt="Fintech dashboard redesign"></span><span class="car-08__cap"><span class="car-08__tag">Product</span><span class="car-08__title">Ledger — Fintech Dashboard</span></span></a></li>
    <li class="car-08__card"><a class="car-08__link" href="#"><span class="car-08__frame"><img src="https://picsum.photos/seed/work-2/640/800" width="640" height="800" alt="Coffee brand identity"></span><span class="car-08__cap"><span class="car-08__tag">Branding</span><span class="car-08__title">Cùrrent — Coffee Identity</span></span></a></li>
    <li class="car-08__card"><a class="car-08__link" href="#"><span class="car-08__frame"><img src="https://picsum.photos/seed/work-3/640/800" width="640" height="800" alt="Travel app case study"></span><span class="car-08__cap"><span class="car-08__tag">Mobile</span><span class="car-08__title">Wander — Travel App</span></span></a></li>
    <li class="car-08__card"><a class="car-08__link" href="#"><span class="car-08__frame"><img src="https://picsum.photos/seed/work-4/640/800" width="640" height="800" alt="E-commerce replatform"></span><span class="car-08__cap"><span class="car-08__tag">Web</span><span class="car-08__title">Maison — E-commerce</span></span></a></li>
  </ul>
</section>
```
## CSS
```css
.car-08,
.car-08 *,
.car-08 *::before,
.car-08 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.car-08 ::selection {
  background: #e6ff4d;
  color: #111;
}

.car-08 {
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  padding: 40px 12px;
  background: #111318;
  display: flex;
  align-items: center;
  justify-content: center;
}

.car-08__rail {
  list-style: none;
  display: flex;
  gap: 18px;
  width: min(820px,100%);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding: 14px;
  padding: 14px;
  scrollbar-width: thin;
  scrollbar-color: #333842 transparent;
}

.car-08__rail:focus-visible {
  outline: 3px solid #e6ff4d;
  outline-offset: 2px;
  border-radius: 16px;
}

.car-08__card {
  flex: 0 0 clamp(220px,72%,260px);
  scroll-snap-align: start;
}

.car-08__link {
  display: block;
  position: relative;
  border-radius: 16px;
  overflow: hidden;
  text-decoration: none;
  background: #1a1d24;
  box-shadow: 0 20px 40px -30px rgba(0,0,0,.9);
}

.car-08__frame {
  display: block;
  aspect-ratio: 4/5;
  overflow: hidden;
}

.car-08__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .5s ease;
}

.car-08__link:hover .car-08__frame img,
.car-08__link:focus-visible .car-08__frame img {
  transform: scale(1.07);
}

.car-08__cap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 44px 18px 18px;
  background: linear-gradient(transparent,rgba(10,11,14,.9));
  transform: translateY(101%);
  opacity: 0;
  transition: transform .4s cubic-bezier(.2,.8,.2,1),opacity .4s;
}

.car-08__link:hover .car-08__cap,
.car-08__link:focus-within .car-08__cap {
  transform: translateY(0);
  opacity: 1;
}

.car-08__tag {
  display: inline-block;
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #e6ff4d;
  margin-bottom: 6px;
}

.car-08__title {
  display: block;
  font-size: 16px;
  font-weight: 700;
  color: #fff;
}

@media (prefers-reduced-motion: reduce) {
  .car-08__frame img,
    .car-08__cap {
    transition: none;
  }

  .car-08__cap {
    transform: none;
    opacity: 1;
  }
}
```

## JavaScript
```js
/* No JavaScript — hover/focus-within slides the caption up (translateY) and zooms the image (transform). */
```

How this works

Each project is a snap-aligned card in an overflow-x:auto flex rail. The overlay caption sits translated below the frame (translateY(100%)) at rest; on :hover or :focus-within it slides to translateY(0) and fades in, while the image scales subtly under an overflow:hidden mask. All animated properties are transform/opacity.

The card wraps a real <a> so it's a keyboard-focusable link; :focus-within triggers the same reveal a mouse hover would, so the caption is never keyboard-inaccessible.

Make it yours

  • Change the reveal direction by editing the caption's rest transform (slide from side, or fade-only).
  • Tune the image zoom with the hovered scale value.
  • Recolour the gradient scrim behind the caption text.
  • Set cards-per-view via flex-basis.

Gotchas — read before shipping

  • Drive the reveal with :focus-within as well as :hover so keyboard users see the caption.
  • Keep the caption text in the DOM (not a pseudo-element) so it's announced and indexable for SEO.
  • Apply overflow:hidden to the frame, not the card, so the zoom crops cleanly without hiding the caption.

Browser support

ChromeSafariFirefoxEdge
69+11+68+69+

:focus-within + scroll-snap; supported across modern browsers.

Techniques used in this demo

Search CodeFronts

Loading…