Product Card is an e-commerce widget that displays a product with image, name, price, optional badge (e.g., “Sale”, “New”), and an add-to-cart CTA. It is designed for SaaS applications with marketplace, catalog, or storefront features. The existing PricingCard handles subscription plan presentation; Product Card focuses on physical or digital product display with purchase intent. The widget is intentionally compact (size S) and composable.
Goals
Deliver a <ProductCard> component that displays a product image, name, price, optional badge, and add-to-cart button.
Use design-system tokens for all visual properties.
Support variant states: default, on-sale, out-of-stock, featured.
Ship Storybook stories with realistic e-commerce data.
Non-Goals
Full product detail page layout.
Cart management or checkout flow.
Product search or filtering.
Image gallery or zoom.
Reviews or ratings display (separate component).
Scope
In Scope
Area
Description
Component
<ProductCard> displaying product image, name, price, badge, and CTA
Image
Product image with aspect-ratio container and fallback placeholder
Name
Product name with optional truncation
Price
Current price with optional original price (strikethrough for sales)
Badge
Optional status badge (“Sale”, “New”, “Sold Out”, custom)
CTA
Add-to-cart button with loading and disabled states
Variants
Default, on-sale (with original price), out-of-stock (disabled CTA), featured (highlighted border)
Stories
Storybook stories for all variants and states
Out of Scope
Area
Reason
Product detail page
Separate layout pattern
Cart management
Application-layer concern
Image gallery/zoom
Separate component
Reviews/ratings
Separate component
Product comparison
Separate widget
Users and Pain Points
User Groups
User
Description
Needs
Developers
Engineers building e-commerce or marketplace features
A composable product card using DS primitives
Designers
Design-system consumers
Consistent product display aligned with DS tokens
End Users
Shoppers browsing products
Clear product info with easy add-to-cart action
Pain Points
User
Pain Point
Impact
Developers
Building product cards from scratch with image containers, price formatting, badge placement, and cart button state
Repeated boilerplate, inconsistent cards across products
End Users
Inconsistent product cards make it hard to scan and compare products
Slower browsing, reduced purchase intent
Definitions
Term
Definition
Product Card
A compact display widget showing a product’s key information and purchase action
Original Price
The pre-discount price shown with strikethrough styling during sales
CTA
Call to action; in this context, the “Add to Cart” button
Featured
A visual variant with a highlighted border or background to draw attention
Current State
Existing Behavior
PricingCard in src/components/ui/cards/pricing-card.tsx displays a subscription pricing tier with plan name, price, billing period, feature list, and CTA. It is designed for pricing pages, not product catalogs. There is no product-focused card for e-commerce use cases.
Current Limitations
No e-commerce product card component.
PricingCard is subscription-focused and does not support product images, sale prices, or stock status.
No pattern for original/sale price display with strikethrough.
Existing Workarounds
Developers build custom product cards with ad hoc image containers and inline styles.
Introduce <ProductCard> that accepts product data (image, name, price, originalPrice, badge, onAddToCart) and renders a compact card. The image fills an aspect-ratio container at the top. Below it, the product name, price (with optional strikethrough original price), and an optional badge display. At the bottom, an “Add to Cart” button fires the onAddToCart callback. Out-of-stock products show a disabled button with “Sold Out” text.
Key Capabilities
Product image with configurable aspect ratio and placeholder fallback.
Product name with two-line truncation.
Current price display with optional original price (strikethrough) for sales.
Configurable badge (text and tone).
Add-to-cart button with loading state and disabled state for out-of-stock.
Featured variant with highlighted border.
Hover elevation animation.
User Experience
Users see a grid of product cards. Each card shows a product image, name, price, and an “Add to Cart” button. Sale items show the original price crossed out next to the sale price, with a “Sale” badge. Out-of-stock items show a “Sold Out” badge and a disabled button. Hovering a card slightly elevates it.
Developer Experience
<ProductCard
image="/products/headphones.jpg"
name="Wireless Noise-Cancelling Headphones"
price="$249.99"
originalPrice="$329.99"
badge={{ text: "Sale", tone: "destructive" }}
onAddToCart={()=>addToCart("headphones-1")}
/>
Requirements
ID
Requirement
Priority
Notes
FR-001
ProductCard displays product image, name, and price
Must
-
FR-002
Optional original price renders with strikethrough
Must
-
FR-003
Optional badge renders with configurable text and tone
Must
-
FR-004
Add-to-cart button fires onAddToCart callback
Must
-
FR-005
Out-of-stock variant disables the CTA
Must
-
FR-006
Featured variant applies highlighted border
Should
-
FR-007
Image placeholder renders when image is not provided
Should
-
Priority Definitions
Priority
Meaning
Must
Required for this feature to ship.
Should
Important, but can be deferred if needed.
Could
Nice to have. Not required for initial release.
Functional Requirements
ID
Requirement
User Benefit
Priority
FUNC-001
image prop renders in an aspect-ratio container (default 4:3)
Consistent image display
Must
FUNC-002
When image is not provided, a placeholder with a product icon renders
Graceful fallback
Should
FUNC-003
name renders with two-line truncation (line-clamp-2)
Consistent card heights
Must
FUNC-004
price renders prominently; originalPrice renders with line-through styling
Clear pricing
Must
FUNC-005
badge prop accepts { text: string; tone?: string } and renders a Badge
Flexible status indication
Must
FUNC-006
onAddToCart fires when the CTA button is clicked
Cart integration
Must
FUNC-007
isAddingToCart prop shows loading spinner on the CTA button
Feedback during async add
Should
FUNC-008
outOfStock prop disables CTA and changes button text to “Sold Out”
Clear unavailability
Must
FUNC-009
featured prop adds a highlighted border or ring
Visual emphasis
Should
FUNC-010
Card has hover elevation animation
Interactive feel
Should
Non-Functional Requirements
ID
Requirement
Category
Priority
NFR-001
Component renders in under 10ms
Performance
Must
NFR-002
Image lazy-loads with loading="lazy"
Performance
Should
NFR-003
All interactive elements are keyboard-navigable
Accessibility
Must
NFR-004
Works in light and dark themes
Theming
Must
NFR-005
No new runtime dependencies
Maintainability
Must
NFR-006
Image container does not cause layout shift on load