Shop — Multi-vendor Marketplace Landing
A dense, deal-driven landing page for a fictional multi-vendor marketplace, built entirely with CSS gradients, inline SVG and emoji instead of images. A search-forward hero with quick-search chips leads into a twelve-tile category mega-grid, a flash-deals rail with a live countdown, a scrollable top-sellers carousel, vendor spotlight cards and a trust strip. A working slide-in cart drawer tracks quantities, totals and favorites, all in vanilla JS.
MCP
Code
/* ============ TOKENS ============ */
:root {
--bg: #ffffff;
--bg-soft: #f5f6f9;
--ink: #16181d;
--muted: #6b7280;
--brand: #3457ff;
--brand-d: #2742d6;
--brand-ink: #ffffff;
--sale: #e0245e;
--ok: #1f9d55;
--amber: #f59e0b;
--line: rgba(16, 18, 29, .1);
--line-2: rgba(16, 18, 29, .16);
--shadow: 0 2px 8px rgba(16, 18, 29, .06), 0 12px 28px rgba(16, 18, 29, .08);
--shadow-sm: 0 1px 3px rgba(16, 18, 29, .08);
--r: 14px;
--r-sm: 10px;
--wrap: 1200px;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
h1, h2, h3, h4 { margin: 0; line-height: 1.18; letter-spacing: -.02em; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
img, svg { display: block; }
button { font: inherit; cursor: pointer; }
.wrap { width: min(100% - 32px, var(--wrap)); margin-inline: auto; }
.skip {
position: absolute; left: 8px; top: -48px; z-index: 200;
background: var(--ink); color: #fff; padding: 10px 16px; border-radius: 10px;
transition: top .18s;
}
.skip:focus { top: 8px; }
:focus-visible { outline: 3px solid var(--brand); outline-offset: 2px; border-radius: 6px; }
/* ============ UTILITY BAR ============ */
.utilbar { background: var(--ink); color: #e9eaf2; font-size: .8rem; }
.utilbar__row { display: flex; align-items: center; justify-content: space-between; min-height: 36px; gap: 12px; }
.utilbar__deal { font-weight: 600; }
.utilbar__links { display: flex; gap: 18px; }
.utilbar__links a { color: #c9cbe0; transition: color .15s; }
.utilbar__links a:hover { color: #fff; }
/* ============ MASTHEAD ============ */
.masthead { position: sticky; top: 0; z-index: 100; background: var(--bg); border-bottom: 1px solid var(--line); box-shadow: var(--shadow-sm); }
.masthead__row { display: flex; align-items: center; gap: 18px; padding: 12px 0; }
.brand { display: inline-flex; align-items: center; gap: 9px; color: var(--brand); flex: none; }
.brand__mark { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 11px; background: var(--brand); color: #fff; }
.brand__name { font-weight: 900; font-size: 1.3rem; letter-spacing: -.03em; color: var(--ink); }
.search { flex: 1; display: flex; align-items: stretch; max-width: 640px; border: 2px solid var(--ink); border-radius: 12px; overflow: hidden; background: #fff; }
.search:focus-within { box-shadow: 0 0 0 3px rgba(52, 87, 255, .25); border-color: var(--brand); }
.search__cat { border: none; background: var(--bg-soft); padding: 0 12px; font-weight: 600; font-size: .88rem; border-right: 1px solid var(--line); color: var(--ink); }
.search__cat:focus { outline: none; }
.search__input { flex: 1; border: none; padding: 11px 14px; font-size: .95rem; min-width: 0; }
.search__input:focus { outline: none; }
.search__btn { border: none; background: var(--ink); color: #fff; padding: 0 16px; display: grid; place-items: center; transition: background .15s; }
.search__btn:hover { background: var(--brand); }
.masthead__actions { display: flex; gap: 6px; flex: none; }
.iconbtn { display: inline-flex; flex-direction: column; align-items: center; gap: 2px; background: none; border: none; color: var(--ink); padding: 6px 10px; border-radius: 10px; font-size: .72rem; font-weight: 600; transition: background .15s; }
.iconbtn:hover { background: var(--bg-soft); }
.cartbtn { position: relative; }
.cartbtn__count { position: absolute; top: 0; right: 4px; min-width: 18px; height: 18px; padding: 0 4px; display: grid; place-items: center; background: var(--sale); color: #fff; border-radius: 9px; font-size: .68rem; font-weight: 800; }
.cartbtn__count.is-bump { animation: bump .35s ease; }
@keyframes bump { 40% { transform: scale(1.45); } }
/* ============ CAT NAV ============ */
.catnav { background: var(--ink); color: #fff; }
.catnav__row { display: flex; align-items: center; gap: 6px; overflow-x: auto; scrollbar-width: none; }
.catnav__row::-webkit-scrollbar { display: none; }
.catnav__all { display: inline-flex; align-items: center; gap: 8px; background: var(--brand); color: #fff; border: none; padding: 9px 14px; font-weight: 700; font-size: .85rem; flex: none; }
.catnav__all:hover { background: var(--brand-d); }
.catnav__bars { display: inline-grid; gap: 3px; }
.catnav__bars i { width: 16px; height: 2px; background: #fff; border-radius: 2px; }
.catnav__list { display: flex; gap: 2px; list-style: none; margin: 0; padding: 0; }
.catnav__list a { display: inline-block; padding: 9px 12px; font-size: .85rem; font-weight: 600; color: #d7d9ec; white-space: nowrap; transition: color .15s; }
.catnav__list a:hover { color: #fff; }
.catnav__hot { color: var(--amber) !important; }
/* ============ MEGA MENU ============ */
.mega { background: var(--bg); border-bottom: 1px solid var(--line); box-shadow: var(--shadow); }
.mega__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; padding: 22px 0; }
.mega__col h3 { font-size: .95rem; margin-bottom: 10px; color: var(--brand); }
.mega__col a { display: block; padding: 5px 0; font-size: .88rem; color: var(--muted); transition: color .15s; }
.mega__col a:hover { color: var(--ink); }
/* ============ HERO ============ */
.hero { background: linear-gradient(180deg, #eef1ff 0%, #f7f8fc 100%); padding: 36px 0 44px; }
.hero__grid { display: grid; grid-template-columns: 1.25fr 1fr; gap: 32px; align-items: center; }
.pill { display: inline-block; background: #fff; border: 1px solid var(--line); padding: 5px 12px; border-radius: 999px; font-size: .8rem; font-weight: 700; color: var(--brand-d); box-shadow: var(--shadow-sm); }
.hero__copy h1 { font-size: clamp(2rem, 4.4vw, 3.2rem); font-weight: 900; margin: 14px 0 12px; }
.hero__copy > p { color: var(--muted); font-size: 1.06rem; max-width: 46ch; }
.hero__search { display: flex; flex-wrap: wrap; gap: 8px; margin: 20px 0 16px; }
.chip { background: #fff; border: 1px solid var(--line-2); border-radius: 999px; padding: 7px 14px; font-size: .85rem; font-weight: 600; color: var(--ink); transition: transform .12s, border-color .15s, background .15s; }
.chip::before { content: "🔍 "; }
.chip:hover { border-color: var(--brand); color: var(--brand-d); transform: translateY(-2px); }
.hero__trust { display: flex; flex-wrap: wrap; gap: 16px; font-size: .85rem; font-weight: 600; color: var(--ink); }
.hero__promo { display: grid; gap: 16px; }
.promo { position: relative; background: #fff; border-radius: var(--r); padding: 18px; box-shadow: var(--shadow); display: flex; align-items: center; gap: 16px; overflow: hidden; }
.promo__tag { position: absolute; top: 12px; left: 12px; background: var(--sale); color: #fff; font-size: .7rem; font-weight: 800; padding: 3px 9px; border-radius: 999px; text-transform: uppercase; letter-spacing: .04em; }
.promo__tag--alt { background: var(--ok); }
.promo__art { width: 96px; height: 96px; border-radius: 14px; flex: none; }
.art-buds { background: radial-gradient(circle at 30% 30%, #8aa2ff, #3457ff 70%); position: relative; }
.art-buds::after { content: ""; position: absolute; inset: 28px; border-radius: 50%; background: #fff; box-shadow: inset 0 -6px 0 rgba(0,0,0,.12); }
.art-lamp { background: linear-gradient(135deg, #ffe39a, #ffb347); position: relative; }
.art-lamp::after { content: ""; position: absolute; left: 50%; bottom: 16px; width: 8px; height: 46px; background: #6b4a1f; transform: translateX(-50%); border-radius: 4px; }
.promo__txt strong { display: block; font-size: 1.05rem; }
.promo__price { color: var(--sale); font-weight: 800; font-size: 1.1rem; }
.promo__price s { color: var(--muted); font-weight: 500; font-size: .85rem; margin-left: 4px; }
.promo--b .promo__price { color: var(--ink); }
/* ============ SECTIONS ============ */
.section { padding: 38px 0; }
.section--deals { background: #fff5f8; }
.section__head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.section__head h2 { font-size: clamp(1.3rem, 2.6vw, 1.7rem); font-weight: 800; }
.seeall { color: var(--brand-d); font-weight: 700; font-size: .9rem; }
.seeall:hover { text-decoration: underline; }
/* ============ CATEGORY GRID ============ */
.catgrid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; }
.cat { background: #fff; border: 1px solid var(--line); border-radius: var(--r); padding: 16px 12px; text-align: center; transition: transform .14s, box-shadow .14s, border-color .14s; cursor: pointer; }
.cat:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: var(--brand); }
.cat__tile { width: 100%; aspect-ratio: 1; border-radius: 12px; display: grid; place-items: center; font-size: 2rem; margin-bottom: 10px; }
.cat__name { font-weight: 700; font-size: .92rem; }
.cat__count { color: var(--muted); font-size: .78rem; }
/* ============ RAILS / PRODUCT CARDS ============ */
.rail { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(218px, 1fr); gap: 16px; overflow-x: auto; padding: 6px 4px 16px; scroll-snap-type: x mandatory; scrollbar-width: thin; }
.section--deals .rail { grid-auto-columns: minmax(218px, 240px); }
.rail::-webkit-scrollbar { height: 8px; }
.rail::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 8px; }
.card { background: #fff; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; display: flex; flex-direction: column; scroll-snap-align: start; transition: transform .14s, box-shadow .14s; }
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.card__media { position: relative; aspect-ratio: 4 / 3; display: grid; place-items: center; font-size: 3rem; }
.card__badge { position: absolute; top: 10px; left: 10px; background: var(--sale); color: #fff; font-size: .7rem; font-weight: 800; padding: 3px 8px; border-radius: 999px; text-transform: uppercase; letter-spacing: .03em; }
.card__fav { position: absolute; top: 8px; right: 8px; width: 32px; height: 32px; border-radius: 50%; border: none; background: rgba(255,255,255,.9); display: grid; place-items: center; font-size: 1rem; line-height: 1; transition: transform .12s, background .15s; }
.card__fav:hover { transform: scale(1.12); }
.card__fav.is-on { background: var(--sale); }
.card__body { padding: 12px 13px 14px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.card__vendor { font-size: .74rem; font-weight: 700; color: var(--brand-d); text-transform: uppercase; letter-spacing: .03em; }
.card__title { font-size: .94rem; font-weight: 600; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 2.6em; }
.card__rate { display: flex; align-items: center; gap: 6px; font-size: .78rem; color: var(--muted); }
.card__stars { color: var(--amber); letter-spacing: 1px; }
.card__priceline { display: flex; align-items: baseline; gap: 8px; margin-top: 2px; }
.card__price { font-size: 1.18rem; font-weight: 800; }
.card__was { color: var(--muted); text-decoration: line-through; font-size: .85rem; }
.card__off { color: var(--sale); font-weight: 800; font-size: .78rem; }
.card__ship { font-size: .76rem; color: var(--ok); font-weight: 600; }
.card__add { margin-top: 8px; background: var(--brand); color: #fff; border: none; border-radius: var(--r-sm); padding: 9px; font-weight: 700; font-size: .88rem; transition: background .15s, transform .1s; }
.card__add:hover { background: var(--brand-d); }
.card__add:active { transform: scale(.97); }
.card__add.is-added { background: var(--ok); }
/* ============ COUNTDOWN ============ */
.countdown { display: inline-flex; align-items: center; gap: 6px; font-weight: 800; }
.countdown__lbl { color: var(--muted); font-size: .85rem; font-weight: 700; margin-right: 4px; }
.countdown__box { background: var(--ink); color: #fff; min-width: 38px; text-align: center; padding: 6px 4px; border-radius: 8px; font-variant-numeric: tabular-nums; }
.countdown i { color: var(--ink); font-style: normal; }
/* ============ RAIL NAV ============ */
.rail__nav { display: flex; gap: 8px; }
.railbtn { width: 38px; height: 38px; border-radius: 50%; border: 1px solid var(--line-2); background: #fff; font-size: 1.3rem; line-height: 1; color: var(--ink); transition: background .15s, border-color .15s; }
.railbtn:hover { background: var(--brand); color: #fff; border-color: var(--brand); }
/* ============ VENDORS ============ */
.vendors { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
.vendor { background: #fff; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; transition: transform .14s, box-shadow .14s; }
.vendor:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.vendor__cover { height: 70px; }
.vendor__body { padding: 0 16px 16px; margin-top: -26px; }
.vendor__avatar { width: 52px; height: 52px; border-radius: 14px; border: 3px solid #fff; display: grid; place-items: center; font-size: 1.5rem; box-shadow: var(--shadow-sm); }
.vendor__name { font-weight: 800; margin-top: 8px; }
.vendor__meta { font-size: .8rem; color: var(--muted); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.vendor__star { color: var(--amber); font-weight: 700; }
.vendor__follow { margin-top: 12px; width: 100%; background: var(--bg-soft); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 8px; font-weight: 700; font-size: .85rem; transition: background .15s, color .15s; }
.vendor__follow:hover { background: var(--ink); color: #fff; }
.vendor__follow.is-on { background: var(--brand); color: #fff; border-color: var(--brand); }
/* ============ TRUST STRIP ============ */
.trust { background: var(--bg-soft); border-block: 1px solid var(--line); padding: 28px 0; }
.trust__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.trust__item { display: flex; gap: 12px; align-items: flex-start; }
.trust__ic { font-size: 1.7rem; flex: none; }
.trust__item strong { display: block; font-size: .98rem; }
.trust__item p { color: var(--muted); font-size: .85rem; }
/* ============ SELL CTA ============ */
.sell { background: linear-gradient(120deg, var(--brand), var(--brand-d)); color: #fff; padding: 40px 0; }
.sell__inner { display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
.sell h2 { font-size: clamp(1.4rem, 3vw, 2rem); font-weight: 900; }
.sell p { color: rgba(255,255,255,.88); margin-top: 6px; max-width: 50ch; }
.btn { border: none; border-radius: 12px; padding: 13px 24px; font-weight: 800; font-size: .98rem; transition: transform .1s, box-shadow .15s, background .15s; }
.btn:active { transform: scale(.97); }
.btn--light { background: #fff; color: var(--brand-d); }
.btn--light:hover { box-shadow: 0 8px 22px rgba(0,0,0,.25); }
.btn--brand { background: var(--brand); color: #fff; }
.btn--brand:hover { background: var(--brand-d); }
/* ============ FOOTER ============ */
.footer { background: var(--ink); color: #c9cbe0; padding: 40px 0 24px; }
.footer__grid { display: grid; grid-template-columns: 1.4fr repeat(3, 1fr); gap: 28px; }
.footer .brand__name { color: #fff; }
.footer__tag { font-size: .85rem; margin-top: 8px; max-width: 28ch; }
.footer h4 { color: #fff; font-size: .92rem; margin-bottom: 10px; }
.footer__grid a { display: block; padding: 4px 0; font-size: .87rem; color: #b6b9d4; transition: color .15s; }
.footer__grid a:hover { color: #fff; }
.footer__bar { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-top: 26px; padding-top: 18px; border-top: 1px solid rgba(255,255,255,.12); font-size: .8rem; }
/* ============ CART DRAWER ============ */
.drawer { position: fixed; inset: 0; z-index: 150; }
.drawer__scrim { position: absolute; inset: 0; background: rgba(16,18,29,.5); animation: fade .2s ease; }
.drawer__panel { position: absolute; top: 0; right: 0; bottom: 0; width: min(420px, 92vw); background: #fff; display: flex; flex-direction: column; box-shadow: -10px 0 40px rgba(0,0,0,.2); animation: slidein .26s cubic-bezier(.2,.8,.2,1); }
@keyframes fade { from { opacity: 0; } }
@keyframes slidein { from { transform: translateX(100%); } }
.drawer__head { display: flex; align-items: center; justify-content: space-between; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.drawer__head h3 { font-size: 1.2rem; }
.drawer__x { background: var(--bg-soft); border: none; width: 34px; height: 34px; border-radius: 50%; font-size: 1rem; }
.drawer__x:hover { background: var(--line); }
.drawer__body { flex: 1; overflow-y: auto; padding: 12px 20px; display: flex; flex-direction: column; gap: 12px; }
.drawer__empty { color: var(--muted); text-align: center; padding: 50px 10px; }
.citem { display: flex; gap: 12px; align-items: center; }
.citem__media { width: 56px; height: 56px; border-radius: 10px; flex: none; display: grid; place-items: center; font-size: 1.6rem; }
.citem__info { flex: 1; min-width: 0; }
.citem__title { font-size: .88rem; font-weight: 600; line-height: 1.3; }
.citem__vendor { font-size: .73rem; color: var(--muted); }
.citem__price { font-weight: 800; }
.qty { display: inline-flex; align-items: center; border: 1px solid var(--line-2); border-radius: 8px; overflow: hidden; margin-top: 4px; }
.qty button { width: 26px; height: 26px; border: none; background: var(--bg-soft); font-weight: 800; }
.qty button:hover { background: var(--line); }
.qty span { min-width: 28px; text-align: center; font-weight: 700; font-size: .85rem; }
.citem__rm { background: none; border: none; color: var(--muted); font-size: .78rem; text-decoration: underline; padding: 2px 0; }
.citem__rm:hover { color: var(--sale); }
.drawer__foot { border-top: 1px solid var(--line); padding: 16px 20px; }
.drawer__total { display: flex; justify-content: space-between; font-size: 1.05rem; margin-bottom: 12px; }
.drawer__total strong { font-size: 1.25rem; }
.drawer__checkout { width: 100%; }
/* ============ TOAST ============ */
.toast { position: fixed; left: 50%; bottom: 24px; transform: translate(-50%, 24px); background: var(--ink); color: #fff; padding: 12px 20px; border-radius: 12px; font-weight: 600; font-size: .9rem; box-shadow: var(--shadow); opacity: 0; pointer-events: none; transition: transform .28s cubic-bezier(.2,.8,.2,1), opacity .28s; z-index: 200; max-width: 90vw; }
.toast.is-on { opacity: 1; transform: translate(-50%, 0); }
/* ============ RESPONSIVE ============ */
@media (max-width: 1080px) {
.catgrid { grid-template-columns: repeat(4, 1fr); }
.vendors { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
.masthead__row { flex-wrap: wrap; }
.search { order: 3; flex-basis: 100%; max-width: none; }
.mega__grid { grid-template-columns: repeat(2, 1fr); }
.trust__grid { grid-template-columns: repeat(2, 1fr); }
.footer__grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 760px) {
.hero__grid { grid-template-columns: 1fr; }
.hero__promo { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 620px) {
.utilbar__links { display: none; }
.catgrid { grid-template-columns: repeat(2, 1fr); }
.hero__promo { grid-template-columns: 1fr; }
.iconbtn span { display: none; }
.iconbtn { padding: 8px; }
.cartbtn__count { right: -2px; top: -2px; }
.footer__grid { grid-template-columns: 1fr; gap: 18px; }
}
@media (max-width: 400px) {
.countdown__lbl { display: none; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}"use strict";
/* ---------- helpers ---------- */
const $ = (s, r = document) => r.querySelector(s);
const $$ = (s, r = document) => [...r.querySelectorAll(s)];
const money = (n) => "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const stars = (r) => "★★★★★".slice(0, Math.round(r)) + "☆☆☆☆☆".slice(0, 5 - Math.round(r));
let toastTimer;
function toast(msg) {
const t = $("#toast");
t.textContent = msg;
t.classList.add("is-on");
clearTimeout(toastTimer);
toastTimer = setTimeout(() => t.classList.remove("is-on"), 2200);
}
/* ---------- data ---------- */
const TINTS = ["#eef1ff", "#fff1f5", "#eafaf0", "#fff7e6", "#f3eeff", "#e8f7ff"];
const CATEGORIES = [
{ name: "Electronics", icon: "🎧", count: "82k items", tint: "#eef1ff" },
{ name: "Home & Living", icon: "🛋️", count: "61k items", tint: "#eafaf0" },
{ name: "Fashion", icon: "👟", count: "94k items", tint: "#fff1f5" },
{ name: "Handmade", icon: "🕯️", count: "38k items", tint: "#fff7e6" },
{ name: "Beauty", icon: "💄", count: "27k items", tint: "#f3eeff" },
{ name: "Toys & Games", icon: "🎲", count: "19k items", tint: "#e8f7ff" },
{ name: "Garden", icon: "🪴", count: "22k items", tint: "#eafaf0" },
{ name: "Sports", icon: "🏀", count: "31k items", tint: "#eef1ff" },
{ name: "Kitchen", icon: "🍳", count: "44k items", tint: "#fff7e6" },
{ name: "Pets", icon: "🐾", count: "12k items", tint: "#fff1f5" },
{ name: "Books", icon: "📚", count: "70k items", tint: "#f3eeff" },
{ name: "Stationery", icon: "✏️", count: "9k items", tint: "#e8f7ff" },
];
const DEALS = [
{ id: "d1", icon: "🎧", vendor: "Aurora Audio", title: "Aurora Buds Pro — noise cancelling earbuds", price: 59, was: 129, rate: 4.8, reviews: 2140, badge: "-54%" },
{ id: "d2", icon: "⌚", vendor: "Tempo", title: "Tempo Fit smartwatch with GPS", price: 79, was: 159, rate: 4.6, reviews: 980, badge: "-50%" },
{ id: "d3", icon: "💡", vendor: "Halo Home", title: "Halo LED desk lamp, dimmable", price: 28, was: 49, rate: 4.7, reviews: 612, badge: "-43%" },
{ id: "d4", icon: "🔊", vendor: "BassBox", title: "BassBox mini bluetooth speaker", price: 34, was: 69, rate: 4.5, reviews: 1455, badge: "-51%" },
{ id: "d5", icon: "🧴", vendor: "GlowLab", title: "GlowLab vitamin C serum bundle", price: 22, was: 40, rate: 4.9, reviews: 3320, badge: "-45%" },
{ id: "d6", icon: "🪑", vendor: "NestWork", title: "NestWork ergonomic seat cushion", price: 31, was: 55, rate: 4.4, reviews: 410, badge: "-44%" },
];
const TOP = [
{ id: "t1", icon: "👟", vendor: "StridelLab", title: "Cloudstep running sneakers", price: 89, rate: 4.7, reviews: 5210, ship: "Free shipping" },
{ id: "t2", icon: "☕", vendor: "Potterly", title: "Hand-thrown ceramic mug, set of 2", price: 32, rate: 4.9, reviews: 1880, ship: "Free shipping" },
{ id: "t3", icon: "🎒", vendor: "Roamwell", title: "Everyday 22L commuter backpack", price: 64, rate: 4.6, reviews: 940, ship: "Free shipping" },
{ id: "t4", icon: "🕯️", vendor: "Emberly", title: "Soy candle trio — citrus & cedar", price: 27, rate: 4.8, reviews: 2010, ship: "Ships in 2 days" },
{ id: "t5", icon: "⌨️", vendor: "KeyForge", title: "Mechanical 65% keyboard, hot-swap", price: 119, rate: 4.7, reviews: 760, ship: "Free shipping" },
{ id: "t6", icon: "🪴", vendor: "Sprout & Co", title: "Self-watering planter, medium", price: 24, rate: 4.5, reviews: 530, ship: "Ships in 3 days" },
{ id: "t7", icon: "🧦", vendor: "Cozyfeet", title: "Merino wool crew socks, 3-pack", price: 19, rate: 4.8, reviews: 3120, ship: "Free shipping" },
];
const VENDORS = [
{ name: "Potterly", icon: "🏺", cover: "linear-gradient(120deg,#f59e0b,#e0245e)", avatar: "#fff7e6", rate: 4.9, sales: "12.4k sales" },
{ name: "Aurora Audio", icon: "🎧", cover: "linear-gradient(120deg,#3457ff,#7d5cff)", avatar: "#eef1ff", rate: 4.8, sales: "48k sales" },
{ name: "Emberly", icon: "🕯️", cover: "linear-gradient(120deg,#e0245e,#f59e0b)", avatar: "#fff1f5", rate: 4.9, sales: "9.1k sales" },
{ name: "Sprout & Co", icon: "🪴", cover: "linear-gradient(120deg,#1f9d55,#3457ff)", avatar: "#eafaf0", rate: 4.7, sales: "6.8k sales" },
];
/* ---------- product card factory ---------- */
function productCard(p, i) {
const tint = TINTS[i % TINTS.length];
const el = document.createElement("article");
el.className = "card";
const off = p.was ? `<span class="card__off">${Math.round((1 - p.price / p.was) * 100)}% off</span>` : "";
const was = p.was ? `<span class="card__was">${money(p.was)}</span>` : "";
const badge = p.badge ? `<span class="card__badge">${p.badge}</span>` : "";
const ship = p.ship ? `<span class="card__ship">🚚 ${p.ship}</span>` : `<span class="card__ship">🚚 Free shipping</span>`;
el.innerHTML = `
<div class="card__media" style="background:${tint}">
${badge}
<button class="card__fav" type="button" aria-pressed="false" aria-label="Save to favorites">🤍</button>
<span aria-hidden="true">${p.icon}</span>
</div>
<div class="card__body">
<span class="card__vendor">${p.vendor}</span>
<h3 class="card__title">${p.title}</h3>
<div class="card__rate"><span class="card__stars" aria-hidden="true">${stars(p.rate)}</span>${p.rate} (${p.reviews.toLocaleString("en-US")})</div>
<div class="card__priceline"><span class="card__price">${money(p.price)}</span>${was}${off}</div>
${ship}
<button class="card__add" type="button">Add to cart</button>
</div>`;
const fav = $(".card__fav", el);
fav.addEventListener("click", () => {
const on = fav.classList.toggle("is-on");
fav.setAttribute("aria-pressed", String(on));
fav.textContent = on ? "❤️" : "🤍";
toast(on ? `Saved ${p.vendor} item to favorites` : "Removed from favorites");
});
const add = $(".card__add", el);
add.addEventListener("click", () => {
addToCart(p);
add.textContent = "✓ Added";
add.classList.add("is-added");
setTimeout(() => { add.textContent = "Add to cart"; add.classList.remove("is-added"); }, 1100);
});
return el;
}
/* ---------- render ---------- */
function render() {
const cg = $("#catGrid");
CATEGORIES.forEach((c) => {
const el = document.createElement("button");
el.className = "cat";
el.type = "button";
el.innerHTML = `<div class="cat__tile" style="background:${c.tint}">${c.icon}</div><div class="cat__name">${c.name}</div><div class="cat__count">${c.count}</div>`;
el.addEventListener("click", () => toast(`Browsing ${c.name}`));
cg.appendChild(el);
});
const dr = $("#dealsRail");
DEALS.forEach((p, i) => dr.appendChild(productCard(p, i)));
const tr = $("#topRail");
TOP.forEach((p, i) => tr.appendChild(productCard(p, i)));
const vg = $("#vendorGrid");
VENDORS.forEach((v) => {
const el = document.createElement("article");
el.className = "vendor";
el.innerHTML = `
<div class="vendor__cover" style="background:${v.cover}"></div>
<div class="vendor__body">
<div class="vendor__avatar" style="background:${v.avatar}">${v.icon}</div>
<div class="vendor__name">${v.name}</div>
<div class="vendor__meta"><span class="vendor__star">★ ${v.rate}</span> · ${v.sales}</div>
<button class="vendor__follow" type="button" aria-pressed="false">+ Follow</button>
</div>`;
const f = $(".vendor__follow", el);
f.addEventListener("click", () => {
const on = f.classList.toggle("is-on");
f.setAttribute("aria-pressed", String(on));
f.textContent = on ? "✓ Following" : "+ Follow";
toast(on ? `Following ${v.name}` : `Unfollowed ${v.name}`);
});
vg.appendChild(el);
});
}
/* ---------- cart state ---------- */
const cart = new Map();
function addToCart(p) {
const row = cart.get(p.id);
if (row) row.qty++;
else cart.set(p.id, { ...p, qty: 1 });
paintCart();
bumpCount();
toast(`Added “${p.title.slice(0, 28)}…” to cart`);
}
function bumpCount() {
const c = $("#cartCount");
c.classList.remove("is-bump");
void c.offsetWidth;
c.classList.add("is-bump");
}
function paintCart() {
let count = 0, total = 0;
cart.forEach((r) => { count += r.qty; total += r.qty * r.price; });
$("#cartCount").textContent = count;
$("#cartTotal").textContent = money(total);
const body = $("#cartItems");
body.innerHTML = "";
if (cart.size === 0) {
body.innerHTML = `<p class="drawer__empty">🛒 Your cart is empty.<br>Add some deals to get started!</p>`;
return;
}
let i = 0;
cart.forEach((r) => {
const tint = TINTS[i++ % TINTS.length];
const el = document.createElement("div");
el.className = "citem";
el.innerHTML = `
<div class="citem__media" style="background:${tint}">${r.icon}</div>
<div class="citem__info">
<div class="citem__title">${r.title}</div>
<div class="citem__vendor">${r.vendor}</div>
<div class="qty" role="group" aria-label="Quantity">
<button type="button" data-act="dec" aria-label="Decrease">−</button>
<span>${r.qty}</span>
<button type="button" data-act="inc" aria-label="Increase">+</button>
</div>
<button class="citem__rm" type="button">Remove</button>
</div>
<div class="citem__price">${money(r.price * r.qty)}</div>`;
$('[data-act="inc"]', el).addEventListener("click", () => { r.qty++; paintCart(); bumpCount(); });
$('[data-act="dec"]', el).addEventListener("click", () => { r.qty--; if (r.qty < 1) cart.delete(r.id); paintCart(); bumpCount(); });
$(".citem__rm", el).addEventListener("click", () => { cart.delete(r.id); paintCart(); bumpCount(); toast("Removed from cart"); });
body.appendChild(el);
});
}
/* ---------- cart drawer ---------- */
const drawer = $("#cartDrawer");
let lastFocus = null;
function openCart() {
lastFocus = document.activeElement;
drawer.hidden = false;
document.body.style.overflow = "hidden";
$(".drawer__x", drawer).focus();
}
function closeCart() {
drawer.hidden = true;
document.body.style.overflow = "";
if (lastFocus) lastFocus.focus();
}
$("#cartBtn").addEventListener("click", openCart);
$$("[data-close]", drawer).forEach((b) => b.addEventListener("click", closeCart));
$("#checkoutBtn").addEventListener("click", () => {
if (cart.size === 0) { toast("Your cart is empty"); return; }
toast("🔒 Checkout is disabled in this demo");
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
if (!drawer.hidden) closeCart();
if (!$("#megaMenu").hidden) closeMega();
}
});
/* ---------- mega menu ---------- */
const mega = $("#megaMenu");
const megaToggle = $("#megaToggle");
function closeMega() { mega.hidden = true; megaToggle.setAttribute("aria-expanded", "false"); }
megaToggle.addEventListener("click", () => {
const open = mega.hidden;
mega.hidden = !open;
megaToggle.setAttribute("aria-expanded", String(open));
});
document.addEventListener("click", (e) => {
if (!mega.hidden && !mega.contains(e.target) && e.target !== megaToggle && !megaToggle.contains(e.target)) closeMega();
});
/* ---------- search ---------- */
$("#searchForm").addEventListener("submit", (e) => {
e.preventDefault();
const q = $("#searchInput").value.trim();
toast(q ? `Searching for “${q}”…` : "Type something to search");
});
$$(".chip").forEach((c) => c.addEventListener("click", () => {
$("#searchInput").value = c.dataset.q;
$("#searchInput").focus();
toast(`Searching for “${c.dataset.q}”…`);
}));
/* ---------- rail nav buttons ---------- */
$$(".railbtn").forEach((b) => b.addEventListener("click", () => {
const rail = $("#" + b.dataset.rail);
rail.scrollBy({ left: Number(b.dataset.dir) * 320, behavior: "smooth" });
}));
/* ---------- misc buttons ---------- */
$("#accountBtn").addEventListener("click", () => toast("Sign in is disabled in this demo"));
$("#sellBtn").addEventListener("click", () => toast("✨ Vendor onboarding is disabled in this demo"));
/* ---------- countdown ---------- */
function startCountdown() {
const end = Date.now() + (3 * 3600 + 47 * 60 + 12) * 1000; // ~3h47m
const h = $("#cdH"), m = $("#cdM"), s = $("#cdS");
const pad = (n) => String(n).padStart(2, "0");
function tick() {
let left = Math.max(0, Math.floor((end - Date.now()) / 1000));
h.textContent = pad(Math.floor(left / 3600));
m.textContent = pad(Math.floor((left % 3600) / 60));
s.textContent = pad(left % 60);
if (left > 0) setTimeout(tick, 1000);
else toast("⚡ Flash sale ended!");
}
tick();
}
/* ---------- init ---------- */
render();
paintCart();
startCountdown();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Bazaaro — Multi-vendor Marketplace</title>
<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=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip" href="#main">Skip to content</a>
<!-- Utility bar -->
<div class="utilbar">
<div class="wrap utilbar__row">
<span class="utilbar__deal">🚚 Free shipping over $35 · ⚡ Same-day dispatch</span>
<nav class="utilbar__links" aria-label="Utility">
<a href="#sell">Sell on Bazaaro</a>
<a href="#help">Help</a>
<a href="#track">Track order</a>
</nav>
</div>
</div>
<!-- Header -->
<header class="masthead">
<div class="wrap masthead__row">
<a class="brand" href="#main" aria-label="Bazaaro home">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 32 32" width="28" height="28"><path d="M6 9h20l-2 13a3 3 0 0 1-3 2.6H11A3 3 0 0 1 8 22L6 9Z" fill="currentColor"/><path d="M11 9a5 5 0 0 1 10 0" fill="none" stroke="#fff" stroke-width="2.2" stroke-linecap="round"/></svg>
</span>
<span class="brand__name">Bazaaro</span>
</a>
<form class="search" role="search" id="searchForm">
<select class="search__cat" aria-label="Search category">
<option>All</option>
<option>Tech</option>
<option>Home</option>
<option>Fashion</option>
<option>Handmade</option>
</select>
<input class="search__input" id="searchInput" type="search" placeholder="Search 4 million products…" aria-label="Search products" autocomplete="off" />
<button class="search__btn" type="submit" aria-label="Search">
<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"><circle cx="11" cy="11" r="7" fill="none" stroke="currentColor" stroke-width="2.2"/><path d="m20 20-4-4" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
</button>
</form>
<div class="masthead__actions">
<button class="iconbtn" id="accountBtn" type="button">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><circle cx="12" cy="8" r="4" fill="none" stroke="currentColor" stroke-width="2"/><path d="M4 20a8 8 0 0 1 16 0" fill="none" stroke="currentColor" stroke-width="2"/></svg>
<span>Account</span>
</button>
<button class="iconbtn cartbtn" id="cartBtn" type="button" aria-label="Open cart">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M5 7h15l-1.5 9.5a2 2 0 0 1-2 1.7H9a2 2 0 0 1-2-1.7L5 4H2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><circle cx="9.5" cy="20.5" r="1.4" fill="currentColor"/><circle cx="17" cy="20.5" r="1.4" fill="currentColor"/></svg>
<span>Cart</span>
<span class="cartbtn__count" id="cartCount" aria-live="polite">0</span>
</button>
</div>
</div>
<!-- Category nav -->
<nav class="catnav" aria-label="Departments">
<div class="wrap catnav__row">
<button class="catnav__all" id="megaToggle" aria-expanded="false" aria-controls="megaMenu">
<span class="catnav__bars" aria-hidden="true"><i></i><i></i><i></i></span> All departments
</button>
<ul class="catnav__list">
<li><a href="#deals" class="catnav__hot">⚡ Today's Deals</a></li>
<li><a href="#categories">Electronics</a></li>
<li><a href="#categories">Home & Living</a></li>
<li><a href="#categories">Fashion</a></li>
<li><a href="#categories">Handmade</a></li>
<li><a href="#vendors">Vendors</a></li>
<li><a href="#sell">Sell</a></li>
</ul>
</div>
</nav>
</header>
<!-- Mega menu (collapsible) -->
<div class="mega" id="megaMenu" hidden>
<div class="wrap mega__grid">
<div class="mega__col"><h3>Electronics</h3><a href="#categories">Headphones</a><a href="#categories">Smart Home</a><a href="#categories">Wearables</a><a href="#categories">Cameras</a></div>
<div class="mega__col"><h3>Home & Living</h3><a href="#categories">Kitchen</a><a href="#categories">Decor</a><a href="#categories">Lighting</a><a href="#categories">Storage</a></div>
<div class="mega__col"><h3>Fashion</h3><a href="#categories">Sneakers</a><a href="#categories">Bags</a><a href="#categories">Watches</a><a href="#categories">Jewelry</a></div>
<div class="mega__col"><h3>Handmade</h3><a href="#categories">Ceramics</a><a href="#categories">Candles</a><a href="#categories">Prints</a><a href="#categories">Textiles</a></div>
</div>
</div>
<main id="main">
<!-- Hero -->
<section class="hero">
<div class="wrap hero__grid">
<div class="hero__copy">
<span class="pill">🛍️ 12,400+ independent vendors</span>
<h1>Everything you want, from sellers you'll love.</h1>
<p>One cart, thousands of shops. Discover daily deals, handmade originals, and big-brand tech — all with buyer protection.</p>
<div class="hero__search" aria-label="Popular searches">
<button class="chip" type="button" data-q="wireless earbuds">wireless earbuds</button>
<button class="chip" type="button" data-q="ceramic mug">ceramic mug</button>
<button class="chip" type="button" data-q="desk lamp">desk lamp</button>
<button class="chip" type="button" data-q="sneakers">sneakers</button>
</div>
<div class="hero__trust">
<span>🔒 Secure checkout</span><span>↩️ 30-day returns</span><span>⭐ 4.8 avg rating</span>
</div>
</div>
<aside class="hero__promo" aria-label="Featured deal">
<div class="promo promo--a">
<span class="promo__tag">Flash</span>
<div class="promo__art art-buds" aria-hidden="true"></div>
<div class="promo__txt"><strong>Aurora Buds Pro</strong><span class="promo__price">$59 <s>$129</s></span></div>
</div>
<div class="promo promo--b">
<span class="promo__tag promo__tag--alt">New</span>
<div class="promo__art art-lamp" aria-hidden="true"></div>
<div class="promo__txt"><strong>Halo Desk Lamp</strong><span class="promo__price">$42</span></div>
</div>
</aside>
</div>
</section>
<!-- Category mega-grid -->
<section class="section" id="categories">
<div class="wrap">
<div class="section__head"><h2>Shop by category</h2><a class="seeall" href="#deals">See today's deals →</a></div>
<div class="catgrid" id="catGrid"></div>
</div>
</section>
<!-- Flash deals with countdown -->
<section class="section section--deals" id="deals">
<div class="wrap">
<div class="section__head">
<h2>⚡ Flash deals</h2>
<div class="countdown" id="countdown" aria-label="Time left in flash sale">
<span class="countdown__lbl">Ends in</span>
<span class="countdown__box" id="cdH">00</span><i>:</i><span class="countdown__box" id="cdM">00</span><i>:</i><span class="countdown__box" id="cdS">00</span>
</div>
</div>
<div class="rail" id="dealsRail"></div>
</div>
</section>
<!-- Top sellers rail -->
<section class="section" id="topsellers">
<div class="wrap">
<div class="section__head"><h2>Top sellers this week</h2>
<div class="rail__nav"><button class="railbtn" data-rail="topRail" data-dir="-1" aria-label="Scroll left">‹</button><button class="railbtn" data-rail="topRail" data-dir="1" aria-label="Scroll right">›</button></div>
</div>
<div class="rail" id="topRail"></div>
</div>
</section>
<!-- Vendor spotlights -->
<section class="section" id="vendors">
<div class="wrap">
<div class="section__head"><h2>Vendor spotlights</h2><a class="seeall" href="#sell">Browse all vendors →</a></div>
<div class="vendors" id="vendorGrid"></div>
</div>
</section>
<!-- Trust / returns strip -->
<section class="trust" id="help">
<div class="wrap trust__grid">
<div class="trust__item"><span class="trust__ic">🔒</span><div><strong>Buyer protection</strong><p>Full refund if it's not as described.</p></div></div>
<div class="trust__item"><span class="trust__ic">↩️</span><div><strong>Easy 30-day returns</strong><p>Free prepaid label on most orders.</p></div></div>
<div class="trust__item"><span class="trust__ic">🚚</span><div><strong>Fast tracked shipping</strong><p>Real-time updates to your door.</p></div></div>
<div class="trust__item"><span class="trust__ic">💬</span><div><strong>24/7 support</strong><p>Talk to a human, any time.</p></div></div>
</div>
</section>
<!-- Sell CTA -->
<section class="sell" id="sell">
<div class="wrap sell__inner">
<div>
<h2>Start selling on Bazaaro</h2>
<p>Reach millions of buyers, zero listing fees for 90 days. Join 12,400+ vendors.</p>
</div>
<button class="btn btn--light" id="sellBtn" type="button">Open your shop</button>
</div>
</section>
</main>
<footer class="footer" id="track">
<div class="wrap footer__grid">
<div><span class="brand__name">Bazaaro</span><p class="footer__tag">A fictional multi-vendor marketplace demo.</p></div>
<div><h4>Shop</h4><a href="#deals">Deals</a><a href="#categories">Categories</a><a href="#topsellers">Top sellers</a></div>
<div><h4>Sell</h4><a href="#sell">Open a shop</a><a href="#vendors">Vendor stories</a><a href="#help">Seller help</a></div>
<div><h4>Support</h4><a href="#help">Returns</a><a href="#track">Track order</a><a href="#help">Contact</a></div>
</div>
<div class="wrap footer__bar"><span>© 2026 Bazaaro · Illustrative demo, no real checkout.</span><span>🔒 Secure · 🌍 Ships worldwide</span></div>
</footer>
<!-- Cart drawer -->
<div class="drawer" id="cartDrawer" hidden>
<div class="drawer__scrim" data-close></div>
<aside class="drawer__panel" role="dialog" aria-modal="true" aria-labelledby="cartTitle">
<header class="drawer__head"><h3 id="cartTitle">Your cart</h3><button class="drawer__x" data-close aria-label="Close cart">✕</button></header>
<div class="drawer__body" id="cartItems"></div>
<footer class="drawer__foot">
<div class="drawer__total"><span>Subtotal</span><strong id="cartTotal">$0.00</strong></div>
<button class="btn btn--brand drawer__checkout" id="checkoutBtn" type="button">Secure checkout 🔒</button>
</footer>
</aside>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Multi-vendor Marketplace Landing
A busy, Amazon-meets-Etsy storefront for Bazaaro, a fictional marketplace of 12,400+ independent vendors. The layout stacks many energetic modules — a utility deal bar, a sticky masthead with a category-scoped search box, a dark department nav with a collapsible mega menu, a search-forward hero with promo tiles, a twelve-tile category grid, flash deals, top sellers, vendor spotlights, a trust-and-returns strip and a sell call-to-action. Every “product photo” is a CSS gradient, inline SVG or emoji on a soft tinted tile, so the page ships with zero external images.
The interactions all genuinely work in vanilla JS. Add to cart opens a slide-in drawer that
tracks per-item quantities, line totals and a live subtotal, with the header badge bumping on each
change. Product cards have toggleable favorites, the flash-deals module runs a real ticking
countdown, the top-sellers rail scrolls with prev/next buttons, vendor cards have a follow toggle,
and the hero quick-search chips populate the search box. The cart drawer is a focus-managed dialog
that closes on Escape, scrim click or the close button.
Built clean white + ink with a single bold blue brand accent and a red sale color, Inter throughout,
star ratings and review counts, free-shipping notes and secure-checkout badges. It is fully keyboard
operable with visible focus rings, meets WCAG AA contrast, respects prefers-reduced-motion, and
reflows gracefully from wide desktop down to ~360px.
Illustrative storefront UI only — fictional products, prices, and reviews. No real checkout.