Skip to content
All Taste components

Templates

Restaurant & café page

A warm page for a café with a full-bleed hero, a priced menu in rupees, a photo mosaic, reviews and a table booking form.

Centred-logo nav · image hero · story · menu with veg marks · gallery · reviews · reservation with hours · footer

Open today · 8 am to 11 pm

Slow breakfasts, Goan plates and good coffee.

A courtyard café in the old Latin quarter of Panaji, cooking what our grandmothers cooked, with a little less ghee.

Since 2014

We started with six tables and one recipe for fish curry. Ten years on, the curry is the same, the tables are under a mango tree, and the bread still comes from the bakery next door at seven every morning.

Ana and Rohan Fernandes, owners

The menu

Cooked to order, all day

Breakfast · 8 to 11:30

  • Poi with chorizo butter₹320

    Goan bread from the bakery next door, warm

  • Banana and jaggery pancakes₹290

    Coconut cream, toasted cashews

  • Akuri on sourdough₹280

    Parsi-style scrambled eggs, green chutney

Plates · from 12:30

  • Fish recheado₹590

    Kingfish in red masala, kokum curry, red rice

  • Mushroom xacuti₹420

    Roasted coconut gravy, two poi

  • Jackfruit cafreal bao₹380

    Green masala, pickled onion, three pieces

Coffee and coolers

  • South Indian filter coffee₹140

    Chikmagalur beans, brass tumbler

  • Kokum cold brew₹220

    Twelve-hour brew over kokum syrup

  • Sol kadhi₹160

    Kokum and coconut milk, chilled

Desserts

  • Bebinca₹240

    Seven-layer coconut cake, vanilla ice cream

  • Serradura₹220

    Biscuit crumb and cream, in a jar

  • Dark chocolate cashew tart₹260

    Sea salt, orange zest

VegetarianContains egg, meat or fishPrices include GST · No service chargeFull menu (PDF)

Inside Kokum

A courtyard, a mango tree, forty seats

More photos
The courtyard at breakfast
Fish recheado
Filter coffee
Our kitchen team
Bebinca
Evenings
“The fish recheado is the best I have had outside a Goan home. Book the courtyard, not inside.”
Meghna S., Mumbai
“Came for coffee, stayed for lunch, came back the next morning for the poi. Lovely staff.”
Tom & Ines, Lisbon
“Quiet, unpretentious, and the kokum cold brew should be bottled and sold.”
Rahul V., Panaji

Reservations

Book a table

Walk-ins are welcome for breakfast. For dinner and groups of six or more, please book.

Seating

We confirm by SMS within 15 minutes.

import { ArrowRight, Star } from "lucide-react";
import { cn } from "@/lib/utils";

type Dish = { name: string; note: string; price: string; diet?: "veg" | "non-veg" };

const menu: { title: string; dishes: Dish[] }[] = [
  { title: "Breakfast · 8 to 11:30", dishes: [
    { name: "Poi with chorizo butter", note: "Goan bread from the bakery next door, warm", price: "₹320", diet: "non-veg" },
    { name: "Banana and jaggery pancakes", note: "Coconut cream, toasted cashews", price: "₹290", diet: "veg" },
    { name: "Akuri on sourdough", note: "Parsi-style scrambled eggs, green chutney", price: "₹280", diet: "non-veg" },
  ] },
  { title: "Plates · from 12:30", dishes: [
    { name: "Fish recheado", note: "Kingfish in red masala, kokum curry, red rice", price: "₹590", diet: "non-veg" },
    { name: "Mushroom xacuti", note: "Roasted coconut gravy, two poi", price: "₹420", diet: "veg" },
    { name: "Jackfruit cafreal bao", note: "Green masala, pickled onion, three pieces", price: "₹380", diet: "veg" },
  ] },
  { title: "Coffee and coolers", dishes: [
    { name: "South Indian filter coffee", note: "Chikmagalur beans, brass tumbler", price: "₹140" },
    { name: "Kokum cold brew", note: "Twelve-hour brew over kokum syrup", price: "₹220" },
    { name: "Sol kadhi", note: "Kokum and coconut milk, chilled", price: "₹160" },
  ] },
  { title: "Desserts", dishes: [
    { name: "Bebinca", note: "Seven-layer coconut cake, vanilla ice cream", price: "₹240", diet: "veg" },
    { name: "Serradura", note: "Biscuit crumb and cream, in a jar", price: "₹220", diet: "veg" },
    { name: "Dark chocolate cashew tart", note: "Sea salt, orange zest", price: "₹260", diet: "veg" },
  ] },
];

const gallery = [
  { caption: "The courtyard at breakfast", className: "col-span-2 row-span-2" },
  { caption: "Fish recheado" },
  { caption: "Filter coffee" },
  { caption: "Our kitchen team", className: "sm:col-span-2" },
  { caption: "Bebinca" },
  { caption: "Evenings" },
];

const reviews = [
  { quote: "The fish recheado is the best I have had outside a Goan home.", name: "Meghna S., Mumbai" },
  { quote: "Came for coffee, stayed for lunch, came back the next morning for the poi.", name: "Tom & Ines, Lisbon" },
  { quote: "Quiet, unpretentious, and the kokum cold brew should be bottled and sold.", name: "Rahul V., Panaji" },
];

const hours = [{ meal: "Breakfast", time: "8:00 – 11:30" }, { meal: "Lunch", time: "12:30 – 3:30" }, { meal: "Dinner", time: "7:00 – 11:00" }];

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 Eyebrow({ children }: { children: React.ReactNode }) {
  return <p className="text-xs font-medium uppercase tracking-[.24em] text-primary">{children}</p>;
}

function Logo() {
  return (
    <a href="/" className="grid justify-items-center leading-none">
      <span className="text-2xl font-semibold tracking-tighter">kokum</span>
      <span className="mt-1 text-[10px] uppercase tracking-[.32em] text-fg-subtle">Café · Panaji</span>
    </a>
  );
}

function Header() {
  return (
    <header>
      <Container className="grid h-20 grid-cols-[auto_1fr] items-center gap-6 md:grid-cols-[1fr_auto_1fr]">
        <nav aria-label="Main" className="hidden gap-6 text-sm text-fg-muted md:flex">
          {["Menu", "Story", "Gallery", "Visit"].map((l) => <a key={l} href="#" className="hover:text-fg">{l}</a>)}
        </nav>
        <Logo />
        <a href="#reserve" className="justify-self-end rounded bg-primary px-4 py-2 text-sm font-medium text-primary-fg hover:bg-primary-hover">Reserve a table</a>
      </Container>
    </header>
  );
}

function Hero() {
  return (
    <section>
      <Container>
        <div className="relative flex h-[480px] flex-col justify-end gap-5 overflow-hidden rounded-xl bg-gradient-to-br from-brand-500 to-brand-900 p-6 text-neutral-50 sm:h-[560px] sm:p-12">
          <span className="absolute left-6 top-6 rounded-full bg-neutral-50 px-3.5 py-1.5 text-sm font-medium text-neutral-950">Open today · 8 am to 11 pm</span>
          <h1 className="max-w-[13ch] text-balance text-4xl font-semibold tracking-tight sm:text-7xl">Slow breakfasts, Goan plates and good coffee.</h1>
          <p className="max-w-md text-lg text-neutral-100">A courtyard café in the old Latin quarter of Panaji.</p>
          <div className="flex flex-wrap gap-3">
            <a href="#reserve" className="inline-flex h-11 items-center rounded bg-neutral-50 px-5 font-medium text-neutral-950">Reserve a table</a>
            <a href="#menu" className="inline-flex h-11 items-center rounded border border-neutral-300 px-5 font-medium">See the menu</a>
          </div>
        </div>
      </Container>
    </section>
  );
}

function Story() {
  return (
    <section className="py-20 sm:py-24">
      <Container className="grid justify-items-center gap-6 text-center">
        <Eyebrow>Since 2014</Eyebrow>
        <p className="max-w-[32ch] text-balance text-2xl leading-snug tracking-tight sm:text-3xl">
          We started with six tables and one recipe for fish curry. Ten years on, the curry is the same and the tables are under a mango tree.
        </p>
        <p className="text-sm text-fg-muted">Ana and Rohan Fernandes, owners</p>
      </Container>
    </section>
  );
}

function DietMark({ diet }: { diet?: Dish["diet"] }) {
  if (!diet) return <span className="size-3 shrink-0" aria-hidden />;
  return (
    <span role="img" aria-label={diet === "veg" ? "Vegetarian" : "Non-vegetarian"}
      className={cn("grid size-3 shrink-0 place-items-center self-center rounded-[2px] border-[1.5px]", diet === "veg" ? "border-success" : "border-danger")}>
      <span className={cn("size-1.5 rounded-full", diet === "veg" ? "bg-success" : "bg-danger")} />
    </span>
  );
}

function Menu() {
  return (
    <section id="menu" aria-labelledby="menu-title" className="bg-surface py-20 sm:py-24">
      <Container>
        <div className="mb-12 grid justify-items-center gap-3 text-center">
          <Eyebrow>The menu</Eyebrow>
          <h2 id="menu-title" className="text-3xl font-semibold tracking-tight sm:text-4xl">Cooked to order, all day</h2>
        </div>
        <div className="grid gap-12 md:grid-cols-2 md:gap-x-20">
          {menu.map((group) => (
            <div key={group.title}>
              <h3 className="border-b border-fg pb-3 text-xs font-semibold uppercase tracking-[.18em]">{group.title}</h3>
              <ul>
                {group.dishes.map((d) => (
                  <li key={d.name} className="border-b border-border py-4">
                    <div className="flex items-baseline gap-2.5 font-medium">
                      <DietMark diet={d.diet} />
                      <span>{d.name}</span>
                      <span className="flex-1 -translate-y-1 border-b border-dotted border-border-strong" aria-hidden />
                      <span className="tabular-nums">{d.price}</span>
                    </div>
                    <p className="mt-1 pl-[22px] text-sm text-fg-muted">{d.note}</p>
                  </li>
                ))}
              </ul>
            </div>
          ))}
        </div>
        <p className="mt-10 text-center text-sm text-fg-muted">Prices include GST · No service charge</p>
      </Container>
    </section>
  );
}

function Gallery() {
  return (
    <section aria-label="Gallery" className="py-20 sm:py-24">
      <Container>
        <h2 className="mb-10 text-3xl font-semibold tracking-tight sm:text-4xl">A courtyard, a mango tree, forty seats</h2>
        <div className="grid auto-rows-[140px] grid-cols-2 gap-3 sm:auto-rows-[190px] sm:grid-cols-4">
          {gallery.map((g) => (
            <figure key={g.caption} className={cn("flex items-end rounded-lg bg-gradient-to-br from-brand-300 to-brand-700 p-3", g.className)}>
              <figcaption className="rounded-full bg-neutral-950 px-2.5 py-1 text-xs text-neutral-50">{g.caption}</figcaption>
            </figure>
          ))}
        </div>
      </Container>
    </section>
  );
}

function Reviews() {
  return (
    <section aria-label="Reviews" className="bg-surface py-20">
      <Container className="grid gap-10 md:grid-cols-3 md:divide-x md:divide-border">
        {reviews.map((r) => (
          <figure key={r.name} className="grid justify-items-center gap-4 text-center md:px-8">
            <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>
            <blockquote className="text-lg">“{r.quote}”</blockquote>
            <figcaption className="text-sm text-fg-subtle">{r.name}</figcaption>
          </figure>
        ))}
      </Container>
    </section>
  );
}

const field = "h-10 w-full rounded border border-border-strong bg-bg px-3 font-normal outline-none focus:ring-2 focus:ring-ring";

function Reserve() {
  return (
    <section id="reserve" className="py-20 sm:py-24">
      <Container>
        <div className="mb-10 grid justify-items-center gap-3 text-center">
          <Eyebrow>Reservations</Eyebrow>
          <h2 className="text-3xl font-semibold tracking-tight sm:text-4xl">Book a table</h2>
        </div>
        <div className="grid overflow-hidden rounded-xl border border-border lg:grid-cols-[1.25fr_1fr]">
          <form aria-label="Reserve a table" className="grid gap-4 p-6 sm:p-10">
            <div className="grid gap-4 sm:grid-cols-2">
              <label className="grid gap-1.5 text-sm font-medium">Name<input className={field} autoComplete="name" /></label>
              <label className="grid gap-1.5 text-sm font-medium">Mobile number<input type="tel" className={field} autoComplete="tel" /></label>
            </div>
            <div className="grid gap-4 sm:grid-cols-3">
              <label className="grid gap-1.5 text-sm font-medium">Date<input type="date" className={field} /></label>
              <label className="grid gap-1.5 text-sm font-medium">Time
                <select className={field} defaultValue="19:30"><option value="19:00">7:00 pm</option><option value="19:30">7:30 pm</option><option value="20:00">8:00 pm</option></select>
              </label>
              <label className="grid gap-1.5 text-sm font-medium">Guests
                <select className={field} defaultValue="2"><option value="1">1 guest</option><option value="2">2 guests</option><option value="4">4 guests</option></select>
              </label>
            </div>
            <label className="grid gap-1.5 text-sm font-medium">Anything we should know?<textarea rows={3} className={cn(field, "h-auto py-2")} /></label>
            <button type="submit" className="h-11 rounded bg-primary font-medium text-primary-fg hover:bg-primary-hover">Request table</button>
            <p className="text-sm text-fg-subtle">We confirm by SMS within 15 minutes.</p>
          </form>
          <aside aria-label="Hours and address" className="grid content-start gap-6 bg-neutral-900 p-6 text-neutral-50 sm:p-10">
            <div>
              <h3 className="mb-2 text-xs font-semibold uppercase tracking-[.18em] text-neutral-300">Hours</h3>
              <dl className="grid grid-cols-[1fr_auto] gap-1.5">
                {hours.map((h) => <div key={h.meal} className="contents"><dt>{h.meal}</dt><dd className="text-right tabular-nums">{h.time}</dd></div>)}
              </dl>
              <p className="mt-2 text-neutral-300">Closed on Tuesdays</p>
            </div>
            <div><h3 className="mb-2 text-xs font-semibold uppercase tracking-[.18em] text-neutral-300">Find us</h3><p>14 Rua de Ourém, Fontainhas, Panaji, Goa 403001</p></div>
            <div><h3 className="mb-2 text-xs font-semibold uppercase tracking-[.18em] text-neutral-300">Call</h3><p>+91 832 242 6610</p></div>
          </aside>
        </div>
      </Container>
    </section>
  );
}

function Footer() {
  return (
    <footer className="border-t border-border py-14 text-center">
      <Container className="grid justify-items-center gap-4">
        <Logo />
        <nav aria-label="Footer" className="flex flex-wrap justify-center gap-6 text-sm text-fg-muted">
          {["Menu", "Reservations", "Private dining", "Gift cards", "Careers"].map((l) => <a key={l} href="#" className="hover:text-fg">{l}</a>)}
        </nav>
        <p className="text-sm text-fg-subtle">© 2026 Kokum Hospitality LLP</p>
      </Container>
    </footer>
  );
}

export default function Page() {
  return (
    <div className="bg-bg font-sans text-fg">
      <Header />
      <main>
        <Hero />
        <Story />
        <Menu />
        <Gallery />
        <Reviews />
        <Reserve />
      </main>
      <Footer />
    </div>
  );
}

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