Open demo

CSS code

HTML
<div><span class="lbl">Shop grid with wishlist</span>
<ul class="pc">
  <li><article class="pc-card">
    <div class="pc-media">
      <img src="https://images.unsplash.com/photo-1596755094514-f87e34085b2c?auto=format&fit=crop&w=600&q=70" alt="Blue chambray shirt with a small white dot print on a hanger" width="600" height="750" loading="lazy">
      <span class="pc-badge">New</span>
      <button type="button" class="pc-wish" aria-pressed="false" aria-label="Save Dotted Chambray Shirt to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path 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 class="pc-body">
      <p class="pc-cat">Shirts</p>
      <h3 class="pc-title"><a href="#">Dotted Chambray Shirt</a></h3>
      <p class="pc-rating"><span class="pc-stars" style="--r:4.5" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.5 out of 5, 128 reviews</span><span aria-hidden="true">4.5 (128)</span></p>
      <p class="pc-price">$64</p>
      <button type="button" class="pc-add"><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><span>Add<span class="sr-only"> Dotted Chambray Shirt</span> to cart</span></button>
    </div>
  </article></li>
  <li><article class="pc-card">
    <div class="pc-media">
      <img src="https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?auto=format&fit=crop&w=600&q=70" alt="Man wearing a plain white crew-neck T-shirt" width="600" height="750" loading="lazy">
      <span class="pc-badge is-sale">−20%</span>
      <button type="button" class="pc-wish" aria-pressed="true" aria-label="Save Everyday Cotton Tee to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path 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 class="pc-body">
      <p class="pc-cat">T-shirts</p>
      <h3 class="pc-title"><a href="#">Everyday Cotton Tee</a></h3>
      <p class="pc-rating"><span class="pc-stars" style="--r:4.8" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.8 out of 5, 412 reviews</span><span aria-hidden="true">4.8 (412)</span></p>
      <p class="pc-price"><span class="sr-only">Sale price</span><span class="is-now">$24</span> <s><span class="sr-only">Was</span> $30</s></p>
      <button type="button" class="pc-add"><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><span>Add<span class="sr-only"> Everyday Cotton Tee</span> to cart</span></button>
    </div>
  </article></li>
  <li><article class="pc-card">
    <div class="pc-media">
      <img src="https://images.unsplash.com/photo-1591047139829-d91aecb6caea?auto=format&fit=crop&w=600&q=70" alt="Rust-coloured bomber jacket with a zip front hanging on a wall" width="600" height="750" loading="lazy">
      <button type="button" class="pc-wish" aria-pressed="false" aria-label="Save Field Bomber Jacket to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path 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 class="pc-body">
      <p class="pc-cat">Outerwear</p>
      <h3 class="pc-title"><a href="#">Field Bomber Jacket</a></h3>
      <p class="pc-rating"><span class="pc-stars" style="--r:4.2" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.2 out of 5, 57 reviews</span><span aria-hidden="true">4.2 (57)</span></p>
      <p class="pc-price">$148</p>
      <button type="button" class="pc-add"><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><span>Add<span class="sr-only"> Field Bomber Jacket</span> to cart</span></button>
    </div>
  </article></li>
</ul></div>

<div><span class="lbl">Centered minimal cards</span>
<ul class="pc is-minimal">
  <li><article class="pc-card">
    <div class="pc-media">
      <img src="https://images.unsplash.com/photo-1514228742587-6b1558fcca3d?auto=format&fit=crop&w=600&q=70" alt="Plain white stoneware mug on a white table" width="600" height="600" loading="lazy">
      <span class="pc-badge">Bestseller</span>
    </div>
    <div class="pc-body">
      <h3 class="pc-title"><a href="#">Stoneware Mug</a></h3>
      <p class="pc-rating"><span class="pc-stars" style="--r:4.9" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.9 out of 5, 236 reviews</span><span aria-hidden="true">4.9 (236)</span></p>
      <p class="pc-price">$18</p>
      <button type="button" class="pc-add"><span>Add<span class="sr-only"> Stoneware Mug</span> to cart</span></button>
    </div>
  </article></li>
  <li><article class="pc-card">
    <div class="pc-media">
      <img src="https://images.unsplash.com/photo-1503602642458-232111445657?auto=format&fit=crop&w=600&q=70" alt="Pale wooden bar stool against a light blue wall" width="600" height="600" loading="lazy">
      <span class="pc-badge is-low">Only 3 left</span>
    </div>
    <div class="pc-body">
      <h3 class="pc-title"><a href="#">Oak Bar Stool</a></h3>
      <p class="pc-rating"><span class="pc-stars" style="--r:4.4" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.4 out of 5, 39 reviews</span><span aria-hidden="true">4.4 (39)</span></p>
      <p class="pc-price">$129</p>
      <button type="button" class="pc-add"><span>Add<span class="sr-only"> Oak Bar Stool</span> to cart</span></button>
    </div>
  </article></li>
  <li><article class="pc-card">
    <div class="pc-media">
      <img src="https://images.unsplash.com/photo-1485955900006-10f4d324d411?auto=format&fit=crop&w=600&q=70" alt="Small striped succulent in a mint green ceramic pot" width="600" height="600" loading="lazy">
    </div>
    <div class="pc-body">
      <h3 class="pc-title"><a href="#">Potted Zebra Haworthia</a></h3>
      <p class="pc-rating"><span class="pc-stars" style="--r:4.7" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.7 out of 5, 88 reviews</span><span aria-hidden="true">4.7 (88)</span></p>
      <p class="pc-price">$22</p>
      <button type="button" class="pc-add"><span>Add<span class="sr-only"> Potted Zebra Haworthia</span> to cart</span></button>
    </div>
  </article></li>
</ul></div>
CSS
/* Classic shop cards: photo with badge and wishlist toggle, category, title link, rating, price and a full-width add-to-cart button. */
.pc{--pc-ink:#1c1917;--pc-muted:#57534e;--pc-line:#e7e5e4;--pc-card:#fff;--pc-media:#efebe6;--pc-star:#d97706;--pc-star-off:#d6d3d1;
  --pc-sale:#b91c1c;--pc-heart:#be123c;--pc-focus:#2563eb;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:22px;list-style:none;margin:0;padding:0}
.pc-card{position:relative;display:flex;flex-direction:column;height:100%;background:var(--pc-card);border:1px solid var(--pc-line);
  border-radius:16px;overflow:hidden;transition:box-shadow .2s,transform .2s}
.pc-card:hover{box-shadow:0 14px 30px -14px rgba(28,25,23,.28);transform:translateY(-2px)}
.pc-media{position:relative;aspect-ratio:4/5;background:var(--pc-media);overflow:hidden}
.pc-media img{display:block;width:100%;height:100%;object-fit:cover}
.pc-badge{position:absolute;top:12px;left:12px;padding:4px 10px;border-radius:999px;background:var(--pc-ink);color:#fff;
  font-size:.72rem;font-weight:700;letter-spacing:.04em;line-height:1.4}
.pc-badge.is-sale{background:var(--pc-sale)}
.pc-badge.is-low{background:#fef3c7;color:#78350f}
.pc-wish{position:absolute;top:10px;right:10px;display:grid;place-items:center;width:38px;height:38px;padding:0;border:0;border-radius:50%;
  background:#fff;color:var(--pc-ink);box-shadow:0 2px 8px rgba(28,25,23,.18);cursor:pointer}
.pc-wish svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linejoin:round}
.pc-wish[aria-pressed="true"]{color:var(--pc-heart)}
.pc-wish[aria-pressed="true"] svg{fill:currentColor}
.pc-body{display:flex;flex:1;flex-direction:column;gap:6px;padding:16px 16px 18px}
.pc-cat{margin:0;font-size:.72rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--pc-muted)}
.pc-title{margin:0;font-size:1rem;font-weight:600;line-height:1.35}
.pc-title a{color:var(--pc-ink);text-decoration:none}
.pc-title a:hover{text-decoration:underline;text-underline-offset:3px}
.pc-rating{display:flex;align-items:center;gap:6px;margin:0;font-size:.8rem;color:var(--pc-muted)}
.pc-stars{--r:5;font-size:.95rem;letter-spacing:1px;line-height:1;color:transparent;
  background:linear-gradient(90deg,var(--pc-star) calc(var(--r) / 5 * 100%),var(--pc-star-off) 0);-webkit-background-clip:text;background-clip:text}
.pc-price{display:flex;align-items:baseline;gap:8px;margin:2px 0 12px;font-size:1.15rem;font-weight:700;color:var(--pc-ink)}
.pc-price .is-now{color:var(--pc-sale)}
.pc-price s{font-size:.875rem;font-weight:500;color:#78716c}
.pc-add{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;margin-top:auto;padding:11px 14px;border:0;border-radius:10px;
  background:var(--pc-ink);color:#fff;font:inherit;font-size:.9rem;font-weight:600;cursor:pointer;transition:background-color .2s,color .2s}
.pc-add:hover{background:#44403c}
.pc-add svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linejoin:round}
.pc :is(a,button):focus-visible{outline:3px solid var(--pc-focus);outline-offset:2px}
/* Centered minimal variant */
.pc.is-minimal .pc-card{border:0;background:transparent;text-align:center}
.pc.is-minimal .pc-card:hover{box-shadow:none;transform:none}
.pc.is-minimal .pc-media{aspect-ratio:1;border-radius:14px}
.pc.is-minimal .pc-body{align-items:center;padding:14px 4px 0}
.pc.is-minimal .pc-add{width:auto;padding:9px 22px;border:1.5px solid var(--pc-ink);border-radius:999px;background:transparent;color:var(--pc-ink)}
.pc.is-minimal .pc-add:hover{background:var(--pc-ink);color:#fff}
.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){.pc-card,.pc-add{transition:none}.pc-card:hover{transform:none}}
JavaScript
// Wishlist hearts: flip aria-pressed on click (the CSS fills the heart when it is "true").
document.querySelectorAll('.pc-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">Shop grid with wishlist</span>
<ul class="pc row row-cols-1 row-cols-sm-2 row-cols-md-3 list-unstyled mb-0">
  <li class="col"><article class="card h-100">
    <div class="position-relative"><div class="ratio">
      <img src="https://images.unsplash.com/photo-1596755094514-f87e34085b2c?auto=format&fit=crop&w=600&q=70" alt="Blue chambray shirt with a small white dot print on a hanger" width="600" height="750" loading="lazy"></div>
      <span class="badge rounded-pill text-bg-dark position-absolute">New</span>
      <button type="button" class="btn pc-wish rounded-circle position-absolute" data-bs-toggle="button" aria-pressed="false" aria-label="Save Dotted Chambray Shirt to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path 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 class="card-body d-flex flex-column pb-3">
      <p class="pc-cat fw-semibold text-uppercase mb-0">Shirts</p>
      <h3 class="pc-title card-title fw-semibold mb-0"><a href="#" class="text-decoration-none">Dotted Chambray Shirt</a></h3>
      <p class="pc-rating d-flex align-items-center gap-2 mb-0"><span class="pc-stars" style="--r:4.5" aria-hidden="true">★★★★★</span><span class="visually-hidden">Rated 4.5 out of 5, 128 reviews</span><span aria-hidden="true">4.5 (128)</span></p>
      <p class="pc-price fw-bold">$64</p>
      <button type="button" class="btn btn-dark pc-add w-100 mt-auto d-flex align-items-center justify-content-center gap-2"><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><span>Add<span class="visually-hidden"> Dotted Chambray Shirt</span> to cart</span></button>
    </div>
  </article></li>
  <li class="col"><article class="card h-100">
    <div class="position-relative"><div class="ratio">
      <img src="https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?auto=format&fit=crop&w=600&q=70" alt="Man wearing a plain white crew-neck T-shirt" width="600" height="750" loading="lazy"></div>
      <span class="badge rounded-pill position-absolute" style="background:var(--pc-sale)">−20%</span>
      <button type="button" class="btn pc-wish rounded-circle position-absolute active" data-bs-toggle="button" aria-pressed="true" aria-label="Save Everyday Cotton Tee to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path 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 class="card-body d-flex flex-column pb-3">
      <p class="pc-cat fw-semibold text-uppercase mb-0">T-shirts</p>
      <h3 class="pc-title card-title fw-semibold mb-0"><a href="#" class="text-decoration-none">Everyday Cotton Tee</a></h3>
      <p class="pc-rating d-flex align-items-center gap-2 mb-0"><span class="pc-stars" style="--r:4.8" aria-hidden="true">★★★★★</span><span class="visually-hidden">Rated 4.8 out of 5, 412 reviews</span><span aria-hidden="true">4.8 (412)</span></p>
      <p class="pc-price fw-bold d-flex align-items-baseline gap-2"><span class="visually-hidden">Sale price</span><span style="color:var(--pc-sale)">$24</span> <s><span class="visually-hidden">Was</span> $30</s></p>
      <button type="button" class="btn btn-dark pc-add w-100 mt-auto d-flex align-items-center justify-content-center gap-2"><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><span>Add<span class="visually-hidden"> Everyday Cotton Tee</span> to cart</span></button>
    </div>
  </article></li>
  <li class="col"><article class="card h-100">
    <div class="position-relative"><div class="ratio">
      <img src="https://images.unsplash.com/photo-1591047139829-d91aecb6caea?auto=format&fit=crop&w=600&q=70" alt="Rust-coloured bomber jacket with a zip front hanging on a wall" width="600" height="750" loading="lazy"></div>
      <button type="button" class="btn pc-wish rounded-circle position-absolute" data-bs-toggle="button" aria-pressed="false" aria-label="Save Field Bomber Jacket to wishlist"><svg viewBox="0 0 24 24" aria-hidden="true"><path 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 class="card-body d-flex flex-column pb-3">
      <p class="pc-cat fw-semibold text-uppercase mb-0">Outerwear</p>
      <h3 class="pc-title card-title fw-semibold mb-0"><a href="#" class="text-decoration-none">Field Bomber Jacket</a></h3>
      <p class="pc-rating d-flex align-items-center gap-2 mb-0"><span class="pc-stars" style="--r:4.2" aria-hidden="true">★★★★★</span><span class="visually-hidden">Rated 4.2 out of 5, 57 reviews</span><span aria-hidden="true">4.2 (57)</span></p>
      <p class="pc-price fw-bold">$148</p>
      <button type="button" class="btn btn-dark pc-add w-100 mt-auto d-flex align-items-center justify-content-center gap-2"><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><span>Add<span class="visually-hidden"> Field Bomber Jacket</span> to cart</span></button>
    </div>
  </article></li>
</ul></div>

<div><span class="lbl">Centered minimal cards</span>
<ul class="pc is-minimal row row-cols-1 row-cols-sm-2 row-cols-md-3 list-unstyled mb-0">
  <li class="col"><article class="card h-100 text-center">
    <div class="position-relative"><div class="ratio">
      <img src="https://images.unsplash.com/photo-1514228742587-6b1558fcca3d?auto=format&fit=crop&w=600&q=70" alt="Plain white stoneware mug on a white table" width="600" height="600" loading="lazy"></div>
      <span class="badge rounded-pill text-bg-dark position-absolute">Bestseller</span>
    </div>
    <div class="card-body d-flex flex-column align-items-center pb-0">
      <h3 class="pc-title card-title fw-semibold mb-0"><a href="#" class="text-decoration-none">Stoneware Mug</a></h3>
      <p class="pc-rating d-flex align-items-center gap-2 mb-0"><span class="pc-stars" style="--r:4.9" aria-hidden="true">★★★★★</span><span class="visually-hidden">Rated 4.9 out of 5, 236 reviews</span><span aria-hidden="true">4.9 (236)</span></p>
      <p class="pc-price fw-bold">$18</p>
      <button type="button" class="btn pc-add mt-auto"><span>Add<span class="visually-hidden"> Stoneware Mug</span> to cart</span></button>
    </div>
  </article></li>
  <li class="col"><article class="card h-100 text-center">
    <div class="position-relative"><div class="ratio">
      <img src="https://images.unsplash.com/photo-1503602642458-232111445657?auto=format&fit=crop&w=600&q=70" alt="Pale wooden bar stool against a light blue wall" width="600" height="600" loading="lazy"></div>
      <span class="badge rounded-pill position-absolute" style="background:#fef3c7;color:#78350f">Only 3 left</span>
    </div>
    <div class="card-body d-flex flex-column align-items-center pb-0">
      <h3 class="pc-title card-title fw-semibold mb-0"><a href="#" class="text-decoration-none">Oak Bar Stool</a></h3>
      <p class="pc-rating d-flex align-items-center gap-2 mb-0"><span class="pc-stars" style="--r:4.4" aria-hidden="true">★★★★★</span><span class="visually-hidden">Rated 4.4 out of 5, 39 reviews</span><span aria-hidden="true">4.4 (39)</span></p>
      <p class="pc-price fw-bold">$129</p>
      <button type="button" class="btn pc-add mt-auto"><span>Add<span class="visually-hidden"> Oak Bar Stool</span> to cart</span></button>
    </div>
  </article></li>
  <li class="col"><article class="card h-100 text-center">
    <div class="ratio">
      <img src="https://images.unsplash.com/photo-1485955900006-10f4d324d411?auto=format&fit=crop&w=600&q=70" alt="Small striped succulent in a mint green ceramic pot" width="600" height="600" loading="lazy">
    </div>
    <div class="card-body d-flex flex-column align-items-center pb-0">
      <h3 class="pc-title card-title fw-semibold mb-0"><a href="#" class="text-decoration-none">Potted Zebra Haworthia</a></h3>
      <p class="pc-rating d-flex align-items-center gap-2 mb-0"><span class="pc-stars" style="--r:4.7" aria-hidden="true">★★★★★</span><span class="visually-hidden">Rated 4.7 out of 5, 88 reviews</span><span aria-hidden="true">4.7 (88)</span></p>
      <p class="pc-price fw-bold">$22</p>
      <button type="button" class="btn pc-add mt-auto"><span>Add<span class="visually-hidden"> Potted Zebra Haworthia</span> to cart</span></button>
    </div>
  </article></li>
</ul></div>
CSS
/* Classic shop cards on Bootstrap: .row-cols-* grid of .card, .ratio photo, .badge, a data-bs-toggle="button"
   wishlist (Bootstrap flips aria-pressed and .active) and a .btn-dark add-to-cart button. */
.pc{--pc-ink:#1c1917;--pc-muted:#57534e;--pc-star:#d97706;--pc-star-off:#d6d3d1;--pc-sale:#b91c1c;--pc-heart:#be123c;--pc-focus:#2563eb;
  --bs-gutter-x:22px;--bs-gutter-y:22px}
.pc .card{--bs-card-border-color:#e7e5e4;--bs-card-border-radius:16px;--bs-card-inner-border-radius:15px;--bs-card-spacer-x:16px;--bs-card-spacer-y:16px;
  overflow:hidden;transition:box-shadow .2s,transform .2s}
.pc .card:hover{box-shadow:0 14px 30px -14px rgba(28,25,23,.28);transform:translateY(-2px)}
.pc .ratio{--bs-aspect-ratio:125%;background:#efebe6}
.pc .ratio img{object-fit:cover}
.pc .badge{top:12px;left:12px;padding:4px 10px;font-size:.72rem;font-weight:700;letter-spacing:.04em;line-height:1.4}
.pc-wish{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-bg:#fff;--bs-btn-color:var(--pc-ink);--bs-btn-hover-bg:#fff;--bs-btn-hover-color:var(--pc-ink);
  --bs-btn-active-bg:#fff;--bs-btn-active-color:var(--pc-heart);--bs-btn-border-width:0;
  top:10px;right:10px;display:grid;place-items:center;width:38px;height:38px;box-shadow:0 2px 8px rgba(28,25,23,.18)}
.pc-wish svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linejoin:round}
.pc-wish.active svg{fill:currentColor}
.pc .card-body{gap:6px}
.pc-cat{font-size:.72rem;letter-spacing:.1em;color:var(--pc-muted)}
.pc-title{font-size:1rem;line-height:1.35}
.pc-title a{color:var(--pc-ink)}
.pc-title a:hover{text-decoration:underline!important;text-underline-offset:3px}
.pc-rating{font-size:.8rem;color:var(--pc-muted)}
.pc-stars{--r:5;font-size:.95rem;letter-spacing:1px;line-height:1;color:transparent;
  background:linear-gradient(90deg,var(--pc-star) calc(var(--r) / 5 * 100%),var(--pc-star-off) 0);-webkit-background-clip:text;background-clip:text}
.pc-price{font-size:1.15rem;color:var(--pc-ink);margin:2px 0 12px}
.pc-price s{font-size:.875rem;font-weight:500;color:#78716c}
.pc-add{--bs-btn-padding-y:11px;--bs-btn-font-size:.9rem;--bs-btn-font-weight:600;--bs-btn-border-radius:10px;--bs-btn-bg:var(--pc-ink);--bs-btn-border-color:var(--pc-ink);
  --bs-btn-hover-bg:#44403c;--bs-btn-hover-border-color:#44403c;--bs-btn-active-bg:#44403c;--bs-btn-active-border-color:#44403c}
.pc-add svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linejoin:round}
.pc :is(a,.btn):focus-visible{outline:3px solid var(--pc-focus);outline-offset:2px;box-shadow:none}
.pc-wish:focus-visible{box-shadow:0 2px 8px rgba(28,25,23,.18)!important}
/* Centered minimal variant */
.pc.is-minimal .card{--bs-card-border-width:0;--bs-card-bg:transparent;--bs-card-spacer-x:4px;--bs-card-spacer-y:14px}
.pc.is-minimal .card:hover{box-shadow:none;transform:none}
.pc.is-minimal .ratio{--bs-aspect-ratio:100%;border-radius:14px;overflow:hidden}
.pc.is-minimal .pc-add{--bs-btn-padding-x:22px;--bs-btn-padding-y:9px;--bs-btn-border-radius:999px;--bs-btn-border-width:1.5px;--bs-btn-bg:transparent;--bs-btn-color:var(--pc-ink);
  --bs-btn-hover-bg:var(--pc-ink);--bs-btn-hover-color:#fff;--bs-btn-hover-border-color:var(--pc-ink)}
@media (prefers-reduced-motion:reduce){.pc .card,.pc .btn{transition:none}.pc .card:hover{transform:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#57534e] uppercase">Shop grid with wishlist</span>
<ul class="grid grid-cols-[repeat(auto-fill,minmax(230px,1fr))] gap-[22px]">
  <li><article class="flex h-full flex-col overflow-hidden rounded-2xl border border-[#e7e5e4] bg-white transition duration-200 hover:-translate-y-0.5 hover:shadow-[0_14px_30px_-14px_rgba(28,25,23,.28)] motion-reduce:transition-none motion-reduce:hover:translate-none">
    <div class="relative aspect-[4/5] overflow-hidden bg-[#efebe6]">
      <img class="size-full object-cover" src="https://images.unsplash.com/photo-1596755094514-f87e34085b2c?auto=format&fit=crop&w=600&q=70" alt="Blue chambray shirt with a small white dot print on a hanger" width="600" height="750" loading="lazy">
      <span class="absolute top-3 left-3 rounded-full bg-[#1c1917] px-2.5 py-1 text-[.72rem] leading-[1.4] font-bold tracking-[.04em] text-white">New</span>
      <button type="button" class="pc-wish group absolute top-2.5 right-2.5 grid size-[38px] cursor-pointer place-items-center rounded-full bg-white text-[#1c1917] shadow-[0_2px_8px_rgba(28,25,23,.18)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] aria-pressed:text-[#be123c]" aria-pressed="false" aria-label="Save Dotted Chambray Shirt to wishlist"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linejoin:round] group-aria-pressed:fill-current" viewBox="0 0 24 24" aria-hidden="true"><path 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 class="flex flex-1 flex-col gap-1.5 px-4 pt-4 pb-[18px]">
      <p class="text-[.72rem] font-semibold tracking-[.1em] text-[#57534e] uppercase">Shirts</p>
      <h3 class="text-base leading-[1.35] font-semibold"><a href="#" class="text-[#1c1917] underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Dotted Chambray Shirt</a></h3>
      <p class="flex items-center gap-1.5 text-[.8rem] text-[#57534e]"><span class="bg-[linear-gradient(90deg,#d97706_90%,#d6d3d1_0)] bg-clip-text text-[.95rem] leading-none tracking-[1px] text-transparent" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.5 out of 5, 128 reviews</span><span aria-hidden="true">4.5 (128)</span></p>
      <p class="mt-0.5 mb-3 text-[1.15rem] font-bold">$64</p>
      <button type="button" class="mt-auto flex w-full cursor-pointer items-center justify-center gap-2 rounded-[10px] bg-[#1c1917] px-3.5 py-[11px] text-[.9rem] font-semibold text-white transition-colors duration-200 hover:bg-[#44403c] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none"><svg class="size-[18px] fill-none stroke-current stroke-2 [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><span>Add<span class="sr-only"> Dotted Chambray Shirt</span> to cart</span></button>
    </div>
  </article></li>
  <li><article class="flex h-full flex-col overflow-hidden rounded-2xl border border-[#e7e5e4] bg-white transition duration-200 hover:-translate-y-0.5 hover:shadow-[0_14px_30px_-14px_rgba(28,25,23,.28)] motion-reduce:transition-none motion-reduce:hover:translate-none">
    <div class="relative aspect-[4/5] overflow-hidden bg-[#efebe6]">
      <img class="size-full object-cover" src="https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?auto=format&fit=crop&w=600&q=70" alt="Man wearing a plain white crew-neck T-shirt" width="600" height="750" loading="lazy">
      <span class="absolute top-3 left-3 rounded-full bg-[#b91c1c] px-2.5 py-1 text-[.72rem] leading-[1.4] font-bold tracking-[.04em] text-white">−20%</span>
      <button type="button" class="pc-wish group absolute top-2.5 right-2.5 grid size-[38px] cursor-pointer place-items-center rounded-full bg-white text-[#1c1917] shadow-[0_2px_8px_rgba(28,25,23,.18)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] aria-pressed:text-[#be123c]" aria-pressed="true" aria-label="Save Everyday Cotton Tee to wishlist"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linejoin:round] group-aria-pressed:fill-current" viewBox="0 0 24 24" aria-hidden="true"><path 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 class="flex flex-1 flex-col gap-1.5 px-4 pt-4 pb-[18px]">
      <p class="text-[.72rem] font-semibold tracking-[.1em] text-[#57534e] uppercase">T-shirts</p>
      <h3 class="text-base leading-[1.35] font-semibold"><a href="#" class="text-[#1c1917] underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Everyday Cotton Tee</a></h3>
      <p class="flex items-center gap-1.5 text-[.8rem] text-[#57534e]"><span class="bg-[linear-gradient(90deg,#d97706_96%,#d6d3d1_0)] bg-clip-text text-[.95rem] leading-none tracking-[1px] text-transparent" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.8 out of 5, 412 reviews</span><span aria-hidden="true">4.8 (412)</span></p>
      <p class="mt-0.5 mb-3 flex items-baseline gap-2 text-[1.15rem] font-bold"><span class="sr-only">Sale price</span><span class="text-[#b91c1c]">$24</span> <s class="text-[.875rem] font-medium text-[#78716c]"><span class="sr-only">Was</span> $30</s></p>
      <button type="button" class="mt-auto flex w-full cursor-pointer items-center justify-center gap-2 rounded-[10px] bg-[#1c1917] px-3.5 py-[11px] text-[.9rem] font-semibold text-white transition-colors duration-200 hover:bg-[#44403c] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none"><svg class="size-[18px] fill-none stroke-current stroke-2 [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><span>Add<span class="sr-only"> Everyday Cotton Tee</span> to cart</span></button>
    </div>
  </article></li>
  <li><article class="flex h-full flex-col overflow-hidden rounded-2xl border border-[#e7e5e4] bg-white transition duration-200 hover:-translate-y-0.5 hover:shadow-[0_14px_30px_-14px_rgba(28,25,23,.28)] motion-reduce:transition-none motion-reduce:hover:translate-none">
    <div class="relative aspect-[4/5] overflow-hidden bg-[#efebe6]">
      <img class="size-full object-cover" src="https://images.unsplash.com/photo-1591047139829-d91aecb6caea?auto=format&fit=crop&w=600&q=70" alt="Rust-coloured bomber jacket with a zip front hanging on a wall" width="600" height="750" loading="lazy">
      <button type="button" class="pc-wish group absolute top-2.5 right-2.5 grid size-[38px] cursor-pointer place-items-center rounded-full bg-white text-[#1c1917] shadow-[0_2px_8px_rgba(28,25,23,.18)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] aria-pressed:text-[#be123c]" aria-pressed="false" aria-label="Save Field Bomber Jacket to wishlist"><svg class="size-[18px] fill-none stroke-current stroke-2 [stroke-linejoin:round] group-aria-pressed:fill-current" viewBox="0 0 24 24" aria-hidden="true"><path 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 class="flex flex-1 flex-col gap-1.5 px-4 pt-4 pb-[18px]">
      <p class="text-[.72rem] font-semibold tracking-[.1em] text-[#57534e] uppercase">Outerwear</p>
      <h3 class="text-base leading-[1.35] font-semibold"><a href="#" class="text-[#1c1917] underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Field Bomber Jacket</a></h3>
      <p class="flex items-center gap-1.5 text-[.8rem] text-[#57534e]"><span class="bg-[linear-gradient(90deg,#d97706_84%,#d6d3d1_0)] bg-clip-text text-[.95rem] leading-none tracking-[1px] text-transparent" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.2 out of 5, 57 reviews</span><span aria-hidden="true">4.2 (57)</span></p>
      <p class="mt-0.5 mb-3 text-[1.15rem] font-bold">$148</p>
      <button type="button" class="mt-auto flex w-full cursor-pointer items-center justify-center gap-2 rounded-[10px] bg-[#1c1917] px-3.5 py-[11px] text-[.9rem] font-semibold text-white transition-colors duration-200 hover:bg-[#44403c] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none"><svg class="size-[18px] fill-none stroke-current stroke-2 [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><span>Add<span class="sr-only"> Field Bomber Jacket</span> to cart</span></button>
    </div>
  </article></li>
</ul></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#57534e] uppercase">Centered minimal cards</span>
<ul class="grid grid-cols-[repeat(auto-fill,minmax(230px,1fr))] gap-[22px]">
  <li><article class="flex h-full flex-col text-center">
    <div class="relative aspect-square overflow-hidden rounded-[14px] bg-[#efebe6]">
      <img class="size-full object-cover" src="https://images.unsplash.com/photo-1514228742587-6b1558fcca3d?auto=format&fit=crop&w=600&q=70" alt="Plain white stoneware mug on a white table" width="600" height="600" loading="lazy">
      <span class="absolute top-3 left-3 rounded-full bg-[#1c1917] px-2.5 py-1 text-[.72rem] leading-[1.4] font-bold tracking-[.04em] text-white">Bestseller</span>
    </div>
    <div class="flex flex-1 flex-col items-center gap-1.5 px-1 pt-3.5">
      <h3 class="text-base leading-[1.35] font-semibold"><a href="#" class="text-[#1c1917] underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Stoneware Mug</a></h3>
      <p class="flex items-center gap-1.5 text-[.8rem] text-[#57534e]"><span class="bg-[linear-gradient(90deg,#d97706_98%,#d6d3d1_0)] bg-clip-text text-[.95rem] leading-none tracking-[1px] text-transparent" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.9 out of 5, 236 reviews</span><span aria-hidden="true">4.9 (236)</span></p>
      <p class="mt-0.5 mb-3 text-[1.15rem] font-bold">$18</p>
      <button type="button" class="mt-auto cursor-pointer rounded-full border-[1.5px] border-[#1c1917] px-[22px] py-[9px] text-[.9rem] font-semibold text-[#1c1917] transition-colors duration-200 hover:bg-[#1c1917] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none"><span>Add<span class="sr-only"> Stoneware Mug</span> to cart</span></button>
    </div>
  </article></li>
  <li><article class="flex h-full flex-col text-center">
    <div class="relative aspect-square overflow-hidden rounded-[14px] bg-[#efebe6]">
      <img class="size-full object-cover" src="https://images.unsplash.com/photo-1503602642458-232111445657?auto=format&fit=crop&w=600&q=70" alt="Pale wooden bar stool against a light blue wall" width="600" height="600" loading="lazy">
      <span class="absolute top-3 left-3 rounded-full bg-[#fef3c7] px-2.5 py-1 text-[.72rem] leading-[1.4] font-bold tracking-[.04em] text-[#78350f]">Only 3 left</span>
    </div>
    <div class="flex flex-1 flex-col items-center gap-1.5 px-1 pt-3.5">
      <h3 class="text-base leading-[1.35] font-semibold"><a href="#" class="text-[#1c1917] underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Oak Bar Stool</a></h3>
      <p class="flex items-center gap-1.5 text-[.8rem] text-[#57534e]"><span class="bg-[linear-gradient(90deg,#d97706_88%,#d6d3d1_0)] bg-clip-text text-[.95rem] leading-none tracking-[1px] text-transparent" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.4 out of 5, 39 reviews</span><span aria-hidden="true">4.4 (39)</span></p>
      <p class="mt-0.5 mb-3 text-[1.15rem] font-bold">$129</p>
      <button type="button" class="mt-auto cursor-pointer rounded-full border-[1.5px] border-[#1c1917] px-[22px] py-[9px] text-[.9rem] font-semibold text-[#1c1917] transition-colors duration-200 hover:bg-[#1c1917] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none"><span>Add<span class="sr-only"> Oak Bar Stool</span> to cart</span></button>
    </div>
  </article></li>
  <li><article class="flex h-full flex-col text-center">
    <div class="relative aspect-square overflow-hidden rounded-[14px] bg-[#efebe6]">
      <img class="size-full object-cover" src="https://images.unsplash.com/photo-1485955900006-10f4d324d411?auto=format&fit=crop&w=600&q=70" alt="Small striped succulent in a mint green ceramic pot" width="600" height="600" loading="lazy">
    </div>
    <div class="flex flex-1 flex-col items-center gap-1.5 px-1 pt-3.5">
      <h3 class="text-base leading-[1.35] font-semibold"><a href="#" class="text-[#1c1917] underline-offset-3 hover:underline focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb]">Potted Zebra Haworthia</a></h3>
      <p class="flex items-center gap-1.5 text-[.8rem] text-[#57534e]"><span class="bg-[linear-gradient(90deg,#d97706_94%,#d6d3d1_0)] bg-clip-text text-[.95rem] leading-none tracking-[1px] text-transparent" aria-hidden="true">★★★★★</span><span class="sr-only">Rated 4.7 out of 5, 88 reviews</span><span aria-hidden="true">4.7 (88)</span></p>
      <p class="mt-0.5 mb-3 text-[1.15rem] font-bold">$22</p>
      <button type="button" class="mt-auto cursor-pointer rounded-full border-[1.5px] border-[#1c1917] px-[22px] py-[9px] text-[.9rem] font-semibold text-[#1c1917] transition-colors duration-200 hover:bg-[#1c1917] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none"><span>Add<span class="sr-only"> Potted Zebra Haworthia</span> to cart</span></button>
    </div>
  </article></li>
</ul></div>
JavaScript
// Wishlist hearts: flip aria-pressed on click (aria-pressed: and group-aria-pressed: utilities restyle the heart).
document.querySelectorAll('.pc-wish').forEach(btn => btn.addEventListener('click', () => {
  btn.setAttribute('aria-pressed', String(btn.getAttribute('aria-pressed') !== 'true'));
}));

About this set

The two product card layouts most online shops start with. Shop grid with wishlist is a bordered card with a 4:5 photo, a New or sale badge, a round wishlist heart in the corner, the category, the product name as a link, a star rating with its review count, the price and a full-width Add to cart button with a bag icon; the sale card shows the reduced price in red beside the struck-through original. Centered minimal cards drop the border for a square photo on a warm ground, centred text and an outline pill button, with Bestseller and Only 3 left badges, which suits homeware and gift shops where the photo carries the card. Both grids are CSS Grid with auto-fill columns, so they go from one card on a phone to three or four on a desktop without media queries. The stars are a row of characters filled to the exact rating with a gradient clipped to the text, so 4.5 shows four and a half stars from one custom property. A few lines of JavaScript flip the wishlist buttons between pressed and not pressed. The Bootstrap version uses .card, .ratio, .badge and Bootstrap’s own data-bs-toggle=”button” for the wishlist.

What’s included

  • Two layouts: bordered shop card and centered minimal card
  • Badges for new, sale, bestseller and low stock
  • Partial star ratings from one --r custom property
  • Wishlist hearts that are real toggle buttons with aria-pressed
  • Auto-fill grid from one to four columns, no media queries

Accessibility

Each Add to cart button is named for its product, for example Add Field Bomber Jacket to cart, through visually hidden text, and the wishlist heart is a button named Save Field Bomber Jacket to wishlist whose aria-pressed state screen readers announce. Star graphics are hidden from assistive technology and replaced with text such as Rated 4.2 out of 5, 57 reviews, and sale prices are read as Sale price $24, was $30. Links and buttons show a 3px blue focus outline, and the hover lift is switched off under prefers-reduced-motion.

Customise it

Change --pc-ink, --pc-sale, --pc-heart, --pc-star and --pc-focus on .pc for colour, and the minmax(230px, 1fr) value for card width. In Bootstrap the button colours are --bs-btn-* variables on .pc-add; in Tailwind edit the bg-[#1c1917] and aspect-[4/5] utilities.