shop ecommerce catalog products filter grid modern
Shop Catalog
Multi-product shop catalog with category sidebar, filter options, sortable product grid, and pagination.
modern Responsive Vanilla JS
Live Preview
Sections
navbarsidebar-filterproduct-gridpaginationfooter
Patterns used
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Atelier Craft — Shop Catalog</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://webspire.de/webspire-tokens.css">
<link rel="stylesheet" href="https://webspire.de/webspire-components.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body { font-family: 'Inter', sans-serif; }
/* Brand tokens */
:root {
--ws-color-primary: #0f172a;
--ws-color-primary-hover: #1e293b;
--ws-color-primary-soft: #f1f5f9;
--ws-color-surface: #ffffff;
--ws-color-surface-alt: #f8fafc;
--ws-color-text: #0f172a;
--ws-color-text-soft: #334155;
--ws-color-text-muted: #64748b;
--ws-color-border: #e2e8f0;
--ws-color-danger: #ef4444;
}
/* interactions/hover-lift */
.hover-lift {
--lift-distance: -4px;
--lift-shadow: 0 8px 24px oklch(0 0 0 / 0.2);
--lift-duration: 0.25s;
transition:
transform var(--lift-duration) cubic-bezier(0.16, 1, 0.3, 1),
box-shadow var(--lift-duration) cubic-bezier(0.16, 1, 0.3, 1);
}
.hover-lift:hover {
transform: translateY(var(--lift-distance));
box-shadow: var(--lift-shadow);
}
@media (prefers-reduced-motion: reduce) {
.hover-lift { transition: none; }
}
/* Template helpers */
.quick-view { opacity: 0; transition: opacity 0.25s ease; }
.product-card:hover .quick-view { opacity: 1; }
.product-card:hover .product-img { transform: scale(1.03); }
.product-img { transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
.color-dot { transition: box-shadow 0.2s ease; }
.color-dot:hover, .color-dot[aria-checked="true"] { box-shadow: 0 0 0 2px white, 0 0 0 4px currentColor; }
</style>
</head>
<body class="bg-slate-50 text-slate-900 antialiased">
<!-- Navbar -->
<nav class="ws-navbar sticky top-0 z-50 bg-white border-b border-slate-200" aria-label="Main navigation">
<div class="max-w-7xl mx-auto flex items-center justify-between px-4 sm:px-6 py-3.5">
<a href="#" class="text-xl font-bold tracking-tight">Atelier<span class="text-slate-400 font-light"> Craft</span></a>
<div class="hidden md:flex flex-1 max-w-md mx-8">
<label for="search" class="sr-only">Search products</label>
<div class="relative w-full">
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
<input id="search" type="search" placeholder="Search products..." class="w-full pl-10 pr-4 py-2 text-sm bg-slate-50 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-slate-900 focus:border-transparent" />
</div>
</div>
<div class="flex items-center gap-4">
<a href="#" class="relative p-2 text-slate-600 hover:text-slate-900 transition-colors" aria-label="Shopping cart, 3 items">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"/>
</svg>
<span class="absolute -top-0.5 -right-0.5 flex items-center justify-center w-4.5 h-4.5 text-[10px] font-bold text-white bg-slate-900 rounded-full" aria-hidden="true">3</span>
</a>
<button id="filter-toggle" class="md:hidden p-2 -mr-2 text-slate-600" aria-label="Toggle filters" aria-expanded="false" aria-controls="sidebar-filters">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"/>
</svg>
</button>
</div>
</div>
</nav>
<!-- Main layout -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 py-8">
<!-- Breadcrumb -->
<nav aria-label="Breadcrumb" class="ws-breadcrumb mb-6">
<ol class="flex items-center gap-2 text-sm text-slate-500">
<li><a href="#" class="hover:text-slate-900 transition-colors">Home</a></li>
<li aria-hidden="true">/</li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Shop</a></li>
<li aria-hidden="true">/</li>
<li class="text-slate-900 font-medium" aria-current="page">All Products</li>
</ol>
</nav>
<div class="flex gap-8">
<!-- Sidebar -->
<aside id="sidebar-filters" class="ws-sidebar hidden md:block w-64 shrink-0" aria-label="Product filters">
<div class="sticky top-20 space-y-8">
<!-- Categories -->
<div>
<h3 class="text-xs font-semibold uppercase tracking-wider text-slate-400 mb-3">Categories</h3>
<ul class="space-y-1" role="list">
<li><a href="#" class="flex items-center justify-between px-3 py-2 text-sm font-medium bg-slate-900 text-white rounded-lg">All <span class="text-slate-300">42</span></a></li>
<li><a href="#" class="flex items-center justify-between px-3 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded-lg transition-colors">Bags <span class="text-slate-400">12</span></a></li>
<li><a href="#" class="flex items-center justify-between px-3 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded-lg transition-colors">Accessories <span class="text-slate-400">9</span></a></li>
<li><a href="#" class="flex items-center justify-between px-3 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded-lg transition-colors">Shoes <span class="text-slate-400">8</span></a></li>
<li><a href="#" class="flex items-center justify-between px-3 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded-lg transition-colors">Clothing <span class="text-slate-400">7</span></a></li>
<li><a href="#" class="flex items-center justify-between px-3 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded-lg transition-colors">Sale <span class="text-red-500">6</span></a></li>
</ul>
</div>
<!-- Price Range -->
<div>
<h3 class="text-xs font-semibold uppercase tracking-wider text-slate-400 mb-3">Price Range</h3>
<div class="flex items-center gap-3">
<div class="flex-1">
<label for="price-min" class="sr-only">Minimum price</label>
<input id="price-min" type="text" value="$0" class="w-full px-3 py-2 text-sm text-center bg-white border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-slate-900" />
</div>
<span class="text-slate-300" aria-hidden="true">–</span>
<div class="flex-1">
<label for="price-max" class="sr-only">Maximum price</label>
<input id="price-max" type="text" value="$500" class="w-full px-3 py-2 text-sm text-center bg-white border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-slate-900" />
</div>
</div>
</div>
<!-- Color -->
<div>
<h3 class="text-xs font-semibold uppercase tracking-wider text-slate-400 mb-3">Color</h3>
<div class="flex items-center gap-3" role="radiogroup" aria-label="Filter by color">
<button class="color-dot w-7 h-7 rounded-full bg-stone-800 border-2 border-white shadow-sm" style="color: #44403c;" role="radio" aria-checked="false" aria-label="Brown"></button>
<button class="color-dot w-7 h-7 rounded-full bg-slate-900 border-2 border-white shadow-sm" style="color: #0f172a;" role="radio" aria-checked="false" aria-label="Black"></button>
<button class="color-dot w-7 h-7 rounded-full bg-amber-100 border-2 border-slate-200 shadow-sm" style="color: #d97706;" role="radio" aria-checked="false" aria-label="Natural"></button>
<button class="color-dot w-7 h-7 rounded-full bg-rose-200 border-2 border-white shadow-sm" style="color: #e11d48;" role="radio" aria-checked="false" aria-label="Rose"></button>
</div>
</div>
<!-- Apply -->
<button class="w-full py-2.5 text-sm font-medium bg-slate-900 text-white rounded-lg hover:bg-slate-800 transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-900">
Apply Filters
</button>
</div>
</aside>
<!-- Product Grid -->
<main class="flex-1 min-w-0">
<!-- Grid header -->
<div class="flex items-center justify-between mb-6">
<p class="text-sm text-slate-500">Showing <span class="font-medium text-slate-900">42 results</span></p>
<div class="flex items-center gap-2">
<label for="sort" class="text-sm text-slate-500 hidden sm:inline">Sort by:</label>
<select id="sort" class="text-sm font-medium text-slate-900 bg-white border border-slate-200 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-900">
<option>Newest</option>
<option>Price: Low to High</option>
<option>Price: High to Low</option>
<option>Best Selling</option>
</select>
</div>
</div>
<!-- Products -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
<!-- Product 1 -->
<article class="ws-product-card product-card group bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg hover:border-slate-300 transition-all duration-300">
<div class="relative overflow-hidden">
<div class="product-img aspect-[4/3] bg-stone-100" role="img" aria-label="Canvas Weekender bag"></div>
<button class="quick-view absolute inset-0 flex items-center justify-center bg-slate-900/40 text-white text-sm font-medium focus:opacity-100" aria-label="Quick view Canvas Weekender">
<span class="bg-white text-slate-900 px-5 py-2 rounded-lg text-sm font-medium shadow-lg">Quick View</span>
</button>
</div>
<div class="p-4">
<h3 class="font-medium text-slate-900">Canvas Weekender</h3>
<p class="text-sm text-slate-500 mt-0.5">Waxed cotton carryall for short trips</p>
<p class="text-sm font-semibold text-slate-900 mt-2">$149</p>
</div>
</article>
<!-- Product 2 -->
<article class="ws-product-card product-card group bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg hover:border-slate-300 transition-all duration-300">
<div class="relative overflow-hidden">
<div class="product-img aspect-[4/3] bg-amber-50" role="img" aria-label="Leather Crossbody bag"></div>
<button class="quick-view absolute inset-0 flex items-center justify-center bg-slate-900/40 text-white text-sm font-medium focus:opacity-100" aria-label="Quick view Leather Crossbody">
<span class="bg-white text-slate-900 px-5 py-2 rounded-lg text-sm font-medium shadow-lg">Quick View</span>
</button>
</div>
<div class="p-4">
<h3 class="font-medium text-slate-900">Leather Crossbody</h3>
<p class="text-sm text-slate-500 mt-0.5">Full-grain leather with brass hardware</p>
<p class="text-sm font-semibold text-slate-900 mt-2">$189</p>
</div>
</article>
<!-- Product 3 -->
<article class="ws-product-card product-card group bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg hover:border-slate-300 transition-all duration-300">
<div class="relative overflow-hidden">
<div class="product-img aspect-[4/3] bg-slate-100" role="img" aria-label="Merino Scarf"></div>
<span class="absolute top-3 left-3 bg-slate-900 text-white text-[10px] font-bold uppercase tracking-wide px-2 py-1 rounded">New</span>
<button class="quick-view absolute inset-0 flex items-center justify-center bg-slate-900/40 text-white text-sm font-medium focus:opacity-100" aria-label="Quick view Merino Scarf">
<span class="bg-white text-slate-900 px-5 py-2 rounded-lg text-sm font-medium shadow-lg">Quick View</span>
</button>
</div>
<div class="p-4">
<h3 class="font-medium text-slate-900">Merino Scarf</h3>
<p class="text-sm text-slate-500 mt-0.5">Lightweight wool blend, hand-finished edges</p>
<p class="text-sm font-semibold text-slate-900 mt-2">$68</p>
</div>
</article>
<!-- Product 4 -->
<article class="ws-product-card product-card group bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg hover:border-slate-300 transition-all duration-300">
<div class="relative overflow-hidden">
<div class="product-img aspect-[4/3] bg-rose-50" role="img" aria-label="Suede Chelsea Boots"></div>
<button class="quick-view absolute inset-0 flex items-center justify-center bg-slate-900/40 text-white text-sm font-medium focus:opacity-100" aria-label="Quick view Suede Chelsea Boots">
<span class="bg-white text-slate-900 px-5 py-2 rounded-lg text-sm font-medium shadow-lg">Quick View</span>
</button>
</div>
<div class="p-4">
<h3 class="font-medium text-slate-900">Suede Chelsea Boots</h3>
<p class="text-sm text-slate-500 mt-0.5">Italian suede, Goodyear welted sole</p>
<p class="text-sm font-semibold text-slate-900 mt-2">$295</p>
</div>
</article>
<!-- Product 5 -->
<article class="ws-product-card product-card group bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg hover:border-slate-300 transition-all duration-300">
<div class="relative overflow-hidden">
<div class="product-img aspect-[4/3] bg-sky-50" role="img" aria-label="Denim Tote"></div>
<button class="quick-view absolute inset-0 flex items-center justify-center bg-slate-900/40 text-white text-sm font-medium focus:opacity-100" aria-label="Quick view Denim Tote">
<span class="bg-white text-slate-900 px-5 py-2 rounded-lg text-sm font-medium shadow-lg">Quick View</span>
</button>
</div>
<div class="p-4">
<h3 class="font-medium text-slate-900">Denim Tote</h3>
<p class="text-sm text-slate-500 mt-0.5">Selvedge denim with reinforced handles</p>
<p class="text-sm font-semibold text-slate-900 mt-2">$79</p>
</div>
</article>
<!-- Product 6 -->
<article class="ws-product-card product-card group bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg hover:border-slate-300 transition-all duration-300">
<div class="relative overflow-hidden">
<div class="product-img aspect-[4/3] bg-emerald-50" role="img" aria-label="Linen Camp Shirt"></div>
<button class="quick-view absolute inset-0 flex items-center justify-center bg-slate-900/40 text-white text-sm font-medium focus:opacity-100" aria-label="Quick view Linen Camp Shirt">
<span class="bg-white text-slate-900 px-5 py-2 rounded-lg text-sm font-medium shadow-lg">Quick View</span>
</button>
</div>
<div class="p-4">
<h3 class="font-medium text-slate-900">Linen Camp Shirt</h3>
<p class="text-sm text-slate-500 mt-0.5">Relaxed fit, coconut shell buttons</p>
<p class="text-sm font-semibold text-slate-900 mt-2">$115</p>
</div>
</article>
<!-- Product 7 -->
<article class="ws-product-card product-card group bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg hover:border-slate-300 transition-all duration-300">
<div class="relative overflow-hidden">
<div class="product-img aspect-[4/3] bg-violet-50" role="img" aria-label="Ceramic Watch"></div>
<button class="quick-view absolute inset-0 flex items-center justify-center bg-slate-900/40 text-white text-sm font-medium focus:opacity-100" aria-label="Quick view Ceramic Watch">
<span class="bg-white text-slate-900 px-5 py-2 rounded-lg text-sm font-medium shadow-lg">Quick View</span>
</button>
</div>
<div class="p-4">
<h3 class="font-medium text-slate-900">Ceramic Watch</h3>
<p class="text-sm text-slate-500 mt-0.5">Minimalist dial, Japanese quartz movement</p>
<p class="text-sm font-semibold text-slate-900 mt-2">$245</p>
</div>
</article>
<!-- Product 8 -->
<article class="ws-product-card product-card group bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg hover:border-slate-300 transition-all duration-300">
<div class="relative overflow-hidden">
<div class="product-img aspect-[4/3] bg-orange-50" role="img" aria-label="Woven Belt"></div>
<span class="absolute top-3 left-3 bg-red-500 text-white text-[10px] font-bold uppercase tracking-wide px-2 py-1 rounded">Sale</span>
<button class="quick-view absolute inset-0 flex items-center justify-center bg-slate-900/40 text-white text-sm font-medium focus:opacity-100" aria-label="Quick view Woven Belt">
<span class="bg-white text-slate-900 px-5 py-2 rounded-lg text-sm font-medium shadow-lg">Quick View</span>
</button>
</div>
<div class="p-4">
<h3 class="font-medium text-slate-900">Woven Belt</h3>
<p class="text-sm text-slate-500 mt-0.5">Braided elastic with brushed nickel buckle</p>
<p class="text-sm font-semibold text-slate-900 mt-2"><span class="line-through text-slate-400 font-normal mr-1.5">$55</span>$39</p>
</div>
</article>
<!-- Product 9 -->
<article class="ws-product-card product-card group bg-white rounded-xl border border-slate-200 overflow-hidden hover:shadow-lg hover:border-slate-300 transition-all duration-300">
<div class="relative overflow-hidden">
<div class="product-img aspect-[4/3] bg-teal-50" role="img" aria-label="Leather Bifold Wallet"></div>
<button class="quick-view absolute inset-0 flex items-center justify-center bg-slate-900/40 text-white text-sm font-medium focus:opacity-100" aria-label="Quick view Leather Bifold Wallet">
<span class="bg-white text-slate-900 px-5 py-2 rounded-lg text-sm font-medium shadow-lg">Quick View</span>
</button>
</div>
<div class="p-4">
<h3 class="font-medium text-slate-900">Leather Bifold Wallet</h3>
<p class="text-sm text-slate-500 mt-0.5">Vegetable-tanned, six card slots</p>
<p class="text-sm font-semibold text-slate-900 mt-2">$95</p>
</div>
</article>
</div>
<!-- Pagination -->
<nav class="ws-pagination flex items-center justify-center gap-1 mt-10" aria-label="Pagination">
<a href="#" class="flex items-center justify-center w-10 h-10 text-sm font-semibold bg-slate-900 text-white rounded-lg" aria-current="page" aria-label="Page 1">1</a>
<a href="#" class="flex items-center justify-center w-10 h-10 text-sm font-medium text-slate-600 hover:bg-slate-100 rounded-lg transition-colors" aria-label="Page 2">2</a>
<a href="#" class="flex items-center justify-center w-10 h-10 text-sm font-medium text-slate-600 hover:bg-slate-100 rounded-lg transition-colors" aria-label="Page 3">3</a>
<a href="#" class="flex items-center justify-center h-10 px-4 text-sm font-medium text-slate-600 hover:bg-slate-100 rounded-lg transition-colors ml-1" aria-label="Next page">
Next
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</a>
</nav>
</main>
</div>
</div>
<!-- Footer -->
<footer class="ws-footer bg-white border-t border-slate-200 mt-16" aria-label="Site footer">
<div class="max-w-7xl mx-auto px-4 sm:px-6 py-12">
<div class="grid grid-cols-2 md:grid-cols-4 gap-8">
<div>
<h4 class="text-sm font-semibold text-slate-900 mb-4">Shop</h4>
<ul class="space-y-2.5 text-sm text-slate-500">
<li><a href="#" class="hover:text-slate-900 transition-colors">New Arrivals</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Best Sellers</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Sale</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Gift Cards</a></li>
</ul>
</div>
<div>
<h4 class="text-sm font-semibold text-slate-900 mb-4">Company</h4>
<ul class="space-y-2.5 text-sm text-slate-500">
<li><a href="#" class="hover:text-slate-900 transition-colors">Our Story</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Sustainability</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Careers</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Press</a></li>
</ul>
</div>
<div>
<h4 class="text-sm font-semibold text-slate-900 mb-4">Support</h4>
<ul class="space-y-2.5 text-sm text-slate-500">
<li><a href="#" class="hover:text-slate-900 transition-colors">Shipping & Returns</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Size Guide</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Contact Us</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">FAQ</a></li>
</ul>
</div>
<div>
<h4 class="text-sm font-semibold text-slate-900 mb-4">Connect</h4>
<ul class="space-y-2.5 text-sm text-slate-500">
<li><a href="#" class="hover:text-slate-900 transition-colors">Instagram</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Pinterest</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Newsletter</a></li>
<li><a href="#" class="hover:text-slate-900 transition-colors">Journal</a></li>
</ul>
</div>
</div>
<div class="flex flex-col sm:flex-row items-center justify-between mt-10 pt-8 border-t border-slate-100">
<p class="text-sm text-slate-400">© 2026 Atelier Craft. All rights reserved.</p>
<div class="flex items-center gap-6 mt-4 sm:mt-0">
<a href="#" class="text-sm text-slate-400 hover:text-slate-900 transition-colors">Privacy</a>
<a href="#" class="text-sm text-slate-400 hover:text-slate-900 transition-colors">Terms</a>
<a href="#" class="text-sm text-slate-400 hover:text-slate-900 transition-colors">Cookies</a>
</div>
</div>
</div>
</footer>
<script>
const filterToggle = document.getElementById('filter-toggle');
const sidebar = document.getElementById('sidebar-filters');
if (filterToggle && sidebar) {
filterToggle.addEventListener('click', () => {
const isOpen = !sidebar.classList.contains('hidden');
sidebar.classList.toggle('hidden');
sidebar.classList.toggle('fixed');
sidebar.classList.toggle('inset-0');
sidebar.classList.toggle('z-40');
sidebar.classList.toggle('bg-white');
sidebar.classList.toggle('p-6');
sidebar.classList.toggle('overflow-y-auto');
filterToggle.setAttribute('aria-expanded', String(!isOpen));
});
}
</script>
</body>
</html>
Full-featured product catalog with filterable sidebar and sortable grid. Clean, functional design that scales from small boutiques to larger product ranges.