
Quick View Modal
- 57 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Let shoppers preview product details and add to cart from the listing page in a modal without navigating away.
About
Adds a quick-view modal that surfaces product details and add-to-cart from listing pages to reduce navigation friction. A developer uses it to speed up the shopping flow on collection and search pages.
- In-place product preview from listing pages
- Add-to-cart without leaving the collection view
Quick View Modal by the numbers
- 57 all-time installs (skills.sh)
- Ranked #1,238 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill quick-view-modalAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 57 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Let shoppers preview product details and add to cart from the listing page in a modal without navigating away.
Files
Quick View Modal
Overview
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).
When to Use This Skill
- When conversion data shows shoppers leave the PLP frequently but bounce from the PDP
- When products have simple variant structures (1-2 variant axes) suitable for quick selection
- When implementing a "quick add" button on product cards in collections or search results
- When the site's PDP is heavy (many images, reviews section) and a lighter preview would reduce friction
Core Instructions
Step 1: Determine the merchant's platform and choose the right approach
| 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 |
Step 2: Enable and configure Quick View
---
Shopify
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: 1. Go to Online Store → Themes → Customize 2. Navigate to a collection page template 3. In the Product card section, enable Quick add button 4. Set button style: Standard (shows "Quick Add") or Icon (+ icon)
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): 1. Install from the Shopify App Store (free tier available) 2. The app adds a "Quick View" button on hover over product cards across all collection pages 3. In app settings, configure which product sections appear in the modal: images, description, reviews, size guides 4. Shoppers can select all variant options and add to cart without leaving the collection page 5. No theme code editing required — the app injects via App Block
---
WooCommerce
YITH WooCommerce Quick View (free): 1. Install and activate from WordPress.org 2. Go to YITH → Quick View → General Settings 3. Set Trigger: "Quick View button on hover" (recommended) or click on product image 4. Configure Modal content: choose which sections to show — Gallery, Price, Short Description, Variants (Add to Cart), Reviews summary 5. Set Modal width and whether to show a "View full product" link inside the modal (recommended) 6. Under Style, adjust button text, colors, and positioning to match your theme
The plugin adds a "Quick View" button to .product elements across shop, archive, and search results pages automatically.
---
BigCommerce
Built-in Quick View (Cornerstone theme): 1. Go to Storefront → My Themes → Customize 2. Find Product Cards section (in Global or Category Page settings depending on your theme version) 3. Toggle Enable Quick View to On 4. Configure which elements appear: gallery, options/variants, Add to Cart button, price 5. BigCommerce's Quick View pulls the product's full variant options and images
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.
---
Custom / Headless
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());
}Best Practices
- Always provide a "View full details" link — quick view is a shortcut, not a replacement; complex products (many images, reviews) need the full PDP
- Lazy-fetch product data on open — do not embed full product data in the card HTML; fetch it on demand to keep page weight low
- Show a loading skeleton — the fetch takes 100–300 ms; a skeleton prevents perceived layout shift
- Close on backdrop click — clicking outside the modal content area should close it
- Prevent body scroll when open — on mobile,
overscroll-behavior: containon the dialog prevents the underlying page from scrolling - Use Quick View only for simple products — products with 3+ variant axes, size guides, or detailed spec tables should go to the full PDP
Common Pitfalls
| 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 |
Related Skills
- @product-page-design
- @accessibility-commerce
- @responsive-storefront
- @faceted-navigation
{
"context": "Tests whether the agent implements the quick view modal using a native <dialog> element with showModal()/close(), an accessible loading skeleton, backdrop-click-to-close, and WCAG-compliant focus return on close.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Uses native <dialog> element",
"max_score": 12,
"description": "QuickViewModal.jsx renders a <dialog> HTML element as the modal container (not a <div> with role=\"dialog\" or similar)"
},
{
"name": "Uses showModal() to open",
"max_score": 10,
"description": "QuickViewModal.jsx calls dialogRef.current.showModal() (or equivalent ref-based call) to open the dialog, not setAttribute or className toggling"
},
{
"name": "Uses dialog.close() to close",
"max_score": 10,
"description": "QuickViewModal.jsx calls dialogRef.current.close() (or equivalent) to programmatically close the dialog"
},
{
"name": "Loading skeleton shown while fetching",
"max_score": 8,
"description": "QuickViewModal.jsx (or useQuickView.js) renders a skeleton/placeholder UI element when the product data is still loading (loading state is true)"
},
{
"name": "aria-live on loading element",
"max_score": 8,
"description": "The loading skeleton element has aria-live=\"polite\" attribute"
},
{
"name": "aria-label on loading element",
"max_score": 8,
"description": "The loading skeleton element has an aria-label attribute describing the loading state (e.g. 'Loading product details')"
},
{
"name": "Backdrop click closes modal",
"max_score": 10,
"description": "QuickViewModal.jsx handles click events on the dialog element itself and calls onClose when e.target is the dialog element (not its children)"
},
{
"name": "Trigger element reference stored",
"max_score": 8,
"description": "The component or hook stores a ref to the trigger element (button that opened the modal) before or at the time of opening"
},
{
"name": "Focus returned via requestAnimationFrame",
"max_score": 12,
"description": "On close, focus is returned to the trigger element using requestAnimationFrame (e.g. requestAnimationFrame(() => triggerRef.current?.focus()) or similar)"
},
{
"name": "Fetch error closes modal",
"max_score": 8,
"description": "useQuickView.js (or equivalent) handles fetch errors by setting isOpen to false (closing the modal) rather than leaving it open in a broken state"
},
{
"name": "Hook sets loading: true on open before fetch resolves",
"max_score": 6,
"description": "useQuickView.js sets loading to true immediately when openQuickView is called, before the fetch completes"
}
]
}
Build a Quick View Modal Component
Problem/Feature Description
Vesper Clothing is adding a product preview feature to their collection pages. When a shopper clicks "Quick View" on any product card, a modal overlay should appear and load that product's details from a REST API endpoint at /api/products/{id}/quick-view. While the data is fetching, the modal must communicate loading state clearly — both visually and to screen readers.
The accessibility team has flagged that after a modal closes, keyboard users often lose their place on the page. The modal must return keyboard focus to whichever element triggered it, so users can continue browsing without losing context.
The team also wants users to be able to dismiss the overlay by clicking on the dark backdrop outside the modal content area, in addition to the standard close button.
Your job is to implement two things: 1. A QuickViewModal React component that handles all the above requirements 2. A useQuickView custom React hook that manages open/close state and the async product fetch
Output Specification
Produce the following files:
useQuickView.js— the custom hook managing open/close state and async product fetchingQuickViewModal.jsx— the modal componentQuickViewModal.css— styles for the modal including the backdrop
{
"context": "Tests whether the agent correctly implements a quick view trigger button on a product card component, including hover/touch visibility, accessibility attributes, and CSS animation conventions from the skill.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Button added to image wrapper",
"max_score": 8,
"description": "ProductCard.jsx contains a button element inside or overlaying the product image wrapper area"
},
{
"name": "onClick calls onQuickView with product ID",
"max_score": 8,
"description": "The button's onClick handler calls onQuickView (or equivalent prop) passing the product's ID"
},
{
"name": "Descriptive aria-label on button",
"max_score": 10,
"description": "The quick view button has an aria-label that includes the product name (e.g. aria-label containing both 'quick view' and the product name, case-insensitive)"
},
{
"name": "Image wrapper is position: relative",
"max_score": 8,
"description": "ProductCard.css sets position: relative on the image wrapper element so the absolutely-positioned button can overlay it"
},
{
"name": "Button positioned absolutely over image",
"max_score": 8,
"description": "ProductCard.css positions the quick view button absolutely (position: absolute) within the image wrapper"
},
{
"name": "Button hidden by default (opacity: 0)",
"max_score": 8,
"description": "ProductCard.css sets the button's default opacity to 0 so it is hidden when the card is not hovered"
},
{
"name": "Hover reveals button",
"max_score": 8,
"description": "ProductCard.css sets opacity: 1 on the quick view button when the product card (or image wrapper) is hovered or has focus-within"
},
{
"name": "Hover slide-up animation",
"max_score": 10,
"description": "ProductCard.css uses a translateY transition (e.g. from translateY(8px) to translateY(0)) as part of the hover reveal animation"
},
{
"name": "Always visible on touch devices",
"max_score": 12,
"description": "ProductCard.css includes a @media (hover: none) rule that sets the quick view button to opacity: 1 and its full visible transform, making it permanently visible on touch/no-hover devices"
},
{
"name": "Opacity + transform transition declared",
"max_score": 10,
"description": "ProductCard.css declares a CSS transition on the quick view button covering both opacity and transform properties"
},
{
"name": "Image anchor aria-hidden",
"max_score": 10,
"description": "ProductCard.jsx sets aria-hidden=\"true\" and/or tabIndex={-1} on the image anchor element to avoid redundant navigation links for screen readers"
}
]
}
Add Quick View Button to Product Card
Problem/Feature Description
The marketing team at Pebble & Grain, a home goods retailer, has noticed that customers frequently leave the product listing page to check product details and then abandon their shopping session entirely. The dev team wants to add a quick-preview trigger to each product card so shoppers can peek at product details without a full page navigation.
You've been asked to add a "Quick View" button to the existing ProductCard React component. The button should sit over the product image and be easily accessible regardless of the device type — the company has a significant mobile customer base, and any interaction that only works on desktop hover would leave those customers out.
The component must also be properly accessible for screen reader users, who should hear a meaningful label when navigating to the button.
Output Specification
Produce the following files:
ProductCard.jsx— the updated React component with the quick view buttonProductCard.css— the associated styles, including hover and touch visibility behaviour
Input Files
The following file is provided as a starting point. Extract it before beginning.
=============== FILE: ProductCard.jsx =============== import React from 'react';
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> </div> <div className="product-card__info"> <a href={product.url} className="product-card__name">{product.name}</a> <p className="product-card__price">${product.price}</p> </div> </article> ); }
{
"context": "Tests whether the agent correctly implements variant selection with per-variant image updates, disables out-of-stock variants, closes the modal after add-to-cart, includes a full details link, and prevents body scroll when the modal is open.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Variant image updates on selection",
"max_score": 10,
"description": "QuickViewBody.jsx renders the selected variant's image (e.g. selectedVariant?.image) and falls back to the first product image when no variant image is available"
},
{
"name": "Variant state not position-indexed",
"max_score": 8,
"description": "QuickViewBody.jsx stores the full selected variant object (or variant ID) in state — NOT a numeric index into the variants array"
},
{
"name": "Out-of-stock variants disabled",
"max_score": 10,
"description": "QuickViewBody.jsx disables variant option inputs (radio buttons, buttons, or similar) when the matching variant has inventory === 0"
},
{
"name": "Add to cart passes variantId and quantity",
"max_score": 8,
"description": "QuickViewBody.jsx calls onAddToCart (or addToCart) with an object containing at least variantId and quantity"
},
{
"name": "Modal closes after add to cart",
"max_score": 10,
"description": "QuickViewBody.jsx calls onClose() (or equivalent) after awaiting the add-to-cart operation, causing the modal to close automatically"
},
{
"name": "View full details link present",
"max_score": 10,
"description": "QuickViewBody.jsx renders a link (anchor element) pointing to product.url that allows the user to navigate to the full product page"
},
{
"name": "Body scroll prevented when modal open",
"max_score": 10,
"description": "QuickViewBody.css or QuickViewPage.jsx applies overflow: hidden on body or overscroll-behavior: contain on the dialog/modal when it is open, preventing background scroll"
},
{
"name": "Lazy fetch — product data not embedded in card HTML",
"max_score": 8,
"description": "QuickViewPage.jsx does NOT pre-embed full product detail objects in the product card markup; product data is fetched on demand when the modal is opened"
},
{
"name": "Variant selector uses fieldset/legend or radio group",
"max_score": 8,
"description": "QuickViewBody.jsx groups variant options using a <fieldset> with <legend> (or an equivalent accessible grouping) for each option axis"
},
{
"name": "Add-to-cart button disabled when no variant or out of stock",
"max_score": 8,
"description": "The Add to Cart button in QuickViewBody.jsx is disabled when selectedVariant is null/undefined or when selectedVariant.inventory === 0"
},
{
"name": "Add-to-cart button shows feedback state",
"max_score": 10,
"description": "The Add to Cart button renders different label text while the cart request is in-flight (e.g. 'Adding...' or similar) and shows 'Sold Out' when the selected variant has inventory === 0"
}
]
}
Quick View Body with Variant Selection and Cart
Problem/Feature Description
Threads & Co., an online fashion retailer, has a product listing page where each card now shows a modal overlay when "Quick View" is clicked. The modal shell and data-fetching hook are already in place. What's missing is the body content that renders inside the modal once product data has loaded.
Products have variants (typically size and colour combinations). Each variant has its own inventory count and its own image. The UX team requires that when a shopper picks a variant, the image displayed in the modal updates to reflect that variant's photo. Out-of-stock variants must be visually distinguishable and non-selectable. When the shopper successfully adds a product to the cart, the modal should close automatically — the team wants to avoid the shopper feeling "stuck" in the overlay.
The body must also accommodate shoppers who want more information than the quick view provides; a clear route back to the full product detail page must always be present.
A backend engineer has noted that on mobile the page behind the modal tends to scroll while the modal is open, which is disruptive. The implementation should prevent this.
Output Specification
Produce the following files:
QuickViewBody.jsx— the body component rendered inside the quick view modal once data is loadedQuickViewBody.css— associated stylesQuickViewPage.jsx— a minimal product listing page that wires the modal, the body, and a mockaddToCartfunction together so the integration can be verified
Input Files
The following stub files define the product data shape and the cart API signature your implementation should use. Extract them before beginning.
=============== FILE: types.js =============== /**
- @typedef {Object} ProductVariant
- @property {string} id
- @property {number} price
- @property {string} [image]
- @property {number} inventory - 0 means out of stock
- @property {Object.<string, string>} options - e.g. { Size: 'M', Colour: 'Red' }
*/
/**
- @typedef {Object} ProductOption
- @property {string} name - e.g. 'Size'
- @property {string[]} values - e.g. ['XS', 'S', 'M', 'L']
*/
/**
- @typedef {Object} Product
- @property {string} id
- @property {string} name
- @property {number} price
- @property {string} shortDescription
- @property {string} url
- @property {string[]} images
- @property {ProductVariant[]} variants
- @property {ProductOption[]} options
*/
/**
- Add an item to the cart.
- @param {{ variantId: string, quantity: number }} item
- @returns {Promise<void>}
*/ export async function addToCart(item) { // mock implementation return Promise.resolve(); }
{
"name": "finsi/quick-view-modal",
"version": "0.1.0",
"summary": "Product quick-view overlays with add-to-cart without leaving the listing page",
"skills": {
"quick-view-modal": {
"path": "SKILL.md"
}
}
}