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
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
Inside Kokum
A courtyard, a mango tree, forty seats
“The fish recheado is the best I have had outside a Goan home. Book the courtyard, not inside.”
“Came for coffee, stayed for lunch, came back the next morning for the poi. Lovely staff.”
“Quiet, unpretentious, and the kokum cold brew should be bottled and sold.”
Reservations
Book a table
Walk-ins are welcome for breakfast. For dinner and groups of six or more, please book.
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.