html|css|javascript 66 views

Stunning Image Card Reveal Effect

Create a visually engaging image card effect that reveals content on hover, enhancing user engagement.

By TWC Team • Jan 30, 2026

Code

<!--
  Stunning Image Card Reveal Effect
  Hover/focus reveals overlay content; includes keyboard + reduced-motion support.
-->
<div class="cards" role="list">
  <article class="card" role="listitem" tabindex="0" aria-label="View details: Aurora Peak Jacket">
    <img src="https://images.unsplash.com/photo-1520975661595-6453be3f7070?auto=format&fit=crop&w=800&q=80" alt="Model wearing a jacket in the mountains" loading="lazy" decoding="async">
    <div class="card-content" aria-hidden="true">
      <h3>Aurora Peak Jacket</h3>
      <p>Waterproof shell • 3-layer build • New season colorways.</p>
      <a class="card-cta" href="#" aria-label="Shop Aurora Peak Jacket">Shop now</a>
    </div>
  </article>
</div>

<style>
  :root { color-scheme: dark; }
  .cards { display: grid; place-items: center; padding: 24px; }
  .card {
    position: relative; overflow: hidden; width: 300px; height: 400px; border-radius: 15px;
    box-shadow: 0 4px 20px rgba(0,0,0,.2); transition: transform .3s;
    background: #111; outline: none; /* prevent layout shift if image is slow */
  }
  .card img {
    width: 100%; height: 100%; object-fit: cover; transform: scale(1);
    transition: transform .5s; will-change: transform; /* perf: hint GPU compositing */
  }
  .card-content {
    position: absolute; inset: auto 0 0 0;
    background: linear-gradient(to top, rgba(0,0,0,.78), rgba(0,0,0,.25));
    color: #fff; padding: 18px 20px; transform: translateY(100%);
    transition: transform .3s; will-change: transform;
  }
  .card-content h3 { margin: 0 0 6px; font: 600 18px/1.2 system-ui, sans-serif; }
  .card-content p { margin: 0 0 12px; opacity: .92; font: 400 14px/1.45 system-ui, sans-serif; }
  .card-cta {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 10px 12px; border-radius: 10px; text-decoration: none;
    color: #0b0b0b; background: #fff; font: 600 13px system-ui, sans-serif;
  }

  /* Hover + keyboard parity */
  .card:hover { transform: translateY(-2px); }
  .card:hover img, .card:focus-visible img { transform: scale(1.1); }
  .card:hover .card-content, .card:focus-visible .card-content { transform: translateY(0); }
  .card:focus-visible { box-shadow: 0 0 0 3px rgba(255,255,255,.55), 0 8px 28px rgba(0,0,0,.35); }

  /* Respect user preference to reduce motion */
  @media (prefers-reduced-motion: reduce) {
    .card, .card img, .card-content { transition: none; }
  }
</style>

<script>
  // Enable "Enter/Space to open" behavior for keyboard users; safe no-op if no link.
  document.querySelectorAll(".card").forEach((card) => {
    card.addEventListener("keydown", (event) => {
      if (event.key !== "Enter" && event.key !== " ") return;
      event.preventDefault();
      const primaryLink = card.querySelector(".card-cta");
      if (primaryLink?.href) primaryLink.click();
    });
  });
</script>

<!-- Example usage: Duplicate <article class="card"> blocks inside .cards for galleries/portfolios. -->
Back to Snippets