Skip to content
All Taste components

Templates

Online store page

A storefront home page for a ceramics brand with categories, a product grid with prices and offers, reviews and a newsletter sign-up.

Announcement bar · nav with search and cart · hero · categories · product grid with filters · offer band · why us · reviews summary · newsletter · footer

Free shipping on orders over ₹999 · Breakages replaced free · 7-day returns

New · the Monsoon collection

Handmade stoneware for slow mornings.

Thrown by hand in our Jaipur studio, glazed in small batches, and packed so it reaches you in one piece.

Bestsellers

15% off any set of four, this festive season

Mix colours if you like. Ends 31 October.

MITTI15Shop sets

Thrown by hand

Eleven potters in Jaipur. No two pieces are quite the same.

Lead-free glazes

Food safe, microwave and dishwasher safe, tested every batch.

Ships in 48 hours

Packed in recycled paper pulp, delivered across India.

Breakage replaced

Arrived chipped? Send a photo and we send a new one.

Customer reviews

4.8from 3,412 verified orders
5★
2,798
4★
409
3★
137
2★
41
1★
27

Even better in person

The glaze changes colour in morning light. Packed so well that I reused the box.
Neha G. · Monsoon mug Verified

Survived my toddler

Dropped twice on the kitchen floor, not a chip. Dishwasher safe as promised.
Karthik S. · Pasta bowls Verified

A thoughtful gift

Sent a set to my mother in Kochi. It arrived in three days with a handwritten note.
Aisha R. · Gift set of four Verified

First look at new glazes

One email a month when a new batch leaves the kiln. Subscribers get 10% off their first order.

No spam. Unsubscribe in one click.

import { ArrowRight, Check, Heart, Leaf, Mail, Search, ShieldCheck, ShoppingBag, Star, Truck } from "lucide-react";
import { cn } from "@/lib/utils";

type Product = { name: string; price: string; was?: string; badge?: string; rating: string; href: string };

const categories = [
  { name: "Mugs and cups", count: 32 }, { name: "Bowls", count: 24 }, { name: "Plates", count: 18 },
  { name: "Serveware", count: 15 }, { name: "Planters", count: 12 }, { name: "Gift sets", count: 9 },
];

const products: Product[] = [
  { name: "Monsoon mug, 350 ml", price: "₹1,450", badge: "New", rating: "4.9 (212)", href: "#" },
  { name: "Haldi serving bowl", price: "₹1,890", was: "₹2,360", badge: "−20%", rating: "4.8 (96)", href: "#" },
  { name: "Ghats dinner plate, set of 2", price: "₹2,200", rating: "4.7 (154)", href: "#" },
  { name: "Terracotta planter, medium", price: "₹980", rating: "4.8 (301)", href: "#" },
  { name: "Sea-glass tumblers, set of 4", price: "₹1,650", rating: "4.6 (88)", href: "#" },
  { name: "Rain-speckle teapot", price: "₹3,200", badge: "Bestseller", rating: "4.9 (143)", href: "#" },
  { name: "Pasta bowls, set of 2", price: "₹1,760", was: "₹2,200", badge: "−20%", rating: "4.8 (210)", href: "#" },
  { name: "Kitchen salt pig", price: "₹890", rating: "4.7 (64)", href: "#" },
];

const values = [
  { icon: Leaf, title: "Thrown by hand", body: "Eleven potters in Jaipur. No two pieces are quite the same." },
  { icon: ShieldCheck, title: "Lead-free glazes", body: "Food safe, microwave and dishwasher safe, tested every batch." },
  { icon: Truck, title: "Ships in 48 hours", body: "Packed in recycled paper pulp, delivered across India." },
  { icon: Check, title: "Breakage replaced", body: "Arrived chipped? Send a photo and we send a new one." },
];

const reviews = [
  { title: "Even better in person", body: "The glaze changes colour in morning light. Packed so well that I reused the box.", name: "Neha G.", item: "Monsoon mug" },
  { title: "Survived my toddler", body: "Dropped twice on the kitchen floor, not a chip.", name: "Karthik S.", item: "Pasta bowls" },
  { title: "A thoughtful gift", body: "Sent a set to my mother in Kochi. It arrived in three days.", name: "Aisha R.", item: "Gift set of four" },
];

const distribution = [{ stars: 5, pct: 82 }, { stars: 4, pct: 12 }, { stars: 3, pct: 4 }, { stars: 2, pct: 1 }, { stars: 1, pct: 1 }];

function Container({ className, children }: { className?: string; children: React.ReactNode }) {
  return <div className={cn("mx-auto w-full max-w-6xl px-5 sm:px-8", className)}>{children}</div>;
}

function Stars() {
  return <div className="flex gap-0.5 text-primary" aria-label="5 out of 5 stars">{[0, 1, 2, 3, 4].map((i) => <Star key={i} className="size-4 fill-current" />)}</div>;
}

function Header() {
  return (
    <header className="border-b border-border">
      <p className="bg-fg px-4 py-2 text-center text-sm text-bg">Free shipping on orders over <b>₹999</b> · 7-day returns</p>
      <Container className="flex h-16 items-center gap-8">
        <a href="/" className="font-semibold tracking-tight">Mitti Studio</a>
        <nav aria-label="Shop" className="hidden gap-6 text-sm text-fg-muted lg:flex">
          {["New in", "Mugs", "Bowls", "Plates", "Gifts"].map((l) => <a key={l} href="#" className="hover:text-fg">{l}</a>)}
        </nav>
        <div className="ml-auto flex items-center gap-1">
          <label className="relative mr-2 hidden md:block">
            <span className="sr-only">Search</span>
            <Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-fg-subtle" />
            <input type="search" placeholder="Search mugs, bowls…" className="h-9 w-56 rounded-full bg-surface-2 pl-9 pr-3 text-sm outline-none focus:ring-2 focus:ring-ring" />
          </label>
          <a href="#" aria-label="Wishlist" className="grid size-9 place-items-center rounded hover:bg-surface-2"><Heart className="size-5" /></a>
          <a href="#" aria-label="Cart, 2 items" className="relative grid size-9 place-items-center rounded hover:bg-surface-2">
            <ShoppingBag className="size-5" />
            <span className="absolute right-0 top-0.5 grid min-w-4 place-items-center rounded-full bg-primary px-1 text-[10px] font-semibold text-primary-fg">2</span>
          </a>
        </div>
      </Container>
    </header>
  );
}

function Hero() {
  return (
    <section className="pt-6">
      <Container>
        <div className="grid overflow-hidden rounded-xl bg-surface md:grid-cols-[1fr_1.1fr]">
          <div className="grid content-center justify-items-start gap-5 p-6 sm:p-14">
            <span className="rounded-full bg-primary-soft px-2.5 py-0.5 text-xs font-medium text-primary-soft-fg">New · the Monsoon collection</span>
            <h1 className="text-balance text-4xl font-semibold tracking-tight sm:text-6xl">Handmade stoneware for slow mornings.</h1>
            <p className="max-w-md text-lg text-fg-muted">Thrown by hand in our Jaipur studio and glazed in small batches.</p>
            <a href="#" className="inline-flex h-11 items-center gap-2 rounded bg-primary px-5 font-medium text-primary-fg hover:bg-primary-hover">Shop the collection <ArrowRight className="size-4" /></a>
          </div>
          <div role="img" aria-label="Monsoon mug and bowl on a shelf" className="min-h-72 bg-gradient-to-br from-brand-100 to-brand-300" />
        </div>
      </Container>
    </section>
  );
}

function Categories() {
  return (
    <section aria-labelledby="cats" className="py-16 sm:py-20">
      <Container>
        <h2 id="cats" className="mb-5 font-semibold">Shop by category</h2>
        <div className="grid grid-cols-3 gap-4 lg:grid-cols-6">
          {categories.map((c) => (
            <a key={c.name} href="#" className="grid gap-2.5 text-center text-sm font-medium">
              <span className="aspect-square rounded-xl bg-gradient-to-br from-brand-200 to-brand-500" aria-hidden />
              <span>{c.name}<span className="block font-normal text-fg-subtle">{c.count} pieces</span></span>
            </a>
          ))}
        </div>
      </Container>
    </section>
  );
}

function ProductCard({ p }: { p: Product }) {
  return (
    <article>
      <div className="relative aspect-[4/5] rounded-lg bg-surface-2">
        {p.badge && (
          <span className={cn("absolute left-3 top-3 rounded-full px-2 py-0.5 text-xs font-medium", p.was ? "bg-danger-soft text-danger-fg" : "bg-primary-soft text-primary-soft-fg")}>{p.badge}</span>
        )}
        <button type="button" aria-label={"Save " + p.name} className="absolute right-2.5 top-2.5 grid size-8 place-items-center rounded-full bg-bg shadow-sm"><Heart className="size-4" /></button>
      </div>
      <div className="mt-3.5 grid gap-1.5">
        <h3 className="text-[15px] font-medium"><a href={p.href}>{p.name}</a></h3>
        <p className="text-xs text-fg-subtle">★ {p.rating}</p>
        <div className="flex items-center gap-2 tabular-nums">
          <b className="font-semibold">{p.price}</b>
          {p.was && <s className="text-sm text-fg-subtle">{p.was}</s>}
          <button type="button" className="ml-auto hidden h-8 items-center rounded border border-border-strong px-2.5 text-sm hover:bg-surface sm:inline-flex">Add</button>
        </div>
      </div>
    </article>
  );
}

function Products() {
  return (
    <section className="pb-20">
      <Container>
        <div className="mb-8 flex flex-wrap items-end justify-between gap-4">
          <h2 className="text-3xl font-semibold tracking-tight sm:text-4xl">Bestsellers</h2>
          <fieldset className="flex flex-wrap gap-1.5">
            <legend className="sr-only">Filter</legend>
            {["All", "Mugs", "Bowls", "Plates"].map((f, i) => (
              <label key={f} className="cursor-pointer">
                <input type="radio" name="filter" defaultChecked={i === 0} className="peer sr-only" />
                <span className="block rounded-full border border-border px-3 py-1.5 text-sm text-fg-muted peer-checked:border-fg peer-checked:bg-fg peer-checked:text-bg">{f}</span>
              </label>
            ))}
          </fieldset>
        </div>
        <div className="grid grid-cols-2 gap-x-3 gap-y-8 sm:gap-x-5 lg:grid-cols-4">
          {products.map((p) => <ProductCard key={p.name} p={p} />)}
        </div>
      </Container>
    </section>
  );
}

function Offer() {
  return (
    <section>
      <Container>
        <div className="flex flex-wrap items-center justify-between gap-6 rounded-xl bg-primary p-6 text-primary-fg sm:p-12">
          <div>
            <h2 className="text-2xl font-semibold tracking-tight sm:text-3xl">15% off any set of four, this festive season</h2>
            <p className="mt-1.5">Mix colours if you like. Ends 31 October.</p>
          </div>
          <div className="flex items-center gap-3">
            <span className="rounded border border-dashed border-primary-fg px-4 py-2 font-mono font-semibold tracking-widest">MITTI15</span>
            <a href="#" className="inline-flex h-11 items-center rounded bg-bg px-5 font-medium text-fg">Shop sets</a>
          </div>
        </div>
      </Container>
    </section>
  );
}

function Values() {
  return (
    <section className="py-20">
      <Container className="grid gap-8 sm:grid-cols-2 lg:grid-cols-4">
        {values.map(({ icon: Icon, title, body }) => (
          <div key={title} className="grid content-start gap-2">
            <Icon className="mb-1 size-6 text-primary" />
            <h3 className="font-semibold">{title}</h3>
            <p className="text-sm text-fg-muted">{body}</p>
          </div>
        ))}
      </Container>
    </section>
  );
}

function Reviews() {
  return (
    <section className="bg-surface py-20">
      <Container className="grid gap-12 lg:grid-cols-[280px_1fr]">
        <div className="grid content-start gap-2.5">
          <h2 className="font-semibold">Customer reviews</h2>
          <p className="text-6xl font-semibold tracking-tighter">4.8</p>
          <Stars />
          <p className="text-sm text-fg-subtle">from 3,412 verified orders</p>
          <dl className="mt-3 grid gap-2 text-xs text-fg-muted">
            {distribution.map((d) => (
              <div key={d.stars} className="grid grid-cols-[24px_1fr_36px] items-center gap-2.5">
                <dt>{d.stars}★</dt>
                <dd className="h-1.5 overflow-hidden rounded-full bg-surface-2"><span className="block h-full rounded-full bg-primary" style={{ width: d.pct + "%" }} /></dd>
                <dd className="text-right tabular-nums">{d.pct}%</dd>
              </div>
            ))}
          </dl>
        </div>
        <div className="grid gap-4 md:grid-cols-3">
          {reviews.map((r) => (
            <figure key={r.name} className="grid content-start gap-2.5 rounded-lg border border-border bg-bg p-6">
              <Stars />
              <h3 className="font-semibold">{r.title}</h3>
              <blockquote className="text-sm text-fg-muted">{r.body}</blockquote>
              <figcaption className="mt-1 text-sm text-fg-subtle"><b className="font-medium text-fg">{r.name}</b> · {r.item} · <span className="text-success-fg">Verified</span></figcaption>
            </figure>
          ))}
        </div>
      </Container>
    </section>
  );
}

function Newsletter() {
  return (
    <section className="py-20">
      <Container>
        <div className="grid justify-items-center gap-3 rounded-xl bg-surface px-5 py-14 text-center">
          <Mail className="size-6" />
          <h2 className="text-3xl font-semibold tracking-tight">First look at new glazes</h2>
          <p className="max-w-md text-fg-muted">One email a month. Subscribers get 10% off their first order.</p>
          <form className="mt-2 flex w-full max-w-md flex-col gap-2 sm:flex-row">
            <label htmlFor="news-email" className="sr-only">Email address</label>
            <input id="news-email" type="email" placeholder="you@example.com" className="h-11 flex-1 rounded border border-border-strong bg-bg px-3 outline-none focus:ring-2 focus:ring-ring" />
            <button type="submit" className="h-11 rounded bg-primary px-5 font-medium text-primary-fg hover:bg-primary-hover">Subscribe</button>
          </form>
        </div>
      </Container>
    </section>
  );
}

function Footer() {
  const cols = [
    { title: "Shop", links: ["Mugs and cups", "Bowls", "Plates", "Gift cards"] },
    { title: "Help", links: ["Shipping", "Returns", "Care guide", "Track order"] },
    { title: "About", links: ["Our studio", "Wholesale", "Journal", "Contact"] },
  ];
  return (
    <footer className="border-t border-border pb-8 pt-16">
      <Container>
        <div className="grid grid-cols-2 gap-8 md:grid-cols-[1.6fr_1fr_1fr_1fr]">
          <div className="col-span-2 grid content-start gap-3 md:col-span-1">
            <p className="font-semibold">Mitti Studio</p>
            <p className="max-w-xs text-sm text-fg-muted">Handmade stoneware from Jaipur. UPI, cards, net banking and cash on delivery.</p>
          </div>
          {cols.map((c) => (
            <nav key={c.title} aria-label={c.title}>
              <h2 className="mb-3 text-sm font-medium text-fg-subtle">{c.title}</h2>
              <ul className="grid gap-2.5 text-sm text-fg-muted">{c.links.map((l) => <li key={l}><a href="#" className="hover:text-fg">{l}</a></li>)}</ul>
            </nav>
          ))}
        </div>
        <p className="mt-14 border-t border-border pt-6 text-sm text-fg-subtle">© 2026 Mitti Studio Crafts Pvt. Ltd.</p>
      </Container>
    </footer>
  );
}

export default function Page() {
  return (
    <div className="bg-bg font-sans text-fg">
      <Header />
      <main>
        <Hero />
        <Categories />
        <Products />
        <Offer />
        <Values />
        <Reviews />
        <Newsletter />
      </main>
      <Footer />
    </div>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.