<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Product Cart — Codekk</title>
<!-- Fonts: matching system exactly -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet" />
<!-- Tailwind -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
serif: ['DM Serif Display', 'serif'],
sans: ['DM Sans', 'sans-serif'],
},
colors: {
ink: '#111110',
'ink-light': '#1e1d1b',
'off-white': '#f5f4f1',
'warm-muted': '#888580',
'warm-border': '#2a2927',
'warm-mid': '#e8e7e4',
'warm-card': '#ffffff',
'warm-bg': '#f5f4f1',
},
}
}
}
</script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- NAVBAR (matching system) -->
<nav style="background: rgba(245,244,241,0.92); border-bottom: 1px solid #e8e7e4; backdrop-filter: blur(12px); position: sticky; top: 0; z-index: 100;">
<div class="max-w-[1280px] mx-auto px-6 lg:px-10">
<div class="flex items-center justify-between h-[64px]">
<!-- Logo -->
<a href="#" class="flex items-center gap-2 no-underline">
<div class="w-7 h-7 flex items-center justify-center rounded-[3px]" style="background:#111110;">
<span class="font-serif text-[14px] text-off-white leading-none" style="color:#f5f4f1;">F</span>
</div>
<span class="font-serif text-[19px] text-ink tracking-tight leading-none">
Formfield<span style="color:#888580;">.</span>
</span>
</a>
<!-- Page title -->
<span class="section-label hidden sm:block">Shop</span>
<!-- Cart button -->
<button id="open-cart" class="relative flex items-center gap-2 px-4 py-2 rounded-[4px] border border-warm-mid bg-white hover:border-warm-border transition-colors cursor-pointer"
style="font-family:'DM Sans',sans-serif;">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M1 1h2l2.4 7.2a1 1 0 0 0 .95.8h6.3a1 1 0 0 0 .95-.68L15 4H4" stroke="#111110" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="6.5" cy="13.5" r="1" fill="#111110"/>
<circle cx="12.5" cy="13.5" r="1" fill="#111110"/>
</svg>
<span class="text-[13px] font-medium text-ink hidden sm:inline">Cart</span>
<div id="cart-count" class="hidden">0</div>
</button>
</div>
</div>
</nav>
<!-- MAIN CONTENT -->
<main class="max-w-[1280px] mx-auto px-6 lg:px-10 py-12">
<!-- Header row -->
<div class="flex flex-col sm:flex-row sm:items-end justify-between gap-4 mb-8">
<div>
<p class="section-label mb-2">Our Products</p>
<h1 class="font-serif text-[2rem] text-ink leading-tight" style="letter-spacing:-0.02em;">
Curated Collection
</h1>
</div>
<p class="text-[14px] text-warm-muted" id="product-count">Showing 8 products</p>
</div>
<!-- Filter bar -->
<div class="flex gap-2 flex-wrap mb-8" id="filter-bar">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="design">Design</button>
<button class="filter-btn" data-filter="development">Development</button>
<button class="filter-btn" data-filter="strategy">Strategy</button>
</div>
<!-- Divider -->
<div class="h-px bg-warm-mid mb-8"></div>
<!-- Products grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-5" id="products-grid">
<!-- Rendered by JS -->
</div>
</main>
<!-- CART OVERLAY -->
<div id="cart-overlay"></div>
<!-- CART SIDEBAR -->
<div id="cart-sidebar">
<!-- Header -->
<div class="flex items-center justify-between px-6 py-5" style="border-bottom:1px solid #e8e7e4;">
<div>
<p class="section-label mb-1">Your Cart</p>
<h2 class="font-serif text-[1.3rem] text-ink leading-none" style="letter-spacing:-0.01em;">
Order Summary
</h2>
</div>
<button id="close-cart" class="w-8 h-8 flex items-center justify-center rounded-[4px] border border-warm-mid hover:border-warm-border hover:bg-warm-bg transition-colors cursor-pointer bg-white">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M1 1l10 10M11 1L1 11" stroke="#888580" stroke-width="1.5" stroke-linecap="round"/>
</svg>
</button>
</div>
<!-- Cart items -->
<div id="cart-items-scroll" class="flex-1 overflow-y-auto px-6">
<!-- Empty state -->
<div id="empty-cart">
<div class="w-12 h-12 rounded-[4px] border border-warm-mid flex items-center justify-center mb-2">
<svg width="22" height="22" viewBox="0 0 22 22" fill="none">
<path d="M2 2h2.5l3 9.5a1.2 1.2 0 0 0 1.15.9h7.5a1.2 1.2 0 0 0 1.15-.86L19.5 6H5.5" stroke="#888580" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="9" cy="19" r="1.2" fill="#888580"/>
<circle cx="16" cy="19" r="1.2" fill="#888580"/>
</svg>
</div>
<p class="text-[14px] font-medium text-ink">Your cart is empty</p>
<p class="text-[13px] text-warm-muted">Add products to get started</p>
<button id="empty-browse" class="btn-outline mt-2" style="font-size:13px; padding:8px 16px;">
Browse products
</button>
</div>
<!-- Items list (shown when not empty) -->
<div id="cart-items-list" class="hidden py-2">
<!-- Items rendered by JS -->
</div>
</div>
<!-- Footer: totals + checkout -->
<div id="cart-footer" class="hidden px-6 py-5" style="border-top:1px solid #e8e7e4;">
<!-- Subtotals -->
<div class="flex flex-col gap-2 mb-4">
<div class="flex justify-between text-[13px]">
<span class="text-warm-muted">Subtotal</span>
<span class="text-ink font-medium" id="subtotal-val">$0.00</span>
</div>
<div class="flex justify-between text-[13px]">
<span class="text-warm-muted">Shipping</span>
<span class="text-ink">Free</span>
</div>
<div class="h-px bg-warm-mid my-1"></div>
<div class="flex justify-between text-[14px]">
<span class="font-medium text-ink">Total</span>
<span class="font-serif text-[1.15rem] text-ink" id="total-val">$0.00</span>
</div>
</div>
<!-- Promo code -->
<div class="flex gap-2 mb-4">
<input
id="promo-input"
type="text"
placeholder="Promo code"
class="flex-1 h-[38px] bg-warm-bg border border-warm-mid rounded-[4px] text-[13px] text-ink px-3 placeholder:text-warm-muted font-sans outline-none"
style="transition: border-color 0.15s ease; font-family:'DM Sans',sans-serif;"
onfocus="this.style.borderColor='#2a2927'" onblur="this.style.borderColor=''"
/>
<button onclick="applyPromo()" class="btn-outline" style="padding:0 14px; height:38px; font-size:13px;">Apply</button>
</div>
<div id="promo-msg" class="text-[12px] text-warm-muted mb-3 hidden"></div>
<!-- Checkout -->
<button class="btn-primary w-full justify-center" style="padding:12px 20px; font-size:14px;" onclick="checkout()">
Checkout <span class="arrow">→</span>
</button>
<p class="text-[11px] text-warm-muted text-center mt-3" style="letter-spacing:0.02em;">
We respond within 24 hours
</p>
</div>
</div>
<!-- Toast -->
<div id="toast"></div>
<!-- Add your JS in app.js -->
<script src="app.js"></script>
</body>
</html>Component details
Overview, install command, dependencies, and project links.
A fully dynamic product cart page built with pure HTML, Tailwind CSS, and vanilla JavaScript — no frameworks, no dependencies. Features an 8-product grid with live category filtering, a slide-in cart sidebar with overlay, real-time quantity controls, line totals, promo code input, and an animated cart badge. Products are organized into Design, Development, and Strategy categories — switching filters updates the grid and product count instantly. The cart sidebar handles empty state, item addition and removal, quantity increment and decrement, and a checkout flow. Typography uses DM Serif Display for prices and headings, DM Sans for all body and UI text, on a warm #f5f4f1 background with #111110 ink, #888580 muted gray, and #e8e7e4 borders — no gradients, no colored accents, nothing decorative that doesn't earn its place.
Discussion
Feedback, implementation tips, and usage notes.
Comments
Sign in to join the conversation
Similar Components
More from the same category

