skills/storefront-ui/quick-view-modal/SKILL.md
Let shoppers preview product details and add items to cart from the listing page without navigating away, reducing friction in the shopping flow
npx skillsauth add finsilabs/awesome-ecommerce-skills quick-view-modalInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Implement a product quick-view overlay that lets shoppers preview key product details — images, variants, description, price — and add items to cart without navigating away from the product listing page. Quick view reduces friction for shoppers browsing multiple products and works best for products with simple variant structures (1–2 variant axes).
| Platform | Recommended Approach | Why |
|----------|---------------------|-----|
| Shopify | Enable Quick View in your theme (Dawn, Sense, Craft all include it) or install Quick View – Instant Preview app | Dawn's built-in Quick Add button adds items directly to cart from collection pages; the Quick View app adds a full product preview with variant selection |
| WooCommerce | Install YITH WooCommerce Quick View (free) or WooCommerce Quick View Pro | YITH Quick View is the most widely used option — adds a "Quick View" button on hover, opens a modal with gallery, variants, and Add to Cart, and requires no custom code |
| BigCommerce | Enable Quick View in Storefront → My Themes → Customize → Product Cards (Cornerstone theme) | Cornerstone includes a built-in quick view popup — toggle it in the Theme Editor with no coding required |
| Custom / Headless | Build a modal using the native <dialog> element with lazy product fetch, variant selection, and focus management | <dialog> provides built-in focus trapping and Escape-to-close; lazy fetching keeps initial page weight low |
Built-in Quick Add (Dawn, Sense, Craft):
Dawn's collection pages include a "Quick Add" button by default that adds a product directly to cart without opening a modal. To enable/configure it:
Note: Dawn's Quick Add only works for products with no variants or a single variant axis. For products with multiple option types (Color + Size), it opens the product page instead.
Full Quick View modal (Quick View – Instant Preview app):
YITH WooCommerce Quick View (free):
The plugin adds a "Quick View" button to .product elements across shop, archive, and search results pages automatically.
Built-in Quick View (Cornerstone theme):
For non-Cornerstone themes, check your theme documentation — Quick View support varies. If not included, install the Quick View app from the BigCommerce App Marketplace.
Quick View button on product cards:
// ProductCard.jsx
export function ProductCard({ product, onQuickView }) {
return (
<article className="product-card">
<div className="product-card__image-wrapper">
<a href={product.url}>
<img src={product.image} alt={product.name} loading="lazy" />
</a>
<button className="quick-view-btn"
onClick={() => onQuickView(product.id)}
aria-label={`Quick view ${product.name}`}>
Quick View
</button>
</div>
<a href={product.url} className="product-card__name">{product.name}</a>
<p className="product-card__price">${product.price}</p>
</article>
);
}
/* Always visible on touch; appears on hover for mouse users */
.quick-view-btn { position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%);
opacity: 0; transition: opacity 0.15s; }
.product-card:hover .quick-view-btn, .product-card:focus-within .quick-view-btn { opacity: 1; }
@media (hover: none) { .quick-view-btn { opacity: 1; } }
Modal using native <dialog> (built-in focus trapping + Escape-to-close):
// QuickViewModal.jsx
import { useEffect, useRef } from 'react';
export function QuickViewModal({ isOpen, product, loading, onClose, onAddToCart }) {
const dialogRef = useRef(null);
useEffect(() => {
const dialog = dialogRef.current;
if (!dialog) return;
if (isOpen) dialog.showModal();
else dialog.close();
}, [isOpen]);
return (
<dialog ref={dialogRef} className="quick-view-dialog"
onClose={onClose}
onClick={(e) => { if (e.target === dialogRef.current) onClose(); }}
aria-label={product ? `Quick view: ${product.name}` : 'Quick view'}>
<button className="close-btn" onClick={onClose} aria-label="Close quick view">×</button>
{loading && (
<div aria-live="polite" aria-label="Loading product details">
{/* Skeleton loaders */}
<div className="skeleton skeleton--image" />
<div className="skeleton skeleton--title" />
</div>
)}
{!loading && product && (
<QuickViewBody product={product} onAddToCart={onAddToCart} onClose={onClose} />
)}
</dialog>
);
}
Quick view body with variant selection:
function QuickViewBody({ product, onAddToCart, onClose }) {
const [selectedVariant, setSelectedVariant] = useState(product.variants[0] ?? null);
return (
<div className="quick-view-body">
<img src={selectedVariant?.image ?? product.images[0]} alt={product.name} />
<div className="quick-view-details">
<h2>{product.name}</h2>
<p>${selectedVariant?.price ?? product.price}</p>
{product.options.map(option => (
<fieldset key={option.name}>
<legend>{option.name}</legend>
{option.values.map(value => {
const variant = product.variants.find(v => v.options[option.name] === value);
return (
<label key={value}>
<input type="radio" name={option.name} value={value}
checked={selectedVariant?.options[option.name] === value}
disabled={!variant || variant.inventory === 0}
onChange={() => setSelectedVariant(variant)} />
{value}{variant?.inventory === 0 ? ' (sold out)' : ''}
</label>
);
})}
</fieldset>
))}
<button className="btn-primary"
disabled={!selectedVariant || selectedVariant.inventory === 0}
onClick={() => onAddToCart({ variantId: selectedVariant.id, quantity: 1 }).then(onClose)}>
Add to Cart
</button>
<a href={product.url}>View Full Details</a>
</div>
</div>
);
}
Return focus to the trigger button when modal closes:
const triggerRef = useRef(null);
function handleOpenQuickView(productId, triggerElement) {
triggerRef.current = triggerElement; // store the button that was clicked
openQuickView(productId);
}
function handleCloseQuickView() {
closeQuickView();
requestAnimationFrame(() => triggerRef.current?.focus());
}
overscroll-behavior: contain on the dialog prevents the underlying page from scrolling| Problem | Solution |
|---------|----------|
| Focus lost after modal closes | Store the trigger element reference before opening; call .focus() inside requestAnimationFrame after close |
| Quick view button not visible on touch devices | Use @media (hover: none) to always show the button on touch screens; do not rely on hover alone |
| Body scrolls behind open modal | Set overflow: hidden on body when modal is open; restore on close |
| Variant selection resets when images change | Keep selectedVariant in state indexed by variant ID, not position in the array |
| Quick view opens for products that need full PDP | Detect products with 3+ options or requiring size guide and navigate to PDP directly instead |
tools
Let shoppers save products to a wishlist, share it with friends, and get notified when saved items come back in stock or drop in price
development
Build a themeable storefront with design tokens and CSS custom properties that supports white-labeling, multi-brand variants, and dark mode
development
Speed up product discovery with instant search suggestions, fuzzy typo matching, and category-aware results powered by Algolia or Elasticsearch
development
Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart buttons, and touch-optimized components for high mobile conversion