5 Pure CSS2 with JSPublished 2026-10-05Free · no sign-up
Seven product cards for online shops: a card with a quick-add button, a sale card that reads the old and new price correctly, colour swatches made from radio buttons, a cart line with a quantity stepper, hover actions, a rating with a low-stock note and a card for a digital download.
A product card with an image area, name, price and an Add to bag button. The product name is the link, stretched over the card with a pseudo-element, and the button sits above it with z-index, so the card is clickable without nesting interactive elements.
A sale badge and a struck-through original price. The old price is in a del element and the new one in ins, plus visually hidden words Was and Now, because a line-through alone is not announced by most screen readers.
Colour swatches under the product are radio buttons, so picking one is keyboard accessible and only one can be chosen. The chosen colour's name is shown in text, and the image area changes colour with :has(:checked).
var c = document.querySelector('.pc3');
c.addEventListener('change', function (e) { c.querySelector('legend span').textContent = e.target.value; });
04. Cart Line Item with Quantity
EcommerceCart
A product row as it appears in a cart: thumbnail, name, variant, a quantity stepper and the line price. The stepper buttons are labelled Decrease and Increase quantity, and the number is an output element whose change updates the price.
var o = document.querySelector('.pc4 output'), pr = document.querySelector('.pc4 .pr'), b = document.querySelectorAll('.pc4 .q button');
function set(n) { n = Math.max(1, Math.min(9, n)); o.value = n; pr.textContent = '$' + n * 36; }
b[0].onclick = function () { set(+o.value - 1); }; b[1].onclick = function () { set(+o.value + 1); };
05. Card with Hover Actions
EcommerceHover
Quick view and wishlist buttons slide up over the image on hover. They also appear when anything inside the card has focus, through :focus-within, and they are always visible on touch screens where hover does not exist.
View code
<article class="pc5"><div class="img"><div class="acts"><button type="button">Quick view</button><button type="button" aria-label="Save to wishlist">♡</button></div></div><h3><a href="#jacket">Waxed field jacket</a></h3><p>$145</p></article>
Adds the details shoppers scan for: a star rating with its count and a low-stock note. Partial stars are drawn by clipping a row of gold stars to the rating's width, and the rating is also given as text for screen readers.
View code
<article class="pc6"><div class="img" aria-hidden="true"></div><h3>Trail running sock</h3><p class="rt"><span class="stars" style="--v:4.3" aria-hidden="true"></span><span class="sr">Rated 4.3 out of 5</span> <span>(128)</span></p><p class="pr">$14</p><p class="st">Only 4 left in your size</p></article>
A card for a template or e-book with a file type tag, what is included as a checklist and a single price button. The checklist uses real list markup with tick marks drawn in CSS, so the items are still a list for screen readers.
View code
<article class="pc7"><p class="tag">Figma + HTML</p><h3>Storefront UI kit</h3><ul><li>48 page templates</li><li>Light and dark themes</li><li>Free updates for a year</li></ul><button type="button">Buy for $59</button></article>
Make the product name the link and stretch it over the card with a pseudo-element. Buttons such as Add to bag sit above it with position: relative and a z-index, so you never nest a button inside a link.
Prices that read correctly
A struck-through price is usually not announced. Put the old price in <del> and the new one in <ins>, and add hidden words such as Was and Now so the meaning survives without the styling.
Variants as radio buttons
Colour and size choices are a single choice from a set, which is exactly a radio group. Hide the circle, style the label as a swatch, and use :has(:checked) to show the selection.
Images and layout shift
Give the image area a fixed aspect-ratio so the card does not jump when the photo loads. In real cards use width and height attributes and loading="lazy" on product images below the fold.
Frequently asked questions
Where are the product photos?
The demos use CSS gradients as stand-ins so they load instantly. Swap in your own img elements with alt text.
Do these work with Shopify?
Yes. The HTML maps onto a Liquid product card snippet; keep the class names and loop over your products.
How many cards per row?
Use a grid with repeat(auto-fill, minmax(200px, 1fr)) and the row count adapts to the screen.