Open demo

CSS code

HTML
<div><span class="lbl">Action bar with quick view (hover or Tab into a card)</span>
<ul class="qv">
  <li class="qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?auto=format&fit=crop&w=600&q=70" alt="White minimalist wristwatch with a silicone strap on a pale grey surface" width="600" height="600" loading="lazy">
      <span class="qv-badge">New</span>
      <div class="qv-actions">
        <button type="button" class="qv-btn is-main" data-quick-view="qv-watch"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>Quick view<span class="sr-only">: Linea Minimal Watch</span></button>
        <button type="button" class="qv-btn is-icon" aria-label="Add Linea Minimal Watch to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
        <button type="button" class="qv-btn is-icon qv-wish" aria-pressed="false" aria-label="Save Linea Minimal Watch to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#">Linea Minimal Watch</a></h3><p class="qv-sub">3 strap colours</p></div><p class="qv-price">$189</p></div>
  </li>
  <li class="qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1583394838336-acd977736f90?auto=format&fit=crop&w=600&q=70" alt="Grey over-ear headphones with padded cups on a white background" width="600" height="600" loading="lazy">
      <div class="qv-actions">
        <button type="button" class="qv-btn is-main" data-quick-view="qv-phones"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>Quick view<span class="sr-only">: Studio Over-Ear Headphones</span></button>
        <button type="button" class="qv-btn is-icon" aria-label="Add Studio Over-Ear Headphones to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
        <button type="button" class="qv-btn is-icon qv-wish" aria-pressed="false" aria-label="Save Studio Over-Ear Headphones to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#">Studio Over-Ear Headphones</a></h3><p class="qv-sub">Wired · 40 mm drivers</p></div><p class="qv-price">$129</p></div>
  </li>
  <li class="qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1584917865442-de89df76afd3?auto=format&fit=crop&w=600&q=70" alt="Glossy red top-handle handbag with a silver clasp" width="600" height="600" loading="lazy">
      <span class="qv-badge">Limited</span>
      <div class="qv-actions">
        <button type="button" class="qv-btn is-main" data-quick-view="qv-bag"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>Quick view<span class="sr-only">: Lacquer Top-Handle Bag</span></button>
        <button type="button" class="qv-btn is-icon" aria-label="Add Lacquer Top-Handle Bag to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
        <button type="button" class="qv-btn is-icon qv-wish" aria-pressed="true" aria-label="Save Lacquer Top-Handle Bag to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#">Lacquer Top-Handle Bag</a></h3><p class="qv-sub">Patent leather</p></div><p class="qv-price">$265</p></div>
  </li>
</ul>

<dialog class="qv-dialog" id="qv-watch" aria-labelledby="qv-watch-title">
  <div class="qv-dialog-grid">
    <img src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?auto=format&fit=crop&w=800&q=70" alt="White minimalist wristwatch with a silicone strap on a pale grey surface" width="800" height="800" loading="lazy">
    <div class="qv-dialog-body">
      <h2 id="qv-watch-title">Linea Minimal Watch</h2>
      <p class="qv-rating"><span class="qv-stars" style="--r:4.6" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.6 out of 5, 182 reviews</span><span aria-hidden="true">4.6 (182 reviews)</span></p>
      <p class="qv-price">$189</p>
      <p class="qv-desc">A 38 mm brushed steel case with a matte black dial, sapphire-coated glass and a quick-release silicone strap. Water resistant to 50 m.</p>
      <p class="qv-note">Free shipping over $100 · 30-day returns</p>
      <button type="button" class="qv-add">Add<span class="sr-only"> Linea Minimal Watch</span> to cart</button>
      <form method="dialog"><button class="qv-close" aria-label="Close quick view"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button></form>
    </div>
  </div>
</dialog>
<dialog class="qv-dialog" id="qv-phones" aria-labelledby="qv-phones-title">
  <div class="qv-dialog-grid">
    <img src="https://images.unsplash.com/photo-1583394838336-acd977736f90?auto=format&fit=crop&w=800&q=70" alt="Grey over-ear headphones with padded cups on a white background" width="800" height="800" loading="lazy">
    <div class="qv-dialog-body">
      <h2 id="qv-phones-title">Studio Over-Ear Headphones</h2>
      <p class="qv-rating"><span class="qv-stars" style="--r:4.4" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.4 out of 5, 641 reviews</span><span aria-hidden="true">4.4 (641 reviews)</span></p>
      <p class="qv-price">$129</p>
      <p class="qv-desc">Closed-back monitoring headphones with 40 mm drivers, memory-foam cushions and a detachable 3 m coiled cable. Fold flat for travel.</p>
      <p class="qv-note">Free shipping over $100 · 2-year warranty</p>
      <button type="button" class="qv-add">Add<span class="sr-only"> Studio Over-Ear Headphones</span> to cart</button>
      <form method="dialog"><button class="qv-close" aria-label="Close quick view"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button></form>
    </div>
  </div>
</dialog>
<dialog class="qv-dialog" id="qv-bag" aria-labelledby="qv-bag-title">
  <div class="qv-dialog-grid">
    <img src="https://images.unsplash.com/photo-1584917865442-de89df76afd3?auto=format&fit=crop&w=800&q=70" alt="Glossy red top-handle handbag with a silver clasp" width="800" height="800" loading="lazy">
    <div class="qv-dialog-body">
      <h2 id="qv-bag-title">Lacquer Top-Handle Bag</h2>
      <p class="qv-rating"><span class="qv-stars" style="--r:4.8" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.8 out of 5, 54 reviews</span><span aria-hidden="true">4.8 (54 reviews)</span></p>
      <p class="qv-price">$265</p>
      <p class="qv-desc">Structured patent leather with a turn-lock clasp, suede lining and a detachable shoulder strap. Fits a phone, wallet and keys.</p>
      <p class="qv-note">Limited run of 300 · Free returns</p>
      <button type="button" class="qv-add">Add<span class="sr-only"> Lacquer Top-Handle Bag</span> to cart</button>
      <form method="dialog"><button class="qv-close" aria-label="Close quick view"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button></form>
    </div>
  </div>
</dialog></div>

<div><span class="lbl">Icon rail (hover or Tab into a card)</span>
<ul class="qv">
  <li class="qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1602810318383-e386cc2a3ccf?auto=format&fit=crop&w=600&q=70" alt="Three folded dress shirts in grey, white and burgundy on a wooden board" width="600" height="600" loading="lazy">
      <div class="qv-rail">
        <button type="button" class="qv-btn is-icon qv-wish" aria-pressed="false" aria-label="Save Oxford Shirt Trio to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
        <button type="button" class="qv-btn is-icon is-compare qv-wish" aria-pressed="false" aria-label="Compare Oxford Shirt Trio"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4v16M17 4v16M3 8l4-4 4 4M13 16l4 4 4-4"/></svg></button>
        <button type="button" class="qv-btn is-icon" aria-label="Add Oxford Shirt Trio to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#">Oxford Shirt Trio</a></h3><p class="qv-sub">Set of 3 · slim fit</p></div><p class="qv-price">$138</p></div>
  </li>
  <li class="qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?auto=format&fit=crop&w=600&q=70" alt="Woman in a bright yellow fleece tracksuit standing on a beach promenade" width="600" height="600" loading="lazy">
      <span class="qv-badge">Trending</span>
      <div class="qv-rail">
        <button type="button" class="qv-btn is-icon qv-wish" aria-pressed="true" aria-label="Save Sunday Fleece Set to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
        <button type="button" class="qv-btn is-icon is-compare qv-wish" aria-pressed="false" aria-label="Compare Sunday Fleece Set"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4v16M17 4v16M3 8l4-4 4 4M13 16l4 4 4-4"/></svg></button>
        <button type="button" class="qv-btn is-icon" aria-label="Add Sunday Fleece Set to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#">Sunday Fleece Set</a></h3><p class="qv-sub">Hoodie and joggers</p></div><p class="qv-price">$96</p></div>
  </li>
  <li class="qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1596462502278-27bfdc403348?auto=format&fit=crop&w=600&q=70" alt="Makeup brushes, lipsticks and a bronzer compact laid out on a beige background" width="600" height="600" loading="lazy">
      <div class="qv-rail">
        <button type="button" class="qv-btn is-icon qv-wish" aria-pressed="false" aria-label="Save Rose Brush and Palette Kit to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
        <button type="button" class="qv-btn is-icon is-compare qv-wish" aria-pressed="false" aria-label="Compare Rose Brush and Palette Kit"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4v16M17 4v16M3 8l4-4 4 4M13 16l4 4 4-4"/></svg></button>
        <button type="button" class="qv-btn is-icon" aria-label="Add Rose Brush and Palette Kit to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#">Rose Brush and Palette Kit</a></h3><p class="qv-sub">9 pieces · vegan bristles</p></div><p class="qv-price">$58</p></div>
  </li>
</ul></div>
CSS
/* Hover cards: the action bar (or icon rail) is hidden until the card is hovered OR anything inside it has
   keyboard focus (:focus-within), and is always shown on touch screens (hover: none). Quick view is a native
   <dialog> opened with showModal(), so it traps focus, closes with Esc and returns focus to the button. */
.qv,.qv-dialog{--qv-ink:#18181b;--qv-muted:#52525b;--qv-media:#e8e8ee;--qv-accent:#4338ca;--qv-accent-hover:#3730a3;--qv-heart:#be123c;--qv-star:#d97706;--qv-star-off:#d4d4d8;--qv-focus:#2563eb;}
.qv{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:24px 20px;list-style:none;margin:0;padding:0}
.qv-card{position:relative}
.qv-media{position:relative;aspect-ratio:1;border-radius:18px;background:var(--qv-media);overflow:hidden}
.qv-media img{display:block;width:100%;height:100%;object-fit:cover;transition:transform .5s ease}
.qv-card:hover .qv-media img{transform:scale(1.04)}
.qv-badge{position:absolute;top:12px;left:12px;padding:3px 10px;border-radius:999px;background:#fff;color:var(--qv-ink);font-size:.72rem;font-weight:700;letter-spacing:.04em}
.qv-actions{position:absolute;left:12px;right:12px;bottom:12px;display:flex;gap:8px;opacity:0;transform:translateY(10px);transition:opacity .2s,transform .2s}
.qv-card:hover .qv-actions,.qv-card:focus-within .qv-actions{opacity:1;transform:none}
.qv-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;height:42px;padding:0 14px;border:0;border-radius:12px;background:#fff;color:var(--qv-ink);
  font:inherit;font-size:.85rem;font-weight:600;cursor:pointer;box-shadow:0 4px 14px rgba(24,24,27,.16);transition:background-color .15s,color .15s}
.qv-btn:hover{background:var(--qv-ink);color:#fff}
.qv-btn.is-main{flex:1}
.qv-btn.is-icon{width:42px;padding:0}
.qv-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.qv-btn[aria-pressed="true"]{color:var(--qv-heart)}
.qv-btn[aria-pressed="true"]:hover{color:#fff}
.qv-btn[aria-pressed="true"] .qv-heart{fill:currentColor}
.qv-btn.is-compare[aria-pressed="true"]{background:var(--qv-accent);color:#fff}
.qv-info{display:flex;justify-content:space-between;gap:12px;padding:14px 4px 0}
.qv-title{margin:0;font-size:.98rem;font-weight:600;line-height:1.35}
.qv-title a{color:var(--qv-ink);text-decoration:none}
.qv-title a:hover{text-decoration:underline;text-underline-offset:3px}
.qv-sub{margin:2px 0 0;font-size:.8rem;color:var(--qv-muted)}
.qv-price{margin:0;font-size:1rem;font-weight:700;white-space:nowrap}
/* Icon rail variant */
.qv-rail{position:absolute;top:12px;right:12px;display:flex;flex-direction:column;gap:8px;opacity:0;transform:translateX(10px);transition:opacity .2s,transform .2s}
.qv-card:hover .qv-rail,.qv-card:focus-within .qv-rail{opacity:1;transform:none}
@media (hover:none){.qv-actions,.qv-rail{opacity:1;transform:none}}
/* Quick view dialog */
.qv-dialog{width:min(760px,calc(100% - 32px));max-height:calc(100% - 32px);margin:auto;padding:0;border:0;border-radius:20px;background:#fff;color:var(--qv-ink);box-shadow:0 30px 80px -20px rgba(24,24,27,.45)}
.qv-dialog::backdrop{background:rgba(24,24,27,.55)}
.qv-dialog[open]{animation:qv-in .22s ease-out}
@keyframes qv-in{from{opacity:0;transform:translateY(12px) scale(.98)}}
.qv-dialog-grid{display:grid;grid-template-columns:1fr}
.qv-dialog-grid>img{display:block;width:100%;height:auto;aspect-ratio:16/10;object-fit:cover;background:var(--qv-media)}
.qv-dialog-body{display:flex;flex-direction:column;gap:10px;padding:28px 26px 26px}
.qv-dialog h2{margin:0;padding-right:40px;font-size:1.4rem;line-height:1.25;letter-spacing:-.01em}
.qv-dialog .qv-price{font-size:1.3rem}
.qv-rating{display:flex;align-items:center;gap:6px;margin:0;font-size:.8rem;color:var(--qv-muted)}
.qv-stars{--r:5;font-size:.9rem;letter-spacing:1px;line-height:1;color:transparent;
  background:linear-gradient(90deg,var(--qv-star) calc(var(--r) / 5 * 100%),var(--qv-star-off) 0);-webkit-background-clip:text;background-clip:text}
.qv-desc{margin:0;font-size:.9rem;color:var(--qv-muted)}
.qv-note{margin:0;font-size:.8rem;color:var(--qv-muted)}
.qv-add{margin-top:8px;padding:13px 18px;border:0;border-radius:12px;background:var(--qv-accent);color:#fff;font:inherit;font-size:.95rem;font-weight:600;cursor:pointer;transition:background-color .15s}
.qv-add:hover{background:var(--qv-accent-hover)}
.qv-close{position:absolute;top:16px;right:16px;display:grid;place-items:center;width:36px;height:36px;padding:0;border:0;border-radius:50%;background:#f4f4f5;color:var(--qv-ink);cursor:pointer}
.qv-close:hover{background:#e4e4e7}
.qv-close svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round}
@media (min-width:640px){.qv-dialog-grid{grid-template-columns:1fr 1fr}.qv-dialog-grid>img{height:100%;aspect-ratio:1}.qv-dialog-body{justify-content:center}}
:is(.qv,.qv-dialog) :is(a,button):focus-visible{outline:3px solid var(--qv-focus);outline-offset:2px}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
@media (prefers-reduced-motion:reduce){.qv-media img,.qv-actions,.qv-rail,.qv-btn,.qv-add{transition:none}.qv-card:hover .qv-media img{transform:none}.qv-dialog[open]{animation:none}}
JavaScript
// Quick view: each button opens its <dialog> as a modal; clicking the dim backdrop also closes it.
document.querySelectorAll('[data-quick-view]').forEach(btn => btn.addEventListener('click', () => {
  document.getElementById(btn.dataset.quickView).showModal();
}));
document.querySelectorAll('.qv-dialog').forEach(dlg => dlg.addEventListener('click', e => { if (e.target === dlg) dlg.close(); }));
// Wishlist and compare are toggle buttons: flip aria-pressed on click.
document.querySelectorAll('.qv-wish').forEach(btn => btn.addEventListener('click', () => {
  btn.setAttribute('aria-pressed', String(btn.getAttribute('aria-pressed') !== 'true'));
}));

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS, Bootstrap 5.3.8 JS bundle

HTML
<div><span class="lbl">Action bar with quick view (hover or Tab into a card)</span>
<ul class="qv row row-cols-1 row-cols-sm-2 row-cols-md-3 list-unstyled mb-0">
  <li class="col qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?auto=format&fit=crop&w=600&q=70" alt="White minimalist wristwatch with a silicone strap on a pale grey surface" width="600" height="600" loading="lazy">
      <span class="qv-badge badge rounded-pill">New</span>
      <div class="qv-actions">
        <button type="button" class="btn qv-btn is-main" data-bs-toggle="modal" data-bs-target="#qv-watch"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>Quick view<span class="visually-hidden">: Linea Minimal Watch</span></button>
        <button type="button" class="btn qv-btn is-icon" aria-label="Add Linea Minimal Watch to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
        <button type="button" class="btn qv-btn is-icon" data-bs-toggle="button" aria-pressed="false" aria-label="Save Linea Minimal Watch to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#" class="text-decoration-none">Linea Minimal Watch</a></h3><p class="qv-sub">3 strap colours</p></div><p class="qv-price">$189</p></div>
  </li>
  <li class="col qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1583394838336-acd977736f90?auto=format&fit=crop&w=600&q=70" alt="Grey over-ear headphones with padded cups on a white background" width="600" height="600" loading="lazy">
      <div class="qv-actions">
        <button type="button" class="btn qv-btn is-main" data-bs-toggle="modal" data-bs-target="#qv-phones"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>Quick view<span class="visually-hidden">: Studio Over-Ear Headphones</span></button>
        <button type="button" class="btn qv-btn is-icon" aria-label="Add Studio Over-Ear Headphones to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
        <button type="button" class="btn qv-btn is-icon" data-bs-toggle="button" aria-pressed="false" aria-label="Save Studio Over-Ear Headphones to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#" class="text-decoration-none">Studio Over-Ear Headphones</a></h3><p class="qv-sub">Wired · 40 mm drivers</p></div><p class="qv-price">$129</p></div>
  </li>
  <li class="col qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1584917865442-de89df76afd3?auto=format&fit=crop&w=600&q=70" alt="Glossy red top-handle handbag with a silver clasp" width="600" height="600" loading="lazy">
      <span class="qv-badge badge rounded-pill">Limited</span>
      <div class="qv-actions">
        <button type="button" class="btn qv-btn is-main" data-bs-toggle="modal" data-bs-target="#qv-bag"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>Quick view<span class="visually-hidden">: Lacquer Top-Handle Bag</span></button>
        <button type="button" class="btn qv-btn is-icon" aria-label="Add Lacquer Top-Handle Bag to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
        <button type="button" class="btn qv-btn is-icon active" data-bs-toggle="button" aria-pressed="true" aria-label="Save Lacquer Top-Handle Bag to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#" class="text-decoration-none">Lacquer Top-Handle Bag</a></h3><p class="qv-sub">Patent leather</p></div><p class="qv-price">$265</p></div>
  </li>
</ul>

<div class="modal fade qv-modal" id="qv-watch" tabindex="-1" aria-labelledby="qv-watch-title" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered"><div class="modal-content">
  <div class="qv-dialog-grid">
    <img src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?auto=format&fit=crop&w=800&q=70" alt="White minimalist wristwatch with a silicone strap on a pale grey surface" width="800" height="800" loading="lazy">
    <div class="qv-dialog-body">
      <h2 class="modal-title" id="qv-watch-title">Linea Minimal Watch</h2>
      <p class="qv-rating"><span class="qv-stars" style="--r:4.6" aria-hidden="true">★★★★★</span><span class="visually-hidden">Rated 4.6 out of 5, 182 reviews</span><span aria-hidden="true">4.6 (182 reviews)</span></p>
      <p class="qv-price">$189</p>
      <p class="qv-desc">A 38 mm brushed steel case with a matte black dial, sapphire-coated glass and a quick-release silicone strap. Water resistant to 50 m.</p>
      <p class="qv-note">Free shipping over $100 · 30-day returns</p>
      <button type="button" class="btn qv-add">Add<span class="visually-hidden"> Linea Minimal Watch</span> to cart</button>
      <button type="button" class="btn-close qv-close" data-bs-dismiss="modal" aria-label="Close quick view"></button>
    </div>
  </div>
</div></div>
</div>
<div class="modal fade qv-modal" id="qv-phones" tabindex="-1" aria-labelledby="qv-phones-title" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered"><div class="modal-content">
  <div class="qv-dialog-grid">
    <img src="https://images.unsplash.com/photo-1583394838336-acd977736f90?auto=format&fit=crop&w=800&q=70" alt="Grey over-ear headphones with padded cups on a white background" width="800" height="800" loading="lazy">
    <div class="qv-dialog-body">
      <h2 class="modal-title" id="qv-phones-title">Studio Over-Ear Headphones</h2>
      <p class="qv-rating"><span class="qv-stars" style="--r:4.4" aria-hidden="true">★★★★★</span><span class="visually-hidden">Rated 4.4 out of 5, 641 reviews</span><span aria-hidden="true">4.4 (641 reviews)</span></p>
      <p class="qv-price">$129</p>
      <p class="qv-desc">Closed-back monitoring headphones with 40 mm drivers, memory-foam cushions and a detachable 3 m coiled cable. Fold flat for travel.</p>
      <p class="qv-note">Free shipping over $100 · 2-year warranty</p>
      <button type="button" class="btn qv-add">Add<span class="visually-hidden"> Studio Over-Ear Headphones</span> to cart</button>
      <button type="button" class="btn-close qv-close" data-bs-dismiss="modal" aria-label="Close quick view"></button>
    </div>
  </div>
</div></div>
</div>
<div class="modal fade qv-modal" id="qv-bag" tabindex="-1" aria-labelledby="qv-bag-title" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered"><div class="modal-content">
  <div class="qv-dialog-grid">
    <img src="https://images.unsplash.com/photo-1584917865442-de89df76afd3?auto=format&fit=crop&w=800&q=70" alt="Glossy red top-handle handbag with a silver clasp" width="800" height="800" loading="lazy">
    <div class="qv-dialog-body">
      <h2 class="modal-title" id="qv-bag-title">Lacquer Top-Handle Bag</h2>
      <p class="qv-rating"><span class="qv-stars" style="--r:4.8" aria-hidden="true">★★★★★</span><span class="visually-hidden">Rated 4.8 out of 5, 54 reviews</span><span aria-hidden="true">4.8 (54 reviews)</span></p>
      <p class="qv-price">$265</p>
      <p class="qv-desc">Structured patent leather with a turn-lock clasp, suede lining and a detachable shoulder strap. Fits a phone, wallet and keys.</p>
      <p class="qv-note">Limited run of 300 · Free returns</p>
      <button type="button" class="btn qv-add">Add<span class="visually-hidden"> Lacquer Top-Handle Bag</span> to cart</button>
      <button type="button" class="btn-close qv-close" data-bs-dismiss="modal" aria-label="Close quick view"></button>
    </div>
  </div>
</div></div>
</div></div>

<div><span class="lbl">Icon rail (hover or Tab into a card)</span>
<ul class="qv row row-cols-1 row-cols-sm-2 row-cols-md-3 list-unstyled mb-0">
  <li class="col qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1602810318383-e386cc2a3ccf?auto=format&fit=crop&w=600&q=70" alt="Three folded dress shirts in grey, white and burgundy on a wooden board" width="600" height="600" loading="lazy">
      <div class="qv-rail">
        <button type="button" class="btn qv-btn is-icon" data-bs-toggle="button" aria-pressed="false" aria-label="Save Oxford Shirt Trio to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
        <button type="button" class="btn qv-btn is-icon is-compare" data-bs-toggle="button" aria-pressed="false" aria-label="Compare Oxford Shirt Trio"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4v16M17 4v16M3 8l4-4 4 4M13 16l4 4 4-4"/></svg></button>
        <button type="button" class="btn qv-btn is-icon" aria-label="Add Oxford Shirt Trio to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#" class="text-decoration-none">Oxford Shirt Trio</a></h3><p class="qv-sub">Set of 3 · slim fit</p></div><p class="qv-price">$138</p></div>
  </li>
  <li class="col qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?auto=format&fit=crop&w=600&q=70" alt="Woman in a bright yellow fleece tracksuit standing on a beach promenade" width="600" height="600" loading="lazy">
      <span class="qv-badge badge rounded-pill">Trending</span>
      <div class="qv-rail">
        <button type="button" class="btn qv-btn is-icon active" data-bs-toggle="button" aria-pressed="true" aria-label="Save Sunday Fleece Set to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
        <button type="button" class="btn qv-btn is-icon is-compare" data-bs-toggle="button" aria-pressed="false" aria-label="Compare Sunday Fleece Set"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4v16M17 4v16M3 8l4-4 4 4M13 16l4 4 4-4"/></svg></button>
        <button type="button" class="btn qv-btn is-icon" aria-label="Add Sunday Fleece Set to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#" class="text-decoration-none">Sunday Fleece Set</a></h3><p class="qv-sub">Hoodie and joggers</p></div><p class="qv-price">$96</p></div>
  </li>
  <li class="col qv-card">
    <div class="qv-media">
      <img src="https://images.unsplash.com/photo-1596462502278-27bfdc403348?auto=format&fit=crop&w=600&q=70" alt="Makeup brushes, lipsticks and a bronzer compact laid out on a beige background" width="600" height="600" loading="lazy">
      <div class="qv-rail">
        <button type="button" class="btn qv-btn is-icon" data-bs-toggle="button" aria-pressed="false" aria-label="Save Rose Brush and Palette Kit to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path class="qv-heart" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
        <button type="button" class="btn qv-btn is-icon is-compare" data-bs-toggle="button" aria-pressed="false" aria-label="Compare Rose Brush and Palette Kit"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4v16M17 4v16M3 8l4-4 4 4M13 16l4 4 4-4"/></svg></button>
        <button type="button" class="btn qv-btn is-icon" aria-label="Add Rose Brush and Palette Kit to cart"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
      </div>
    </div>
    <div class="qv-info"><div><h3 class="qv-title"><a href="#" class="text-decoration-none">Rose Brush and Palette Kit</a></h3><p class="qv-sub">9 pieces · vegan bristles</p></div><p class="qv-price">$58</p></div>
  </li>
</ul></div>
CSS
/* Hover cards on Bootstrap: the .btn action bar (or icon rail) is hidden until the card is hovered OR anything
   inside it has keyboard focus (:focus-within), and always shown on touch screens. Quick view is a Bootstrap
   .modal (focus trap, Esc, focus return); wishlist and compare use data-bs-toggle="button" for aria-pressed. */
.qv,.qv-modal{--qv-ink:#18181b;--qv-muted:#52525b;--qv-media:#e8e8ee;--qv-accent:#4338ca;--qv-accent-hover:#3730a3;--qv-heart:#be123c;--qv-star:#d97706;--qv-star-off:#d4d4d8;--qv-focus:#2563eb;}
.qv{--bs-gutter-x:20px;--bs-gutter-y:24px}
.qv-card{position:relative}
.qv-media{position:relative;aspect-ratio:1;border-radius:18px;background:var(--qv-media);overflow:hidden}
.qv-media img{display:block;width:100%;height:100%;object-fit:cover;transition:transform .5s ease}
.qv-card:hover .qv-media img{transform:scale(1.04)}
.qv-badge{position:absolute;top:12px;left:12px;padding:3px 10px;line-height:1.5;border-radius:999px;background:#fff;color:var(--qv-ink);font-size:.72rem;font-weight:700;letter-spacing:.04em}
.qv-actions{position:absolute;left:12px;right:12px;bottom:12px;display:flex;gap:8px;opacity:0;transform:translateY(10px);transition:opacity .2s,transform .2s}
.qv-card:hover .qv-actions,.qv-card:focus-within .qv-actions{opacity:1;transform:none}
.qv-btn{--bs-btn-padding-x:14px;--bs-btn-padding-y:0;--bs-btn-font-size:.85rem;--bs-btn-font-weight:600;--bs-btn-border-width:0;--bs-btn-border-radius:12px;
  --bs-btn-bg:#fff;--bs-btn-color:var(--qv-ink);--bs-btn-hover-bg:var(--qv-ink);--bs-btn-hover-color:#fff;--bs-btn-active-bg:#fff;--bs-btn-active-color:var(--qv-heart);
  display:inline-flex;align-items:center;justify-content:center;gap:6px;height:42px;box-shadow:0 4px 14px rgba(24,24,27,.16)}
.qv-btn:focus-visible:not(:hover,.active){background-color:#fff;color:var(--qv-ink)}
.qv-btn.is-main{flex:1}
.qv-btn.is-icon{--bs-btn-padding-x:0;width:42px}
.qv-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.qv-btn.active:hover{--bs-btn-active-bg:var(--qv-ink);--bs-btn-active-color:#fff}
.qv-btn.active .qv-heart{fill:currentColor}
.qv-btn.is-compare{--bs-btn-active-bg:var(--qv-accent);--bs-btn-active-color:#fff}
.qv-info{display:flex;justify-content:space-between;gap:12px;padding:14px 4px 0}
.qv-title{margin:0;font-size:.98rem;font-weight:600;line-height:1.35}
.qv-title a{color:var(--qv-ink);text-decoration:none}
.qv-title a:hover{text-decoration:underline;text-underline-offset:3px}
.qv-sub{margin:2px 0 0;font-size:.8rem;color:var(--qv-muted)}
.qv-price{margin:0;font-size:1rem;font-weight:700;white-space:nowrap}
/* Icon rail variant */
.qv-rail{position:absolute;top:12px;right:12px;display:flex;flex-direction:column;gap:8px;opacity:0;transform:translateX(10px);transition:opacity .2s,transform .2s}
.qv-card:hover .qv-rail,.qv-card:focus-within .qv-rail{opacity:1;transform:none}
@media (hover:none){.qv-actions,.qv-rail{opacity:1;transform:none}}
/* Quick view modal */
.qv-modal{--bs-modal-width:760px;--bs-modal-border-width:0;--bs-modal-border-radius:20px;--bs-modal-inner-border-radius:20px;--bs-modal-box-shadow:0 30px 80px -20px rgba(24,24,27,.45);--bs-modal-margin:16px}
.qv-modal .modal-content{overflow:hidden;box-shadow:var(--bs-modal-box-shadow)}
.modal-backdrop{--bs-backdrop-bg:#18181b;--bs-backdrop-opacity:.55}
.qv-dialog-grid{display:grid;grid-template-columns:1fr}
.qv-dialog-grid>img{display:block;width:100%;height:auto;aspect-ratio:16/10;object-fit:cover;background:var(--qv-media)}
.qv-dialog-body{display:flex;flex-direction:column;gap:10px;padding:28px 26px 26px}
.qv-modal h2{margin:0;padding-right:40px;font-size:1.4rem;font-weight:700;line-height:1.25;letter-spacing:-.01em}
.qv-modal .qv-price{font-size:1.3rem}
.qv-rating{display:flex;align-items:center;gap:6px;margin:0;font-size:.8rem;color:var(--qv-muted)}
.qv-stars{--r:5;font-size:.9rem;letter-spacing:1px;line-height:1;color:transparent;
  background:linear-gradient(90deg,var(--qv-star) calc(var(--r) / 5 * 100%),var(--qv-star-off) 0);-webkit-background-clip:text;background-clip:text}
.qv-desc{margin:0;font-size:.9rem;color:var(--qv-muted)}
.qv-note{margin:0;font-size:.8rem;color:var(--qv-muted)}
.qv-add{--bs-btn-padding-y:13px;--bs-btn-font-size:.95rem;--bs-btn-font-weight:600;--bs-btn-border-radius:12px;--bs-btn-color:#fff;--bs-btn-bg:var(--qv-accent);--bs-btn-border-color:var(--qv-accent);
  --bs-btn-hover-color:#fff;--bs-btn-hover-bg:var(--qv-accent-hover);--bs-btn-hover-border-color:var(--qv-accent-hover);--bs-btn-active-color:#fff;--bs-btn-active-bg:var(--qv-accent-hover);--bs-btn-active-border-color:var(--qv-accent-hover);margin-top:8px}
.qv-close{--bs-btn-close-opacity:1;--bs-btn-close-hover-opacity:1;--bs-btn-close-focus-shadow:none;position:absolute;top:16px;right:16px;width:36px;height:36px;padding:0;border-radius:50%;background-color:#f4f4f5;background-size:12px}
.qv-close:hover{background-color:#e4e4e7}
@media (min-width:640px){.qv-dialog-grid{grid-template-columns:1fr 1fr}.qv-dialog-grid>img{height:100%;aspect-ratio:1}.qv-dialog-body{justify-content:center}}
:is(.qv,.qv-modal) :is(a,button):focus-visible{outline:3px solid var(--qv-focus);outline-offset:2px;box-shadow:none}
@media (prefers-reduced-motion:reduce){.qv-media img,.qv-actions,.qv-rail,.qv .btn,.qv-add{transition:none}.qv-card:hover .qv-media img{transform:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#52525b] uppercase">Action bar with quick view (hover or Tab into a card)</span>
<ul class="grid grid-cols-[repeat(auto-fill,minmax(230px,1fr))] gap-x-5 gap-y-6">
  <li class="group/card relative">
    <div class="relative aspect-square overflow-hidden rounded-[18px] bg-[#e8e8ee]">
      <img class="block size-full object-cover transition-transform duration-500 group-hover/card:scale-[1.04] motion-reduce:transition-none motion-reduce:group-hover/card:scale-100" src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?auto=format&fit=crop&w=600&q=70" alt="White minimalist wristwatch with a silicone strap on a pale grey surface" width="600" height="600" loading="lazy">
      <span class="absolute top-3 left-3 rounded-full bg-white px-2.5 py-[3px] text-[.72rem] font-bold tracking-[.04em]">New</span>
      <div class="absolute inset-x-3 bottom-3 flex translate-y-2.5 gap-2 opacity-0 transition duration-200 group-focus-within/card:translate-y-0 group-focus-within/card:opacity-100 group-hover/card:translate-y-0 group-hover/card:opacity-100 motion-reduce:transition-none [@media(hover:none)]:translate-y-0 [@media(hover:none)]:opacity-100">
        <button type="button" class="inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none flex-1 px-3.5" data-quick-view="qv-watch"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>Quick view<span class="sr-only">: Linea Minimal Watch</span></button>
        <button type="button" class="inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px]" aria-label="Add Linea Minimal Watch to cart"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
        <button type="button" class="qv-wish group/w inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px] aria-pressed:text-[#be123c] aria-pressed:hover:text-white" aria-pressed="false" aria-label="Save Linea Minimal Watch to wishlist"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path class="group-aria-pressed/w:fill-current" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
      </div>
    </div>
    <div class="flex justify-between gap-3 px-1 pt-3.5"><div><h3 class="text-[.98rem] leading-[1.35] font-semibold"><a href="#" class="underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Linea Minimal Watch</a></h3><p class="mt-0.5 text-[.8rem] text-[#52525b]">3 strap colours</p></div><p class="text-base font-bold whitespace-nowrap">$189</p></div>
  </li>
  <li class="group/card relative">
    <div class="relative aspect-square overflow-hidden rounded-[18px] bg-[#e8e8ee]">
      <img class="block size-full object-cover transition-transform duration-500 group-hover/card:scale-[1.04] motion-reduce:transition-none motion-reduce:group-hover/card:scale-100" src="https://images.unsplash.com/photo-1583394838336-acd977736f90?auto=format&fit=crop&w=600&q=70" alt="Grey over-ear headphones with padded cups on a white background" width="600" height="600" loading="lazy">
      <div class="absolute inset-x-3 bottom-3 flex translate-y-2.5 gap-2 opacity-0 transition duration-200 group-focus-within/card:translate-y-0 group-focus-within/card:opacity-100 group-hover/card:translate-y-0 group-hover/card:opacity-100 motion-reduce:transition-none [@media(hover:none)]:translate-y-0 [@media(hover:none)]:opacity-100">
        <button type="button" class="inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none flex-1 px-3.5" data-quick-view="qv-phones"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>Quick view<span class="sr-only">: Studio Over-Ear Headphones</span></button>
        <button type="button" class="inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px]" aria-label="Add Studio Over-Ear Headphones to cart"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
        <button type="button" class="qv-wish group/w inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px] aria-pressed:text-[#be123c] aria-pressed:hover:text-white" aria-pressed="false" aria-label="Save Studio Over-Ear Headphones to wishlist"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path class="group-aria-pressed/w:fill-current" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
      </div>
    </div>
    <div class="flex justify-between gap-3 px-1 pt-3.5"><div><h3 class="text-[.98rem] leading-[1.35] font-semibold"><a href="#" class="underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Studio Over-Ear Headphones</a></h3><p class="mt-0.5 text-[.8rem] text-[#52525b]">Wired · 40 mm drivers</p></div><p class="text-base font-bold whitespace-nowrap">$129</p></div>
  </li>
  <li class="group/card relative">
    <div class="relative aspect-square overflow-hidden rounded-[18px] bg-[#e8e8ee]">
      <img class="block size-full object-cover transition-transform duration-500 group-hover/card:scale-[1.04] motion-reduce:transition-none motion-reduce:group-hover/card:scale-100" src="https://images.unsplash.com/photo-1584917865442-de89df76afd3?auto=format&fit=crop&w=600&q=70" alt="Glossy red top-handle handbag with a silver clasp" width="600" height="600" loading="lazy">
      <span class="absolute top-3 left-3 rounded-full bg-white px-2.5 py-[3px] text-[.72rem] font-bold tracking-[.04em]">Limited</span>
      <div class="absolute inset-x-3 bottom-3 flex translate-y-2.5 gap-2 opacity-0 transition duration-200 group-focus-within/card:translate-y-0 group-focus-within/card:opacity-100 group-hover/card:translate-y-0 group-hover/card:opacity-100 motion-reduce:transition-none [@media(hover:none)]:translate-y-0 [@media(hover:none)]:opacity-100">
        <button type="button" class="inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none flex-1 px-3.5" data-quick-view="qv-bag"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>Quick view<span class="sr-only">: Lacquer Top-Handle Bag</span></button>
        <button type="button" class="inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px]" aria-label="Add Lacquer Top-Handle Bag to cart"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
        <button type="button" class="qv-wish group/w inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px] aria-pressed:text-[#be123c] aria-pressed:hover:text-white" aria-pressed="true" aria-label="Save Lacquer Top-Handle Bag to wishlist"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path class="group-aria-pressed/w:fill-current" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
      </div>
    </div>
    <div class="flex justify-between gap-3 px-1 pt-3.5"><div><h3 class="text-[.98rem] leading-[1.35] font-semibold"><a href="#" class="underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Lacquer Top-Handle Bag</a></h3><p class="mt-0.5 text-[.8rem] text-[#52525b]">Patent leather</p></div><p class="text-base font-bold whitespace-nowrap">$265</p></div>
  </li>
</ul>

<dialog class="qv-dialog m-auto max-h-[calc(100%-32px)] w-[min(760px,calc(100%-32px))] rounded-[20px] bg-white text-[#18181b] shadow-[0_30px_80px_-20px_rgba(24,24,27,.45)] backdrop:bg-[#18181b]/55 open:animate-qv-in motion-reduce:open:animate-none" id="qv-watch" aria-labelledby="qv-watch-title">
  <div class="relative grid grid-cols-1 sm:grid-cols-2">
    <img class="block aspect-[16/10] h-auto w-full bg-[#e8e8ee] object-cover sm:aspect-square sm:h-full" src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?auto=format&fit=crop&w=800&q=70" alt="White minimalist wristwatch with a silicone strap on a pale grey surface" width="800" height="800" loading="lazy">
    <div class="flex flex-col gap-2.5 px-[26px] pt-7 pb-[26px] sm:justify-center">
      <h2 id="qv-watch-title" class="pr-10 text-[1.4rem] leading-[1.25] font-bold tracking-[-.01em]">Linea Minimal Watch</h2>
      <p class="flex items-center gap-1.5 text-[.8rem] text-[#52525b]"><span class="bg-[linear-gradient(90deg,#d97706_92%,#d4d4d8_0)] bg-clip-text text-[.9rem] leading-none tracking-[1px] text-transparent" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.6 out of 5, 182 reviews</span><span aria-hidden="true">4.6 (182 reviews)</span></p>
      <p class="text-[1.3rem] font-bold whitespace-nowrap">$189</p>
      <p class="text-[.9rem] text-[#52525b]">A 38 mm brushed steel case with a matte black dial, sapphire-coated glass and a quick-release silicone strap. Water resistant to 50 m.</p>
      <p class="text-[.8rem] text-[#52525b]">Free shipping over $100 · 30-day returns</p>
      <button type="button" class="mt-2 cursor-pointer rounded-xl bg-[#4338ca] px-[18px] py-[13px] text-[.95rem] font-semibold text-white transition-colors duration-150 hover:bg-[#3730a3] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none">Add<span class="sr-only"> Linea Minimal Watch</span> to cart</button>
      <form method="dialog"><button class="absolute top-4 right-4 grid size-9 cursor-pointer place-items-center rounded-full bg-[#f4f4f5] hover:bg-[#e4e4e7] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]" aria-label="Close quick view"><svg class="size-[18px] fill-none stroke-current stroke-[2.2] [stroke-linecap:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button></form>
    </div>
  </div>
</dialog>
<dialog class="qv-dialog m-auto max-h-[calc(100%-32px)] w-[min(760px,calc(100%-32px))] rounded-[20px] bg-white text-[#18181b] shadow-[0_30px_80px_-20px_rgba(24,24,27,.45)] backdrop:bg-[#18181b]/55 open:animate-qv-in motion-reduce:open:animate-none" id="qv-phones" aria-labelledby="qv-phones-title">
  <div class="relative grid grid-cols-1 sm:grid-cols-2">
    <img class="block aspect-[16/10] h-auto w-full bg-[#e8e8ee] object-cover sm:aspect-square sm:h-full" src="https://images.unsplash.com/photo-1583394838336-acd977736f90?auto=format&fit=crop&w=800&q=70" alt="Grey over-ear headphones with padded cups on a white background" width="800" height="800" loading="lazy">
    <div class="flex flex-col gap-2.5 px-[26px] pt-7 pb-[26px] sm:justify-center">
      <h2 id="qv-phones-title" class="pr-10 text-[1.4rem] leading-[1.25] font-bold tracking-[-.01em]">Studio Over-Ear Headphones</h2>
      <p class="flex items-center gap-1.5 text-[.8rem] text-[#52525b]"><span class="bg-[linear-gradient(90deg,#d97706_88%,#d4d4d8_0)] bg-clip-text text-[.9rem] leading-none tracking-[1px] text-transparent" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.4 out of 5, 641 reviews</span><span aria-hidden="true">4.4 (641 reviews)</span></p>
      <p class="text-[1.3rem] font-bold whitespace-nowrap">$129</p>
      <p class="text-[.9rem] text-[#52525b]">Closed-back monitoring headphones with 40 mm drivers, memory-foam cushions and a detachable 3 m coiled cable. Fold flat for travel.</p>
      <p class="text-[.8rem] text-[#52525b]">Free shipping over $100 · 2-year warranty</p>
      <button type="button" class="mt-2 cursor-pointer rounded-xl bg-[#4338ca] px-[18px] py-[13px] text-[.95rem] font-semibold text-white transition-colors duration-150 hover:bg-[#3730a3] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none">Add<span class="sr-only"> Studio Over-Ear Headphones</span> to cart</button>
      <form method="dialog"><button class="absolute top-4 right-4 grid size-9 cursor-pointer place-items-center rounded-full bg-[#f4f4f5] hover:bg-[#e4e4e7] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]" aria-label="Close quick view"><svg class="size-[18px] fill-none stroke-current stroke-[2.2] [stroke-linecap:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button></form>
    </div>
  </div>
</dialog>
<dialog class="qv-dialog m-auto max-h-[calc(100%-32px)] w-[min(760px,calc(100%-32px))] rounded-[20px] bg-white text-[#18181b] shadow-[0_30px_80px_-20px_rgba(24,24,27,.45)] backdrop:bg-[#18181b]/55 open:animate-qv-in motion-reduce:open:animate-none" id="qv-bag" aria-labelledby="qv-bag-title">
  <div class="relative grid grid-cols-1 sm:grid-cols-2">
    <img class="block aspect-[16/10] h-auto w-full bg-[#e8e8ee] object-cover sm:aspect-square sm:h-full" src="https://images.unsplash.com/photo-1584917865442-de89df76afd3?auto=format&fit=crop&w=800&q=70" alt="Glossy red top-handle handbag with a silver clasp" width="800" height="800" loading="lazy">
    <div class="flex flex-col gap-2.5 px-[26px] pt-7 pb-[26px] sm:justify-center">
      <h2 id="qv-bag-title" class="pr-10 text-[1.4rem] leading-[1.25] font-bold tracking-[-.01em]">Lacquer Top-Handle Bag</h2>
      <p class="flex items-center gap-1.5 text-[.8rem] text-[#52525b]"><span class="bg-[linear-gradient(90deg,#d97706_96%,#d4d4d8_0)] bg-clip-text text-[.9rem] leading-none tracking-[1px] text-transparent" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.8 out of 5, 54 reviews</span><span aria-hidden="true">4.8 (54 reviews)</span></p>
      <p class="text-[1.3rem] font-bold whitespace-nowrap">$265</p>
      <p class="text-[.9rem] text-[#52525b]">Structured patent leather with a turn-lock clasp, suede lining and a detachable shoulder strap. Fits a phone, wallet and keys.</p>
      <p class="text-[.8rem] text-[#52525b]">Limited run of 300 · Free returns</p>
      <button type="button" class="mt-2 cursor-pointer rounded-xl bg-[#4338ca] px-[18px] py-[13px] text-[.95rem] font-semibold text-white transition-colors duration-150 hover:bg-[#3730a3] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none">Add<span class="sr-only"> Lacquer Top-Handle Bag</span> to cart</button>
      <form method="dialog"><button class="absolute top-4 right-4 grid size-9 cursor-pointer place-items-center rounded-full bg-[#f4f4f5] hover:bg-[#e4e4e7] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]" aria-label="Close quick view"><svg class="size-[18px] fill-none stroke-current stroke-[2.2] [stroke-linecap:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button></form>
    </div>
  </div>
</dialog></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#52525b] uppercase">Icon rail (hover or Tab into a card)</span>
<ul class="grid grid-cols-[repeat(auto-fill,minmax(230px,1fr))] gap-x-5 gap-y-6">
  <li class="group/card relative">
    <div class="relative aspect-square overflow-hidden rounded-[18px] bg-[#e8e8ee]">
      <img class="block size-full object-cover transition-transform duration-500 group-hover/card:scale-[1.04] motion-reduce:transition-none motion-reduce:group-hover/card:scale-100" src="https://images.unsplash.com/photo-1602810318383-e386cc2a3ccf?auto=format&fit=crop&w=600&q=70" alt="Three folded dress shirts in grey, white and burgundy on a wooden board" width="600" height="600" loading="lazy">
      <div class="absolute top-3 right-3 flex translate-x-2.5 flex-col gap-2 opacity-0 transition duration-200 group-focus-within/card:translate-x-0 group-focus-within/card:opacity-100 group-hover/card:translate-x-0 group-hover/card:opacity-100 motion-reduce:transition-none [@media(hover:none)]:translate-x-0 [@media(hover:none)]:opacity-100">
        <button type="button" class="qv-wish group/w inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px] aria-pressed:text-[#be123c] aria-pressed:hover:text-white" aria-pressed="false" aria-label="Save Oxford Shirt Trio to wishlist"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path class="group-aria-pressed/w:fill-current" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
        <button type="button" class="qv-wish inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px] aria-pressed:bg-[#4338ca] aria-pressed:text-white" aria-pressed="false" aria-label="Compare Oxford Shirt Trio"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4v16M17 4v16M3 8l4-4 4 4M13 16l4 4 4-4"/></svg></button>
        <button type="button" class="inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px]" aria-label="Add Oxford Shirt Trio to cart"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
      </div>
    </div>
    <div class="flex justify-between gap-3 px-1 pt-3.5"><div><h3 class="text-[.98rem] leading-[1.35] font-semibold"><a href="#" class="underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Oxford Shirt Trio</a></h3><p class="mt-0.5 text-[.8rem] text-[#52525b]">Set of 3 · slim fit</p></div><p class="text-base font-bold whitespace-nowrap">$138</p></div>
  </li>
  <li class="group/card relative">
    <div class="relative aspect-square overflow-hidden rounded-[18px] bg-[#e8e8ee]">
      <img class="block size-full object-cover transition-transform duration-500 group-hover/card:scale-[1.04] motion-reduce:transition-none motion-reduce:group-hover/card:scale-100" src="https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?auto=format&fit=crop&w=600&q=70" alt="Woman in a bright yellow fleece tracksuit standing on a beach promenade" width="600" height="600" loading="lazy">
      <span class="absolute top-3 left-3 rounded-full bg-white px-2.5 py-[3px] text-[.72rem] font-bold tracking-[.04em]">Trending</span>
      <div class="absolute top-3 right-3 flex translate-x-2.5 flex-col gap-2 opacity-0 transition duration-200 group-focus-within/card:translate-x-0 group-focus-within/card:opacity-100 group-hover/card:translate-x-0 group-hover/card:opacity-100 motion-reduce:transition-none [@media(hover:none)]:translate-x-0 [@media(hover:none)]:opacity-100">
        <button type="button" class="qv-wish group/w inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px] aria-pressed:text-[#be123c] aria-pressed:hover:text-white" aria-pressed="true" aria-label="Save Sunday Fleece Set to wishlist"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path class="group-aria-pressed/w:fill-current" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
        <button type="button" class="qv-wish inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px] aria-pressed:bg-[#4338ca] aria-pressed:text-white" aria-pressed="false" aria-label="Compare Sunday Fleece Set"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4v16M17 4v16M3 8l4-4 4 4M13 16l4 4 4-4"/></svg></button>
        <button type="button" class="inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px]" aria-label="Add Sunday Fleece Set to cart"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
      </div>
    </div>
    <div class="flex justify-between gap-3 px-1 pt-3.5"><div><h3 class="text-[.98rem] leading-[1.35] font-semibold"><a href="#" class="underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Sunday Fleece Set</a></h3><p class="mt-0.5 text-[.8rem] text-[#52525b]">Hoodie and joggers</p></div><p class="text-base font-bold whitespace-nowrap">$96</p></div>
  </li>
  <li class="group/card relative">
    <div class="relative aspect-square overflow-hidden rounded-[18px] bg-[#e8e8ee]">
      <img class="block size-full object-cover transition-transform duration-500 group-hover/card:scale-[1.04] motion-reduce:transition-none motion-reduce:group-hover/card:scale-100" src="https://images.unsplash.com/photo-1596462502278-27bfdc403348?auto=format&fit=crop&w=600&q=70" alt="Makeup brushes, lipsticks and a bronzer compact laid out on a beige background" width="600" height="600" loading="lazy">
      <div class="absolute top-3 right-3 flex translate-x-2.5 flex-col gap-2 opacity-0 transition duration-200 group-focus-within/card:translate-x-0 group-focus-within/card:opacity-100 group-hover/card:translate-x-0 group-hover/card:opacity-100 motion-reduce:transition-none [@media(hover:none)]:translate-x-0 [@media(hover:none)]:opacity-100">
        <button type="button" class="qv-wish group/w inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px] aria-pressed:text-[#be123c] aria-pressed:hover:text-white" aria-pressed="false" aria-label="Save Rose Brush and Palette Kit to wishlist"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path class="group-aria-pressed/w:fill-current" d="M12 20.5s-7.5-4.6-9.4-9.1C1.1 7.9 3.3 4.5 6.8 4.5c2.1 0 3.7 1.2 5.2 3.1 1.5-1.9 3.1-3.1 5.2-3.1 3.5 0 5.7 3.4 4.2 6.9-1.9 4.5-9.4 9.1-9.4 9.1z"/></svg></button>
        <button type="button" class="qv-wish inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px] aria-pressed:bg-[#4338ca] aria-pressed:text-white" aria-pressed="false" aria-label="Compare Rose Brush and Palette Kit"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4v16M17 4v16M3 8l4-4 4 4M13 16l4 4 4-4"/></svg></button>
        <button type="button" class="inline-flex h-[42px] cursor-pointer items-center justify-center gap-1.5 rounded-xl bg-white text-[.85rem] font-semibold shadow-[0_4px_14px_rgba(24,24,27,.16)] transition-colors duration-150 hover:bg-[#18181b] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none w-[42px]" aria-label="Add Rose Brush and Palette Kit to cart"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linecap:round] [stroke-linejoin:round]" viewBox="0 0 24 24" aria-hidden="true"><path d="M5.5 8h13l-1 12h-11z"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8"/></svg></button>
      </div>
    </div>
    <div class="flex justify-between gap-3 px-1 pt-3.5"><div><h3 class="text-[.98rem] leading-[1.35] font-semibold"><a href="#" class="underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Rose Brush and Palette Kit</a></h3><p class="mt-0.5 text-[.8rem] text-[#52525b]">9 pieces · vegan bristles</p></div><p class="text-base font-bold whitespace-nowrap">$58</p></div>
  </li>
</ul></div>
CSS
/* Dialog entrance: a keyframe has no utility equivalent. */
@theme { --animate-qv-in: qv-in .22s ease-out; }
@keyframes qv-in { from { opacity: 0; transform: translateY(12px) scale(.98); } }
JavaScript
// Quick view: each button opens its <dialog> as a modal; clicking the dim backdrop also closes it.
document.querySelectorAll('[data-quick-view]').forEach(btn => btn.addEventListener('click', () => {
  document.getElementById(btn.dataset.quickView).showModal();
}));
document.querySelectorAll('.qv-dialog').forEach(dlg => dlg.addEventListener('click', e => { if (e.target === dlg) dlg.close(); }));
// Wishlist and compare are toggle buttons: flip aria-pressed on click.
document.querySelectorAll('.qv-wish').forEach(btn => btn.addEventListener('click', () => {
  btn.setAttribute('aria-pressed', String(btn.getAttribute('aria-pressed') !== 'true'));
}));

About this set

Product cards that keep the photo clean until the shopper shows interest, then reveal their actions. Action bar with quick view slides a white bar up over the photo with a Quick view button, an add to cart icon button and a wishlist heart. Quick view opens a modal with a larger photo, the rating, price, a short description, delivery terms and an Add to cart button, so a shopper can check a watch, headphones or a bag without leaving the grid. Icon rail slides a column of round buttons in from the right edge for wishlist, compare and add to cart, which suits fashion and beauty grids. The actions appear on hover and also whenever anything inside the card has keyboard focus, through :focus-within, so Tab reaches them without a mouse; on touch screens, which have no hover, they are always shown. The quick view is a native dialog opened with showModal(), which traps focus, closes with Esc or a click on the backdrop and returns focus to the button that opened it. The Bootstrap version uses its .modal and data-bs-toggle=”button”, and the Tailwind version uses group-hover:, group-focus-within: and backdrop: utilities.

What’s included

  • Action bar and icon rail that show on hover and on keyboard focus
  • Actions always visible on touch screens
  • Quick view in a native dialog or a Bootstrap .modal
  • Wishlist and compare toggles with aria-pressed
  • Gentle zoom on the photo that respects reduced motion

Accessibility

No action is hover-only: :focus-within reveals the bar or rail as soon as a button inside it is focused, and touch screens always show it. Icon buttons have names such as Add Linea Minimal Watch to cart and Compare Oxford Shirt Trio, and Quick view is named for its product. The dialog is labelled by the product name, traps focus, closes with Esc and returns focus to its trigger. Focus outlines are 3px, and the slide, zoom and dialog animations stop under prefers-reduced-motion.

Customise it

Change --qv-accent, --qv-heart, --qv-media and --qv-focus on .qv and .qv-dialog, and the width of .qv-dialog. In Bootstrap set --bs-modal-width on .qv-modal; in Tailwind edit the bg-[#4338ca] button and the w-[min(760px,…)] dialog width.