Product card with quick add
A product card with an image, price, rating and an add-to-cart button that reveals on hover and always shows on touch.
Tailwind CSSCardsproductcardecommercehover
Live preview
Code
<article class="group relative w-64 overflow-hidden rounded-xl border border-slate-200 bg-white">
<a href="/products/linen-shirt" class="block">
<div class="relative aspect-[4/5] overflow-hidden bg-slate-100">
<img src="/img/linen-shirt.jpg" alt="Linen shirt in sand colour" loading="lazy"
class="size-full object-cover transition-transform duration-300 group-hover:scale-105">
</div>
<div class="p-4">
<h3 class="truncate text-sm font-medium text-slate-900">Linen shirt</h3>
<p class="mt-1 flex items-center justify-between text-sm">
<span class="font-semibold text-slate-900">$54.00</span>
<span class="text-amber-500" aria-label="Rated 4.5 out of 5">★★★★☆</span>
</p>
</div>
</a>
<!-- hidden until hover or focus; always visible on touch -->
<button type="button" data-sku="SHIRT-001"
class="absolute inset-x-3 top-[calc(100%-5rem)] translate-y-24 rounded-lg bg-slate-900 py-2 text-sm font-semibold text-white opacity-0 transition
group-hover:translate-y-0 group-hover:opacity-100 group-focus-within:translate-y-0 group-focus-within:opacity-100
pointer-coarse:static pointer-coarse:m-3 pointer-coarse:w-[calc(100%-1.5rem)] pointer-coarse:translate-y-0 pointer-coarse:opacity-100">
Add to cart
</button>
<span class="sr-only" role="status" data-added-note></span>
</article>
// Fires a cart:add event; wire your own cart code to it.
document.querySelectorAll('button[data-sku]').forEach((btn) => {
const note = btn.parentElement.querySelector('[data-added-note]');
const label = btn.textContent;
btn.addEventListener('click', () => {
document.dispatchEvent(new CustomEvent('cart:add', { detail: { sku: btn.dataset.sku, qty: 1 } }));
btn.textContent = 'Added';
note.textContent = 'Item added to cart';
setTimeout(() => {
btn.textContent = label;
note.textContent = '';
}, 1500);
});
});
How it works
What it solves
Shop listing grids need a fast way to add an item without opening the product page, but hover-only buttons are unreachable on phones and for keyboard users. This card reveals the action on hover and focus and keeps it visible on touch screens.
How it works
The card is an article with a group class. The image wrapper has a fixed aspect-[4/5] ratio so layout does not shift while pictures load. The quick-add button sits at the bottom of the image, translated out of view with translate-y-full and moved in by group-hover:translate-y-0 and group-focus-within:translate-y-0; the focus-within variant is what makes keyboard use work. A pointer-coarse:translate-y-0 variant keeps the button visible on touch devices. The script finds buttons by a data-sku attribute, posts nothing itself, and dispatches a custom cart:add event so your cart code can listen. It also updates the label to "Added" briefly and announces it through a status span.
Adapting it
Listen for cart:add on document and call your API. For variants, replace the button with a link to the product page when options are required. Show a sale badge with an absolutely positioned span.
Pitfalls
- Never disable the product link inside the card; the image stays clickable.
- Hover-only controls fail on touch; always provide the coarse pointer rule.
- Debounce repeated clicks if your cart call is slow.
How to use it
- Paste
product-card.htmlinside a grid, repeating it per product with a uniquedata-sku. - Load
quick-add.jswithdefer. - Listen for the
cart:addevent ondocumentand call your own cart endpoint. - Swap the inline image for your own source and keep the aspect ratio wrapper.
Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.