Skip to content

Product Card with Image Zoom

Pure CSS product card whose photo zooms smoothly on hover or focus, following the pointer, with rating, color swatches and price.

Live preview

Open in new tab

Code

A pure CSS product card with a large photo that zooms in smoothly when the shopper hovers over the card or tabs into it. It pairs the image with a category line, star rating, short copy, color swatches, a sale price and an add-to-bag link, and suits shop grids, featured product blocks and landing pages.

How it works

The wrapper .pz-stage sets all colors and sizes as custom properties and centers one article.pz-card. The card is a CSS grid: a single column on phones and two equal columns from 640px, with .pz-media on the left and .pz-body on the right. .pz-media has overflow: hidden and holds the .pz-img photo, the .pz-tag label and the .pz-hint pill.

The zoom is a transform: scale() on .pz-img with an eased transition. It is triggered by .pz-card:hover and .pz-card:focus-within, so keyboard users see the same effect when the swatches or the button get focus. At the same moment the hint pill fades and slides away. The image uses transform-origin: var(--pz-x) var(--pz-y).

The small script.js is optional. On devices with a fine pointer it listens for pointermove on .pz-media and writes the cursor position into --pz-x and --pz-y, so the area under the cursor is magnified. On pointerleave the values are removed and the origin returns to the default. Without JavaScript the card still zooms toward the center.

The swatches are real radio buttons inside a fieldset.pz-swatches; :has(input:checked) draws the ring around the selected .pz-swatch.

Customization

  • --pz-zoom on .pz-stage: the scale factor on hover, e.g. 1.15 for a subtle zoom or 1.6 for a close-up.
  • --pz-speed: duration of the zoom transition; raise it for a slower, more cinematic move.
  • --pz-x and --pz-y on .pz-media: the default zoom origin (currently 50% and 55%) used before the pointer moves and without JavaScript.
  • --pz-accent and --pz-accent-dark: the button color, its hover color and the category text.
  • --pz-sale: the color of .pz-price-now; set it to --pz-text for items that are not on sale and remove .pz-price-old.
  • .pz-swatch--teal, .pz-swatch--sand and .pz-swatch--cocoa: each sets --pz-dot; add a modifier for more colors.
  • grid-template-columns in the 640px media query: use 1.2fr 1fr to give the photo more room.

Accessibility

The card is an article labelled by its heading through aria-labelledby. The photo has a descriptive alt, and the decorative hint is aria-hidden. The swatches are a native radio group with a legend, so arrow keys work and screen readers announce each color name from the visually hidden .pz-sr text. The old price is announced as "Was". Swatches and the button show a clear focus outline. With prefers-reduced-motion: reduce the scaling is replaced by a slight brightness change, and on touch screens the hint is hidden.

Usage notes

The photo comes from Unsplash; replace it with your own product image. Use a different name on the radio inputs for every card on the page.

Requirements

No dependencies. Pure HTML, CSS and JavaScript.

Product Card with Image Zoom – Pure CSS product cards component screenshot
Product Card with Image Zoom · Pure CSS Product Cards

More Product Cards

Product Card with Quick Actions – Pure CSS product cards preview

Product Card with Quick Actions

Responsive product card grid where hover or focus reveals wishlist, quick view and add-to-cart actions, with star ratings and a native quick view dialog.

Product Card with Color Options – Tailwind CSS product cards preview

Product Card with Color Options

Tailwind CSS product card with live-recoloring color swatches, a size selector with sold-out state, wishlist toggle and add to bag button.