# Product Card

> An animated product card for React and shadcn, with Quick add so shoppers pick a size without leaving the grid.

A product tile with Quick add: swatches cross-fade the image, Add opens the sizes in place, and a size morphs the bar into a quantity stepper, so shoppers never leave the grid.

- Tier: Eased Pro
- Interaction: Press
- Page: https://eased.dev/components/product-card
- Installs: motion, @base-ui/react

## Install

Product Card ships with Eased Pro (https://eased.dev/#pricing). Members add the `@eased-pro` registry with their token (https://eased.dev/account), then run:

```bash
npx shadcn add @eased-pro/product-card
```

A product tile with swatches and Quick add: Add opens the sizes in place, and a size morphs into a quantity stepper.

## When to use

- Collection grids, search results and "you may also like" rows where shoppers add without opening the product.
- Apparel and other sized goods: the size row appears inside the card, not in a modal.

## When not to use

- The product page itself: use Variant Picker with a full Add to bag button.
- Products that need configuration (engraving, bundles): link to the page instead of quick adding.

## Usage

```tsx example.tsx
import { ProductCard } from "@/components/eased/product-card/product-card";

<ProductCard
  name="Ridgeline Down Jacket"
  href="/products/ridgeline"
  tag="Bestseller"
  rating={4.7}
  reviewCount={1284}
  variants={variants}
  image={(color) => <img src={`/img/ridgeline-${color}.jpg`} alt="" className="size-full object-cover" />}
  onAdd={(v) => addToBag(v.id)}
  onQuantityChange={(v, qty) => setQuantity(v.id, qty)}
/>
```

## API reference

### ProductCard

| Prop | Type | Default | Description |
|---|---|---|---|
| `name` / `href` | `string` | | The name links to `href` and its link covers the card. |
| `variants` | `ProductVariant[]` | | From commerce-core. One size means Add goes straight to the stepper. |
| `image` | `(color) => ReactNode` | | One image per colourway; they cross-fade. |
| `tag` | `string` | | A flag on the image. A markdown badge is added from `compareAt`. |
| `rating` / `reviewCount` | `number` | | |
| `defaultColor` | `string` | first | |
| `onAdd` | `(variant) => void` | | Quantity 1. |
| `onQuantityChange` | `(variant, qty) => void` | | 0 when the bin removes it. |
| `imageClassName` | `string` | `"aspect-[4/5]"` | The image frame's aspect ratio. |

## Keyboard

| Key | Action |
|---|---|
| `Tab` | Name link, swatches (one stop, arrows move), Quick add. |
| `Enter` / `Space` | Opens sizes; focus moves to the first size in stock. |
| `Escape` | Closes the size row. |

## Accessibility

- The card is an `article` named by the product; swatches are a radio group named "… colour".
- Sold-out sizes are disabled and named "M, sold out"; the stepper's buttons name the product and variant.

## Motion

Swatches swap the image with a 320ms cross-fade from a 6px blur, and a mouse hover previews a colourway. Quick add is one 32px slot whose contents swap with blur and a 2% scale (Add, then sizes rising in with a 35ms stagger, then the stepper), so the grid never reflows. At quantity 1 the minus morphs into a bin. Reduced motion: opacity only.

## Notes for AI

Pick it for grids where adding to bag should not leave the page. Keep your bag in `onAdd`/`onQuantityChange`, and show it with commerce-core's `BagButton`, whose count rolls.

Don't wrap the card in a link: the name's link already covers it, and swatches and Quick add sit above that layer.

## Related

- [Variant Picker](/components/variant-picker): the full buy box.
- [Cart Drawer](/components/cart-drawer): the bag the card fills.
- [Wishlist](/components/wishlist): saved for later.
