css|html|javascript 68 views

Stylish Image Gallery with CSS Grid and Hover Effects

Create a responsive image gallery using CSS Grid with hover effects for images.

By TWC Team • Feb 08, 2026

Code

<!--
  Stylish Image Gallery with CSS Grid + hover effects.
  Drop this into any page; replace image URLs and captions as needed.
-->
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Stylish Image Gallery</title>
  <style>
    :root{
      --gap: 10px;
      --radius: 12px;
      --shadow: 0 12px 30px rgba(0,0,0,.18);
      --speed: 240ms;
    }

    /* Gallery grid: responsive columns with consistent gaps */
    .gallery{
      display:grid;
      grid-template-columns:repeat(auto-fill,minmax(200px,1fr));
      gap:var(--gap);
      padding:var(--gap);
      margin:0 auto;
      max-width:1100px;
    }

    .gallery-item{
      overflow:hidden;
      position:relative;
      border-radius:var(--radius);
      background:#0f1115;
      box-shadow:0 2px 10px rgba(0,0,0,.12);
      isolation:isolate; /* ensures overlay/filters don't bleed */
    }

    .gallery-item img{
      width:100%;
      height:100%;
      display:block;
      aspect-ratio: 4 / 3; /* stable layout; prevents CLS */
      object-fit:cover;
      transform:translateZ(0); /* GPU hint for smooth transforms */
      transition:transform var(--speed) ease, filter var(--speed) ease;
      will-change:transform; /* performance: only for hoverable elements */
    }

    /* Subtle overlay + caption (optional) */
    .gallery-item::after{
      content:"";
      position:absolute;
      inset:0;
      background:linear-gradient(to top, rgba(0,0,0,.45), rgba(0,0,0,0) 55%);
      opacity:0;
      transition:opacity var(--speed) ease;
      pointer-events:none;
    }

    .gallery-item figcaption{
      position:absolute;
      left:12px;
      right:12px;
      bottom:10px;
      color:#fff;
      font:600 14px/1.2 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
      letter-spacing:.2px;
      opacity:0;
      transform:translateY(6px);
      transition:opacity var(--speed) ease, transform var(--speed) ease;
      text-shadow:0 2px 10px rgba(0,0,0,.35);
      pointer-events:none;
    }

    .gallery-item:hover img{
      transform:scale(1.08);
      filter:saturate(1.05) contrast(1.05);
    }
    .gallery-item:hover::after,
    .gallery-item:hover figcaption{
      opacity:1;
    }
    .gallery-item:hover figcaption{
      transform:translateY(0);
    }

    /* Accessibility: show hover effects on keyboard focus as well */
    .gallery-item:focus-within img{ transform:scale(1.08); }
    .gallery-item:focus-within::after,
    .gallery-item:focus-within figcaption{ opacity:1; transform:translateY(0); }

    /* Respect reduced motion preferences */
    @media (prefers-reduced-motion: reduce){
      .gallery-item img,
      .gallery-item::after,
      .gallery-item figcaption{ transition:none; }
    }
  </style>
</head>
<body>

  <!-- Example usage: replace src/alt; captions are optional -->
  <div class="gallery">
    <figure class="gallery-item">
      <img src="image1.jpg" alt="Image 1" loading="lazy" decoding="async" />
      <figcaption>Image 1</figcaption>
    </figure>
    <figure class="gallery-item">
      <img src="image2.jpg" alt="Image 2" loading="lazy" decoding="async" />
      <figcaption>Image 2</figcaption>
    </figure>
    <figure class="gallery-item">
      <img src="image3.jpg" alt="Image 3" loading="lazy" decoding="async" />
      <figcaption>Image 3</figcaption>
    </figure>
    <figure class="gallery-item">
      <img src="image4.jpg" alt="Image 4" loading="lazy" decoding="async" />
      <figcaption>Image 4</figcaption>
    </figure>
  </div>

  <script>
    /**
     * Optional enhancement: adds keyboard focusability to each gallery item
     * without changing the required structure, improving accessibility.
     */
    document.querySelectorAll('.gallery-item').forEach((item) => {
      if (!item.hasAttribute('tabindex')) item.setAttribute('tabindex', '0');
    });
  </script>
</body>
</html>
Back to Snippets