
Responsive Storefront
- 68 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart, and touch-optimized components.
About
Delivers a mobile-first storefront layout with thumb-friendly navigation, sticky add-to-cart, and touch-optimized components. A developer uses it to raise mobile conversion on a storefront.
- Sticky add-to-cart and thumb-friendly navigation
- Touch-optimized mobile-first components
Responsive Storefront by the numbers
- 68 all-time installs (skills.sh)
- Ranked #1,163 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 responsive-storefrontAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 68 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart, and touch-optimized components.
Files
Responsive Storefront
Overview
Apply mobile-first responsive design patterns to a commerce storefront so that the shopping experience is fast and usable on phones, where the majority of commerce traffic originates. Key patterns include thumb-zone aware layouts, a sticky buy bar that appears on scroll, tap-target sizing, and responsive product grids that reflow without horizontal scrolling.
When to Use This Skill
- When more than 50% of storefront traffic comes from mobile devices (check your analytics)
- When building a new storefront from scratch and laying out the CSS architecture
- When auditing an existing storefront for mobile usability issues
- When implementing a product detail page and need a sticky buy button pattern
- When optimizing mobile conversion rate and cart abandonment
Core Instructions
Step 1: Determine the merchant's platform and choose the right approach
| Platform | Recommended Approach | Why |
|---|---|---|
| Shopify | Use a mobile-first OS2.0 theme (Dawn, Sense, Craft) — all are responsive by default; configure mobile layout in Theme Editor | Dawn scores 90+ on mobile PageSpeed out of the box; the Theme Editor exposes mobile-specific layout controls |
| WooCommerce | Use a mobile-first WooCommerce theme (Astra, Kadence, or Flatsome) — all include responsive breakpoints and mobile cart drawer built in | Astra and Kadence score well on Core Web Vitals; Flatsome has built-in sticky header and mobile menu without plugins |
| BigCommerce | Use Cornerstone theme which is mobile-first by design; configure breakpoints in Theme Editor | Cornerstone is BigCommerce's reference theme and passes Google's mobile usability tests |
| Custom / Headless | Write mobile-first CSS with min-width media queries and build a responsive product grid, sticky buy bar, and bottom-sheet cart drawer | Full control over all breakpoints and touch interactions; see patterns below |
Step 2: Configure mobile layout on your platform
---
Shopify
Theme Editor mobile configuration: 1. Go to Online Store → Themes → Customize 2. Click the Mobile preview icon in the top toolbar to preview on a phone 3. Configure Header settings:
- Set Mobile menu type: Drawer (recommended) or Dropdown
- Enable Sticky header so navigation stays visible while scrolling
4. Configure Collection page:
- Set Products per row (mobile) to 2 (standard) or 1 (large cards)
- Enable Filters as a drawer on mobile (not sidebar)
5. Configure Product page:
- Enable Sticky add-to-cart in the product section settings
- Set Media placement to stack gallery above product info on mobile (this is the default)
6. In Theme settings → Buttons, set button height to at least 44px to meet touch target requirements
Testing mobile performance: 1. Open Google PageSpeed Insights (pagespeed.web.dev) and enter your store URL 2. Review the Mobile tab — aim for 75+ Performance score 3. The most common fix: compress images via Settings → Files and replace large images with WebP versions
---
WooCommerce
Astra theme mobile configuration: 1. Go to Appearance → Customize → Header → Mobile Header 2. Configure hamburger menu style and position 3. Under Appearance → Customize → WooCommerce → Shop Page, set Products per row (mobile) to 2 4. Under Appearance → Customize → WooCommerce → Product Page, enable Sticky Add to Cart bar for mobile 5. In Appearance → Customize → Global → Container, ensure container width is 100% on mobile (no horizontal padding issues)
Kadence theme mobile configuration: 1. Go to Appearance → Customize → WooCommerce → Product Archive 2. Set mobile columns to 2 and configure card spacing 3. Under Appearance → Customize → WooCommerce → Product Page, enable Sticky Add to Cart 4. In Kadence → Header Builder, configure the mobile header layout — Kadence lets you drag elements (logo, menu, cart) to separate mobile header rows
Flatsome theme: 1. In Theme Options → WooCommerce → Products, configure mobile product grid columns 2. Enable Sticky Add to Cart in Theme Options → WooCommerce → Product 3. Flatsome's UX Builder has a mobile preview mode — use it to inspect every page's mobile layout
---
BigCommerce
Cornerstone mobile configuration: 1. Go to Storefront → My Themes → Customize 2. Preview on mobile using the device icon in the toolbar 3. Under Global → Layout, configure mobile breakpoints and container padding 4. Under Product Page → Images, set gallery layout to Stacked on mobile (image above product info) 5. Enable Mobile overlay for the cart — this converts the cart from a sidebar to a bottom sheet on mobile 6. Under Category Page → Product Cards, set mobile columns to 2
---
Custom / Headless
Mobile-first CSS foundations:
/* tokens.css */
:root {
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 1.5rem;
--touch-target: 44px; /* WCAG 2.5.5 minimum tap target */
--content-max-width: 1200px;
}
/* Breakpoints: sm=640px, md=768px, lg=1024px */
/* Write base styles for mobile, add min-width queries for larger screens */Responsive product grid (auto-fills columns based on available width):
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr));
gap: var(--space-md);
padding: var(--space-md);
}
@media (min-width: 640px) {
.product-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
}
@media (min-width: 1024px) {
.product-grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--space-lg); }
}
/* Minimum 44px touch target on product card CTA */
.product-card__cta { min-height: var(--touch-target); display: flex; align-items: center; }Sticky buy bar — appears when primary Add to Cart scrolls out of view:
export function StickyBuyBar({ product, onAddToCart }) {
const [visible, setVisible] = useState(false);
useEffect(() => {
const primaryBtn = document.getElementById('main-add-to-cart');
if (!primaryBtn) return;
const obs = new IntersectionObserver(([e]) => setVisible(!e.isIntersecting));
obs.observe(primaryBtn);
return () => obs.disconnect();
}, []);
return (
<div className={`sticky-buy-bar ${visible ? 'visible' : ''}`} aria-hidden={!visible}>
<img src={product.image} alt="" width="40" height="40" />
<span>{product.name}</span>
<span>${product.price}</span>
<button onClick={onAddToCart} tabIndex={visible ? 0 : -1}>Add to Cart</button>
</div>
);
}.sticky-buy-bar {
position: fixed; bottom: 0; left: 0; right: 0;
background: #fff; border-top: 1px solid #e2e8f0;
transform: translateY(100%); transition: transform 0.2s ease; z-index: 50;
padding-bottom: env(safe-area-inset-bottom); /* iPhone home indicator */
display: flex; align-items: center; gap: 0.5rem; padding: 0.75rem 1rem;
}
.sticky-buy-bar.visible { transform: translateY(0); }Mobile cart drawer (bottom sheet pattern):
.cart-drawer {
position: fixed; right: 0; top: 0; bottom: 0;
width: min(400px, 100vw); background: #fff;
transform: translateX(100%); transition: transform 0.3s ease;
overflow-y: auto; overscroll-behavior: contain;
}
@media (max-width: 640px) {
.cart-drawer {
top: auto; width: 100vw; height: 85vh;
transform: translateY(100%); border-radius: 16px 16px 0 0;
}
.cart-drawer.open { transform: translateY(0); }
}
.cart-drawer.open { transform: translateX(0); }
/* Checkout button stays within thumb reach */
.cart-checkout-btn {
position: sticky; bottom: 0; background: #fff;
padding: var(--space-md);
padding-bottom: calc(var(--space-md) + env(safe-area-inset-bottom));
}Global 44px touch targets:
button, a, input[type="checkbox"], input[type="radio"], select, [role="button"] {
min-height: var(--touch-target);
min-width: var(--touch-target);
}Best Practices
- Write mobile-first CSS — start with the smallest screen styles and add
min-widthmedia queries to enlarge; the reverse approach leads to specificity conflicts - Use `env(safe-area-inset-bottom)` — required for iPhone notch/home indicator; without it, bottom-fixed buttons are obscured
- Test with real devices — browser DevTools emulation does not replicate iOS Safari's dynamic viewport height or touch scroll inertia
- Use `overscroll-behavior: contain` on scroll containers — prevents body scroll from leaking when users swipe in the cart drawer
- Use `100dvh` instead of `100vh` on full-screen elements — iOS Safari's dynamic toolbar causes
100vhto include the toolbar height, causing overflow - Set font-size to at least 16px on
<body>— prevents iOS Safari from auto-zooming on input focus
Common Pitfalls
| Problem | Solution |
|---|---|
| Sticky buy bar obscures footer or checkout button | Hide the bar when the footer or checkout button enters the viewport using IntersectionObserver |
| iOS Safari layout jumps when toolbar appears | Use 100dvh (dynamic viewport height) instead of 100vh; fall back with @supports (height: 100dvh) |
| Font too small to read without pinch-zooming | Set font-size: 1rem (16px) on body; never set it lower on mobile |
| Cart drawer body scroll | Apply overflow: hidden on <body> when drawer is open; restore on close |
| Product images load slowly on mobile | Specify width and height attributes to prevent layout shift; use loading="lazy" for below-fold images |
Related Skills
- @mega-menu-builder
- @quick-view-modal
- @accessibility-commerce
- @storefront-theming
{
"context": "Tests whether the agent implements the cart drawer as a bottom sheet on mobile and sidebar on desktop, with correct overscroll containment, body scroll locking, safe-area-inset-bottom on checkout button, responsive hero image using the picture element with art direction, and correct font-size for iOS input zoom prevention.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Desktop sidebar layout",
"max_score": 8,
"description": "On desktop the cart drawer is positioned as a right-side sidebar: `position: fixed; right: 0; top: 0; bottom: 0` with `width: min(400px, 100vw)` and hidden via `transform: translateX(100%)`"
},
{
"name": "Mobile bottom sheet",
"max_score": 10,
"description": "At max-width: 640px the drawer switches to a bottom sheet: `top: auto; width: 100vw; height: 85vh; transform: translateY(100%)`"
},
{
"name": "Bottom sheet border radius",
"max_score": 6,
"description": "The mobile bottom sheet has `border-radius: 16px 16px 0 0` (rounded top corners, flat bottom)"
},
{
"name": "overscroll-behavior contain",
"max_score": 10,
"description": "The cart drawer container applies `overscroll-behavior: contain` to prevent body scroll from leaking when scrolling inside the drawer"
},
{
"name": "Body scroll lock — overflow hidden",
"max_score": 8,
"description": "When the drawer opens, `overflow: hidden` is applied to the body element to prevent background scrolling"
},
{
"name": "Body scroll lock — touch-action",
"max_score": 8,
"description": "When the drawer opens, `touch-action: none` is applied to the body (or document.documentElement) to prevent touch scroll on mobile"
},
{
"name": "Checkout button sticky bottom",
"max_score": 8,
"description": "The checkout button uses `position: sticky; bottom: 0` so it stays at the bottom of the drawer as the item list scrolls"
},
{
"name": "Checkout safe area inset",
"max_score": 8,
"description": "The checkout button's padding-bottom includes `env(safe-area-inset-bottom)` (e.g., via calc()) to avoid being hidden behind the iPhone home indicator"
},
{
"name": "Picture element art direction",
"max_score": 8,
"description": "The HeroBanner uses a `<picture>` element with at least two `<source>` tags to serve different image crops for mobile and desktop viewports"
},
{
"name": "Hero image loading attributes",
"max_score": 6,
"description": "The hero `<img>` tag includes `loading=\"eager\"` and `fetchpriority=\"high\"` (NOT loading=\"lazy\")"
},
{
"name": "WebP format in hero",
"max_score": 6,
"description": "The hero image sources reference .webp format files (in srcset or src attributes)"
},
{
"name": "Body font size 16px",
"max_score": 6,
"description": "CSS sets body font-size to at least 16px (to prevent iOS auto-zoom when input fields are focused)"
},
{
"name": "No hover-only CTA states",
"max_score": 8,
"description": "Interactive button states use :focus-visible and/or :active rather than (or in addition to) :hover for primary CTAs"
}
]
}
Mobile-Optimized Cart Drawer
Problem/Feature Description
Bloom Home, a home goods retailer, is rebuilding their cart experience after discovering that mobile users are abandoning their cart at twice the rate of desktop users. User testing revealed several pain points: the cart panel slides in from the side on phones, making it feel like a desktop pattern forced onto a small screen; users accidentally trigger page scrolling instead of scrolling the cart list; the checkout button disappears behind the iPhone home indicator; and tapping outside the cart is unreliable.
The engineering team wants a cart drawer component that feels comfortable on both mobile and desktop. Mobile users in testing kept accidentally triggering page scroll when they meant to scroll within the cart list, and the checkout button was obscured behind the phone's system UI at the bottom of the screen. On desktop, users expect the familiar right-side panel layout. The component must lock the underlying page from scrolling when the cart is open, and support standard cart contents: a scrollable list of items with quantity controls and a checkout button.
The storefront already uses a React codebase. The page also needs a hero banner image at the top that serves different crops for mobile versus desktop browsers, loaded efficiently.
Output Specification
Produce the following files:
CartDrawer.jsx— React component for the cart drawer with open/close behavior and body scroll lockingCartDrawer.css— all CSS for the drawer including responsive layout, open/close transitions, and checkout button positioningHeroBanner.jsx— a React component rendering a responsive hero image with art direction for mobile vs desktopimplementation-notes.md— brief notes covering the mobile vs desktop layout switch, scroll locking approach, and image art direction technique
The component should accept props: isOpen (boolean), onClose (function), items (array of {id, name, price, quantity}), onCheckout (function). Use placeholder image paths in HeroBanner — no actual image files are needed.
{
"context": "Tests whether the agent applies mobile-first CSS architecture with correct design tokens, responsive grid using auto-fill, proper touch target sizing, and correct variant swatch hit area implementation for a commerce product catalog.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Mobile-first base styles",
"max_score": 10,
"description": "Base grid styles target small screens first, with min-width media queries used to enhance for larger screens (NOT max-width for narrowing down)"
},
{
"name": "CSS custom properties",
"max_score": 8,
"description": "Spacing, touch target, and/or layout values are defined as CSS custom properties (CSS variables with -- prefix) rather than hard-coded magic numbers"
},
{
"name": "Touch target token value",
"max_score": 8,
"description": "The touch target custom property or hard-coded value is 44px (matching WCAG 2.5.5 requirement)"
},
{
"name": "Grid auto-fill pattern",
"max_score": 10,
"description": "The product grid uses `display: grid` with `repeat(auto-fill, ...)` so column count adjusts automatically without explicit per-breakpoint column counts"
},
{
"name": "Grid minmax sizing",
"max_score": 8,
"description": "The grid uses `minmax(min(160px, 100%), 1fr)` (or equivalent min() formula) for the mobile base column size"
},
{
"name": "Breakpoint values",
"max_score": 6,
"description": "Media query breakpoints used are 640px and/or 1024px (matching the sm/lg breakpoint spec), not arbitrary values like 600px or 900px"
},
{
"name": "Image aspect ratio",
"max_score": 8,
"description": "Product card images use `aspect-ratio: 4/5` (portrait) and `object-fit: cover`"
},
{
"name": "Global touch target rule",
"max_score": 8,
"description": "A CSS rule sets min-height and min-width of 44px on interactive elements (button, a, select, or [role=button]) globally"
},
{
"name": "CTA button push to bottom",
"max_score": 6,
"description": "The Add to Cart button (or card CTA) uses `margin-top: auto` to push it to the bottom of the card flex container"
},
{
"name": "Swatch hit area 44px",
"max_score": 10,
"description": "Color variant swatches have a 44px hit area (width: 44px; height: 44px) even if the visible circle is smaller"
},
{
"name": "Swatch visual vs hit area",
"max_score": 8,
"description": "Swatch visual indicator is rendered smaller than 44px (e.g., via a ::before pseudo-element or inner element) while the click/tap target remains 44px"
},
{
"name": "Image lazy loading",
"max_score": 10,
"description": "Product card images include `loading=\"lazy\"` and have explicit `width` and `height` attributes to prevent layout shift"
}
]
}
Mobile-First Product Catalog Page
Problem/Feature Description
Finsi, a direct-to-consumer fashion brand, has seen mobile traffic climb to 68% of total storefront visits but conversion on phones is lagging behind desktop by 40%. Their product catalog page was built desktop-first years ago and suffers from images that overflow on small screens, buttons that are frustratingly difficult to tap accurately, and color swatch selectors that frustrate users on touch screens.
The team wants a fresh CSS foundation for their catalog page. They need a product grid that works well across phones, tablets, and desktop screens. Each product card should display a product image (portrait orientation), a name, a price, a row of color variant swatches, and an "Add to Cart" button.
Output Specification
Produce a self-contained HTML file named catalog.html containing:
- Inline
<style>blocks (or a<style>tag in<head>) with all the CSS needed - A
<div class="product-grid">containing at least 4 sample product cards - Each card must include: a product image (can be a placeholder), product name, price, at least 3 color variant swatches, and an "Add to Cart" button
- A brief
notes.mdfile explaining the key CSS decisions you made and which mobile UX concerns they address
{
"context": "Tests whether the agent correctly implements the sticky buy bar using getBoundingClientRect scroll detection, aria accessibility attributes, safe-area-inset-bottom for iOS, translateY CSS animation, and the mobile-first PDP two-column layout with sticky image panel.",
"type": "weighted_checklist",
"checklist": [
{
"name": "getBoundingClientRect detection",
"max_score": 10,
"description": "The scroll handler calls getBoundingClientRect() on the primary Add to Cart button ref to determine visibility (NOT checking scrollY or offsetTop directly)"
},
{
"name": "Visibility condition",
"max_score": 10,
"description": "The sticky bar becomes visible when the primary button's rect.bottom < 0 (button has scrolled above the viewport top)"
},
{
"name": "Passive scroll listener",
"max_score": 8,
"description": "The scroll event listener is registered with `{ passive: true }` option"
},
{
"name": "aria-hidden management",
"max_score": 8,
"description": "The sticky bar container uses aria-hidden={true} when not visible and aria-hidden={false} (or no attribute) when visible"
},
{
"name": "tabIndex management",
"max_score": 8,
"description": "Interactive elements inside the sticky bar use tabIndex={-1} when the bar is hidden and tabIndex={0} (or default) when visible"
},
{
"name": "CSS translateY animation",
"max_score": 8,
"description": "The sticky bar is hidden via `transform: translateY(100%)` and shown via `transform: translateY(0)` (NOT via display:none or visibility:hidden alone)"
},
{
"name": "Safe area inset bottom",
"max_score": 10,
"description": "The sticky bar CSS uses `env(safe-area-inset-bottom)` in the padding-bottom to account for iPhone home indicator"
},
{
"name": "Fixed positioning",
"max_score": 6,
"description": "The sticky bar uses `position: fixed; bottom: 0; left: 0; right: 0` (anchored to the bottom of the viewport)"
},
{
"name": "z-index 50",
"max_score": 6,
"description": "The sticky bar has z-index: 50 (matching the specified value, not an arbitrary high number like 999)"
},
{
"name": "PDP mobile single column",
"max_score": 6,
"description": "The PDP layout uses a single column (`grid-template-columns: 1fr`) as the base mobile style"
},
{
"name": "PDP desktop two-panel",
"max_score": 6,
"description": "The PDP layout switches to `grid-template-columns: 1fr 1fr` at 768px (md breakpoint)"
},
{
"name": "Sticky images panel",
"max_score": 6,
"description": "The images/gallery panel in the PDP uses `position: sticky` on desktop so it stays in view while the right panel scrolls"
},
{
"name": "Dynamic viewport height",
"max_score": 8,
"description": "Any full-height layout element uses `100dvh` (not just `100vh`) to handle iOS Safari's dynamic toolbar"
}
]
}
Product Detail Page with Persistent Buy Button
Problem/Feature Description
Trove Goods, an outdoor gear e-commerce company, has identified that mobile shoppers frequently scroll past the primary "Add to Cart" button on their product detail pages and then lose it — they have to scroll back up to buy. Analytics show that 35% of users who scroll to the reviews section never return to complete a purchase. The product manager wants a persistent buy button that becomes visible only after the original Add to Cart button has scrolled off-screen, allowing customers to purchase at any point during their browsing.
Additionally, the PDP layout itself needs to work on phones (a single column view where the product details stack below the images) and scale up to a two-panel layout on larger screens where the image gallery stays anchored while the user scrolls through descriptions, specs, and reviews.
The solution must handle the iPhone home indicator safely — previous attempts had buttons hidden behind it — and the layout must not jump around when iOS Safari's dynamic toolbar appears and disappears.
Output Specification
Produce the following files:
StickyBuyBar.jsx— a React component implementing the persistent buy bar behaviorStickyBuyBar.css— the CSS for the sticky barpdp-layout.css— CSS for the product detail page layout (single column mobile, two-panel desktop)implementation-notes.md— a short explanation of how the scroll detection works and how iOS edge cases are handled
{
"name": "finsi/responsive-storefront",
"version": "0.1.0",
"summary": "Mobile-first responsive patterns for commerce (thumb-friendly cart, sticky buy bar)",
"skills": {
"responsive-storefront": {
"path": "SKILL.md"
}
}
}