feat: default language Swedish, modern mobile navigation menu, mobile optimizations & translations, shorten mobile hero banner to 4s

- Set 'sv' as default in LanguageProvider + layout (users can switch to 'en')
- Completely revamped mobile drawer menu: slide animation (framer-motion), icons, active states with high-contrast text, large touch targets, better tap feedback
- Horizontal snap-scrolling category filters on mobile (homepage + /menu) for thumb-friendly UX
- Added active:scale / touch-manipulation + press states across cards, buttons, filters for better mobile tap visibility/feedback
- Updated translations for menu page, locations, footer, experience (Swedish-first)
- Made locations + footer language-aware
- Trimmed banner_mobile.mp4 (raw + re-optimized) from 6s to 4s for faster load on mobile
- Reordered languages so Svenska appears first in switcher
- .gitignore already protects developer_instructions.txt (previous commit)
This commit is contained in:
Zeeshan Khan
2026-06-02 13:44:19 +02:00
parent 10d7555d31
commit 0b6cc1acae
11 changed files with 221 additions and 101 deletions
+1 -1
View File
@@ -91,7 +91,7 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html <html
lang="en" lang="sv"
className={`${playfair.variable} ${geistSans.variable} ${geistMono.variable} h-full antialiased`} className={`${playfair.variable} ${geistSans.variable} ${geistMono.variable} h-full antialiased`}
> >
<body className="min-h-full flex flex-col bg-[#F8F5F0] text-[#2C2A26]"> <body className="min-h-full flex flex-col bg-[#F8F5F0] text-[#2C2A26]">
+9 -3
View File
@@ -16,10 +16,16 @@
* - Both branches now have the same opening hours. * - Both branches now have the same opening hours.
*/ */
'use client';
import Navbar from "@/components/Navbar"; import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer"; import Footer from "@/components/Footer";
import { useLanguage } from "@/lib/language-context";
import { getTranslation } from "@/lib/translations";
export default function LocationsPage() { export default function LocationsPage() {
const { language } = useLanguage();
const t = getTranslation(language);
return ( return (
<div className="min-h-screen bg-[#F8F5F0] text-[#2C2A26]"> <div className="min-h-screen bg-[#F8F5F0] text-[#2C2A26]">
<Navbar /> <Navbar />
@@ -27,15 +33,15 @@ export default function LocationsPage() {
<div className="max-w-5xl mx-auto px-6 pt-20 pb-16"> <div className="max-w-5xl mx-auto px-6 pt-20 pb-16">
<div className="text-center mb-12"> <div className="text-center mb-12">
<div className="text-[#B38B4D] text-xs tracking-[3px] mb-3">WHERE TO FIND US</div> <div className="text-[#B38B4D] text-xs tracking-[3px] mb-3">WHERE TO FIND US</div>
<h1 className="text-6xl md:text-7xl tracking-[-2.5px] leading-none mb-4">Our Locations</h1> <h1 className="text-6xl md:text-7xl tracking-[-2.5px] leading-none mb-4">{language === 'sv' ? 'Våra Platser' : 'Our Locations'}</h1>
<p className="text-xl text-[#6B665F] max-w-md mx-auto"> <p className="text-xl text-[#6B665F] max-w-md mx-auto">
Two branches in Gothenburg both serving authentic flavors with the same royal hospitality. {language === 'sv' ? 'Två restauranger i Göteborg — båda serverar autentiska smaker med samma kungliga gästfrihet.' : 'Two branches in Gothenburg — both serving authentic flavors with the same royal hospitality.'}
</p> </p>
</div> </div>
<div className="flex justify-center mb-8"> <div className="flex justify-center mb-8">
<a href="/#contact" className="btn-primary px-8 py-3 rounded-full text-sm tracking-[0.5px]"> <a href="/#contact" className="btn-primary px-8 py-3 rounded-full text-sm tracking-[0.5px]">
Make a Reservation {t.reserve}
</a> </a>
</div> </div>
+20 -15
View File
@@ -11,6 +11,8 @@ import { ScrollTrigger } from "gsap/ScrollTrigger";
import Navbar from "@/components/Navbar"; import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer"; import Footer from "@/components/Footer";
import { useCart } from "@/components/CartContext"; import { useCart } from "@/components/CartContext";
import { useLanguage } from "@/lib/language-context";
import { getTranslation } from "@/lib/translations";
gsap.registerPlugin(ScrollTrigger); gsap.registerPlugin(ScrollTrigger);
@@ -20,6 +22,8 @@ export default function MenuPage() {
const [showVegetarianOnly, setShowVegetarianOnly] = useState(false); const [showVegetarianOnly, setShowVegetarianOnly] = useState(false);
const { addToCart } = useCart(); const { addToCart } = useCart();
const { language } = useLanguage();
const t = getTranslation(language);
// Sidebar categories // Sidebar categories
const sidebarCategories = [ const sidebarCategories = [
@@ -71,9 +75,9 @@ export default function MenuPage() {
<div className="max-w-7xl mx-auto px-6 pt-14 pb-10"> <div className="max-w-7xl mx-auto px-6 pt-14 pb-10">
<div className="max-w-3xl"> <div className="max-w-3xl">
<div className="text-[#B38B4D] text-xs tracking-[3.5px] mb-4 font-medium">AUTHENTIC GENEROUS ROYAL</div> <div className="text-[#B38B4D] text-xs tracking-[3.5px] mb-4 font-medium">AUTHENTIC GENEROUS ROYAL</div>
<h1 className="text-6xl md:text-7xl tracking-[-2.8px] leading-none mb-5 text-[#101724]">Our Menu</h1> <h1 className="text-6xl md:text-7xl tracking-[-2.8px] leading-none mb-5 text-[#101724]">{t.menu.title}</h1>
<p className="text-2xl text-[#4b5563] max-w-2xl"> <p className="text-2xl text-[#4b5563] max-w-2xl">
Traditional recipes. Generous portions. Made with heart. {t.menu.subtitle}
</p> </p>
</div> </div>
</div> </div>
@@ -90,7 +94,8 @@ export default function MenuPage() {
<h3 className="font-serif text-3xl tracking-tight">Signature Categories</h3> <h3 className="font-serif text-3xl tracking-tight">Signature Categories</h3>
</div> </div>
<div className="space-y-1 pr-4"> {/* Modern mobile-first category menu: horizontal scroller on phones/tablets, vertical sidebar on desktop */}
<div className="flex lg:flex-col gap-2 overflow-x-auto pb-3 lg:pb-0 pr-1 lg:pr-4 snap-x snap-mandatory [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
{sidebarCategories.map((cat) => { {sidebarCategories.map((cat) => {
const isActive = activeCategory === cat.id; const isActive = activeCategory === cat.id;
const itemCount = cat.id === "All" const itemCount = cat.id === "All"
@@ -101,16 +106,16 @@ export default function MenuPage() {
<button <button
key={cat.id} key={cat.id}
onClick={() => handleCategorySelect(cat.id)} onClick={() => handleCategorySelect(cat.id)}
className={`w-full flex items-center justify-between px-5 py-3.5 rounded-2xl text-left transition-all group ${ className={`flex-shrink-0 snap-start min-w-[140px] lg:min-w-0 lg:w-full flex items-center justify-between px-5 py-3 lg:py-3.5 rounded-2xl text-left transition-all active:scale-[0.985] group ${
isActive isActive
? "bg-[#101724] text-white shadow-lg" ? "bg-[#101724] text-white shadow-lg"
: "hover:bg-white hover:shadow-sm text-[#101724] border border-transparent hover:border-[#e5e1d7]" : "hover:bg-white hover:shadow-sm active:bg-[#EDE6D9] text-[#101724] border border-transparent hover:border-[#e5e1d7]"
}`} }`}
> >
<span className={`font-medium tracking-[-0.1px] ${isActive ? "" : "group-hover:text-[#0f5a4a]"}`}> <span className={`font-medium tracking-[-0.1px] text-sm lg:text-base ${isActive ? "" : "group-hover:text-[#0f5a4a]"}`}>
{cat.name} {cat.name}
</span> </span>
<span className={`text-xs px-2.5 py-0.5 rounded-full font-mono tabular-nums ${ <span className={`text-[10px] lg:text-xs px-2 py-0.5 rounded-full font-mono tabular-nums ${
isActive ? "bg-white/20" : "bg-[#e5e1d7] text-[#68717f]" isActive ? "bg-white/20" : "bg-[#e5e1d7] text-[#68717f]"
}`}> }`}>
{itemCount} {itemCount}
@@ -128,7 +133,7 @@ export default function MenuPage() {
<div className="mb-8 flex flex-col md:flex-row gap-4 items-center"> <div className="mb-8 flex flex-col md:flex-row gap-4 items-center">
<input <input
type="text" type="text"
placeholder="Search dishes..." placeholder={t.menu.searchPlaceholder}
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="w-full md:w-80 rounded-2xl border border-[#e5e1d7] bg-white px-5 py-3 text-sm placeholder:text-[#8A8478] focus:border-[#c99a2e] focus:ring-2 focus:ring-[#c99a2e]/20 transition-all" className="w-full md:w-80 rounded-2xl border border-[#e5e1d7] bg-white px-5 py-3 text-sm placeholder:text-[#8A8478] focus:border-[#c99a2e] focus:ring-2 focus:ring-[#c99a2e]/20 transition-all"
@@ -136,13 +141,13 @@ export default function MenuPage() {
<button <button
onClick={() => setShowVegetarianOnly(!showVegetarianOnly)} onClick={() => setShowVegetarianOnly(!showVegetarianOnly)}
className={`px-6 py-3 rounded-2xl text-sm font-medium border transition-all ${ className={`px-6 py-3 rounded-2xl text-sm font-medium border transition-all active:scale-[0.985] ${
showVegetarianOnly showVegetarianOnly
? "bg-[#0f5a4a] text-white border-[#0f5a4a]" ? "bg-[#0f5a4a] text-white border-[#0f5a4a]"
: "border-[#e5e1d7] hover:border-[#c99a2e] text-[#101724] bg-white" : "border-[#e5e1d7] hover:border-[#c99a2e] text-[#101724] bg-white"
}`} }`}
> >
{showVegetarianOnly ? "Vegetarian Only" : "Show Vegetarian"} {showVegetarianOnly ? t.menu.vegetarianOnly : t.menu.showVegetarian}
</button> </button>
{(searchQuery || showVegetarianOnly || activeCategory !== "All") && ( {(searchQuery || showVegetarianOnly || activeCategory !== "All") && (
@@ -152,9 +157,9 @@ export default function MenuPage() {
setShowVegetarianOnly(false); setShowVegetarianOnly(false);
setActiveCategory("All"); setActiveCategory("All");
}} }}
className="text-sm font-medium text-[#c99a2e] hover:text-[#8f6b22]" className="text-sm font-medium text-[#c99a2e] hover:text-[#8f6b22] active:underline"
> >
Clear filters {t.menu.clearFilters}
</button> </button>
)} )}
</div> </div>
@@ -162,7 +167,7 @@ export default function MenuPage() {
{/* Menu Items */} {/* Menu Items */}
{filteredCategories.length === 0 ? ( {filteredCategories.length === 0 ? (
<div className="text-center py-16 text-[#6B665F]"> <div className="text-center py-16 text-[#6B665F]">
No dishes found matching your filters. {t.menu.noResults}
</div> </div>
) : ( ) : (
filteredCategories.map((category) => ( filteredCategories.map((category) => (
@@ -171,7 +176,7 @@ export default function MenuPage() {
<div className="text-3xl tracking-[-1px] text-[#101724]">{category.name}</div> <div className="text-3xl tracking-[-1px] text-[#101724]">{category.name}</div>
<div className="flex-1 h-px bg-gradient-to-r from-[#e5e1d7] to-transparent" /> <div className="flex-1 h-px bg-gradient-to-r from-[#e5e1d7] to-transparent" />
<div className="text-sm font-medium text-[#68717f] tracking-widest"> <div className="text-sm font-medium text-[#68717f] tracking-widest">
{category.items.length} dishes {category.items.length} {t.menu.dishes}
</div> </div>
</div> </div>
@@ -179,7 +184,7 @@ export default function MenuPage() {
{category.items.map((item) => ( {category.items.map((item) => (
<div <div
key={item.id} key={item.id}
className="menu-card group bg-white border border-[#EDE6D9] rounded-2xl overflow-hidden flex flex-col hover:border-[#c99a2e]/40 transition-all duration-300 cursor-pointer" className="menu-card group bg-white border border-[#EDE6D9] rounded-2xl overflow-hidden flex flex-col hover:border-[#c99a2e]/40 active:border-[#c99a2e] active:scale-[0.985] transition-all duration-150 cursor-pointer touch-manipulation"
onClick={() => addToCart({ id: item.id, name: item.name, price: item.price, image: item.image })} onClick={() => addToCart({ id: item.id, name: item.name, price: item.price, image: item.image })}
> >
{/* Media - Restored Premium Video Hover + Poster Logic */} {/* Media - Restored Premium Video Hover + Poster Logic */}
+13 -9
View File
@@ -233,13 +233,13 @@ export default function ShahiKitchenHomepage() {
<p className="max-w-md text-lg font-medium text-[#4b5563]">{t.signatureMenu.subtitle}</p> <p className="max-w-md text-lg font-medium text-[#4b5563]">{t.signatureMenu.subtitle}</p>
</div> </div>
{/* Filters */} {/* Filters - modern horizontal scroller on mobile for easy thumb use */}
<div className="mb-8 flex flex-wrap gap-2"> <div className="mb-8 -mx-1 px-1 flex gap-2 overflow-x-auto snap-x snap-mandatory pb-2 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
{["All", "Rice", "Curry", "Meat", "Street", "Roll", "Sweet"].map((cat) => ( {["All", "Rice", "Curry", "Meat", "Street", "Roll", "Sweet"].map((cat) => (
<button <button
key={cat} key={cat}
onClick={() => setMenuFilter(cat as any)} onClick={() => setMenuFilter(cat as any)}
className={`rounded-full px-6 py-3 text-sm font-bold transition-all ${menuFilter === cat ? "bg-[#101724] text-white" : "border border-[#e5e1d7] bg-white text-[#182235] hover:border-[#c99a2e] hover:bg-[#fff6dc]"}`} className={`flex-shrink-0 snap-start rounded-full px-5 py-2.5 text-sm font-bold transition-all active:scale-[0.97] touch-manipulation ${menuFilter === cat ? "bg-[#101724] text-white" : "border border-[#e5e1d7] bg-white text-[#182235] hover:border-[#c99a2e] hover:bg-[#fff6dc] active:bg-[#fff6dc]"}`}
> >
{cat} {cat}
</button> </button>
@@ -254,7 +254,7 @@ export default function ShahiKitchenHomepage() {
key={dish.id} key={dish.id}
layout layout
onClick={() => addDish(dish)} onClick={() => addDish(dish)}
className="signature-card group flex cursor-pointer flex-col overflow-hidden rounded-[2rem] border border-[#EDE6D9] bg-white" className="signature-card group flex cursor-pointer flex-col overflow-hidden rounded-[2rem] border border-[#EDE6D9] bg-white active:scale-[0.985] active:border-[#c99a2e]/60 transition-transform touch-manipulation"
> >
<div className="relative h-48 overflow-hidden bg-[#F2EDE4]"> <div className="relative h-48 overflow-hidden bg-[#F2EDE4]">
<img <img
@@ -277,7 +277,7 @@ export default function ShahiKitchenHomepage() {
<button <button
onClick={(e) => { e.stopPropagation(); addDish(dish); }} onClick={(e) => { e.stopPropagation(); addDish(dish); }}
className="mt-5 w-full py-3 text-sm tracking-[0.6px] border border-[#B38B4D] text-[#B38B4D] rounded-full hover:bg-[#B38B4D] hover:text-white font-medium transition-all" className="mt-5 w-full py-3.5 text-sm tracking-[0.6px] border border-[#B38B4D] text-[#B38B4D] rounded-full hover:bg-[#B38B4D] hover:text-white active:bg-[#8C6B3A] active:text-white active:scale-[0.985] font-medium transition-all touch-manipulation"
> >
{t.signatureMenu.addToTable} {t.signatureMenu.addToTable}
</button> </button>
@@ -299,16 +299,20 @@ export default function ShahiKitchenHomepage() {
<section id="experience" className="section bg-white border-y border-[#EDE6D9]"> <section id="experience" className="section bg-white border-y border-[#EDE6D9]">
<div className="max-w-6xl mx-auto px-6"> <div className="max-w-6xl mx-auto px-6">
<div className="text-center mb-14"> <div className="text-center mb-14">
<div className="text-[#B38B4D] text-xs tracking-[3px] mb-3">THE SHAHI WAY</div> <div className="text-[#B38B4D] text-xs tracking-[3px] mb-3">{language === 'sv' ? 'SHAHI-SÄTTET' : 'THE SHAHI WAY'}</div>
<h3 className="text-5xl md:text-6xl tracking-[-2px]">More than a meal.<br />A moment of royalty.</h3> <h3 className="text-5xl md:text-6xl tracking-[-2px]">{language === 'sv' ? 'Mer än en måltid.\nEtt ögonblick av kunglighet.' : 'More than a meal.\nA moment of royalty.'}</h3>
</div> </div>
<div className="grid md:grid-cols-3 gap-6"> <div className="grid md:grid-cols-3 gap-6">
{[ {(language === 'sv' ? [
{ title: "Den Legendariska Buffén", desc: "Vår berömda lunchbuffé har över 20 roterande rätter — curry, biryani, färsk naan och sötsaker." },
{ title: "Shahi Sötsaker", desc: "Hemgjord mithai dagligen. Från färsk jalebi till rasmalai — det perfekta söta avslutet." },
{ title: "Varm Gästfrihet", desc: "Oavsett om du är här för en snabb lunch eller familjefest, behandlas du alltid som kunglighet." },
] : [
{ title: "The Legendary Buffet", desc: "Our famous lunch buffet features over 20 rotating dishes — curries, biryanis, fresh naan, and sweets." }, { title: "The Legendary Buffet", desc: "Our famous lunch buffet features over 20 rotating dishes — curries, biryanis, fresh naan, and sweets." },
{ title: "Shahi Sweets", desc: "Homemade mithai made daily. From fresh Jalebi to Rasmalai — the perfect sweet ending." }, { title: "Shahi Sweets", desc: "Homemade mithai made daily. From fresh Jalebi to Rasmalai — the perfect sweet ending." },
{ title: "Warm Hospitality", desc: "Whether you're here for a quick lunch or a family celebration, you will always be treated like royalty." }, { title: "Warm Hospitality", desc: "Whether you're here for a quick lunch or a family celebration, you will always be treated like royalty." },
].map((item, index) => ( ]).map((item, index) => (
<div key={index} className="experience-card group relative border border-[#EDE6D9] p-9 rounded-2xl bg-[#F8F5F0] overflow-hidden"> <div key={index} className="experience-card group relative border border-[#EDE6D9] p-9 rounded-2xl bg-[#F8F5F0] overflow-hidden">
<div className="text-[#B38B4D] text-6xl font-light mb-9 tracking-[-2px]">0{index + 1}</div> <div className="text-[#B38B4D] text-6xl font-light mb-9 tracking-[-2px]">0{index + 1}</div>
<h4 className="text-[29px] tracking-[-0.8px] mb-5 text-[#2C2A26] leading-tight">{item.title}</h4> <h4 className="text-[29px] tracking-[-0.8px] mb-5 text-[#2C2A26] leading-tight">{item.title}</h4>
+15 -11
View File
@@ -15,8 +15,12 @@
*/ */
import Link from "next/link"; import Link from "next/link";
import { useLanguage } from "@/lib/language-context";
import { getTranslation } from "@/lib/translations";
export default function Footer() { export default function Footer() {
const { language } = useLanguage();
const t = getTranslation(language);
return ( return (
<footer className="bg-[#F5F1E9] border-t border-[#EDE6D9] pt-14 pb-10 text-[#6B665F]"> <footer className="bg-[#F5F1E9] border-t border-[#EDE6D9] pt-14 pb-10 text-[#6B665F]">
<div className="max-w-7xl mx-auto px-6"> <div className="max-w-7xl mx-auto px-6">
@@ -34,13 +38,13 @@ export default function Footer() {
<span className="text-[#2C2A26] font-medium tracking-[-0.3px]">Shahi Kitchen</span> <span className="text-[#2C2A26] font-medium tracking-[-0.3px]">Shahi Kitchen</span>
</div> </div>
<p className="text-sm leading-relaxed"> <p className="text-sm leading-relaxed">
Authentic Indian &amp; Pakistani cuisine in Gothenburg since 2016. {t.footer.tagline}
</p> </p>
</div> </div>
{/* Contact - Both Locations */} {/* Contact - Both Locations */}
<div> <div>
<div className="text-[#2C2A26] text-sm tracking-[1.5px] mb-4">OUR LOCATIONS</div> <div className="text-[#2C2A26] text-sm tracking-[1.5px] mb-4">{t.footer.locations}</div>
<div className="space-y-4 text-sm"> <div className="space-y-4 text-sm">
<div> <div>
<div className="font-medium text-[#2C2A26]">Shahi Kitchen (Askim / Sisjön)</div> <div className="font-medium text-[#2C2A26]">Shahi Kitchen (Askim / Sisjön)</div>
@@ -63,7 +67,7 @@ export default function Footer() {
{/* Hours */} {/* Hours */}
<div> <div>
<div className="text-[#2C2A26] text-sm tracking-[1.5px] mb-4">OPENING HOURS</div> <div className="text-[#2C2A26] text-sm tracking-[1.5px] mb-4">{language === 'sv' ? 'ÖPPETTIDER' : 'OPENING HOURS'}</div>
<div className="text-sm space-y-1"> <div className="text-sm space-y-1">
<div><span className="font-medium">Askim:</span> MonSun 11:0021:00</div> <div><span className="font-medium">Askim:</span> MonSun 11:0021:00</div>
<div><span className="font-medium">Backaplan:</span> MonSun 11:0021:00</div> <div><span className="font-medium">Backaplan:</span> MonSun 11:0021:00</div>
@@ -72,15 +76,15 @@ export default function Footer() {
{/* Quick Links + Social */} {/* Quick Links + Social */}
<div> <div>
<div className="text-[#2C2A26] text-sm tracking-[1.5px] mb-4">EXPLORE</div> <div className="text-[#2C2A26] text-sm tracking-[1.5px] mb-4">{t.footer.explore}</div>
<div className="flex flex-col gap-1.5 text-sm mb-8"> <div className="flex flex-col gap-1.5 text-sm mb-8">
<Link href="/" className="hover:text-[#B38B4D] transition-colors">Home</Link> <Link href="/" className="hover:text-[#B38B4D] transition-colors">{t.nav.home}</Link>
<Link href="/menu" className="hover:text-[#B38B4D] transition-colors">Menu</Link> <Link href="/menu" className="hover:text-[#B38B4D] transition-colors">{t.nav.menu}</Link>
<Link href="/#experience" className="hover:text-[#B38B4D] transition-colors">Our Experience</Link> <Link href="/#experience" className="hover:text-[#B38B4D] transition-colors">{language === 'sv' ? 'Vår Upplevelse' : 'Our Experience'}</Link>
<Link href="/#contact" className="hover:text-[#B38B4D] transition-colors">Contact &amp; Reserve</Link> <Link href="/#contact" className="hover:text-[#B38B4D] transition-colors">{language === 'sv' ? 'Kontakt &amp; Boka' : 'Contact &amp; Reserve'}</Link>
</div> </div>
<div className="text-[#2C2A26] text-sm tracking-[1.5px] mb-3">FOLLOW US</div> <div className="text-[#2C2A26] text-sm tracking-[1.5px] mb-3">{t.footer.follow}</div>
<div className="flex gap-5 text-sm"> <div className="flex gap-5 text-sm">
<a <a
href="https://www.instagram.com/Shahikitchen/" href="https://www.instagram.com/Shahikitchen/"
@@ -103,8 +107,8 @@ export default function Footer() {
</div> </div>
<div className="mt-14 pt-8 border-t border-[#EDE6D9] text-xs tracking-widest flex flex-col md:flex-row md:items-center justify-between gap-y-2 text-[#8A8478]"> <div className="mt-14 pt-8 border-t border-[#EDE6D9] text-xs tracking-widest flex flex-col md:flex-row md:items-center justify-between gap-y-2 text-[#8A8478]">
<div>© {new Date().getFullYear()} SHAHI KITCHEN GOTHENBURG. ALL RIGHTS RESERVED.</div> <div>© {new Date().getFullYear()} SHAHI KITCHEN GÖTEBORG. ALL RIGHTS RESERVED.</div>
<div>Made with tradition and heart.</div> <div>{language === 'sv' ? 'Tillagat med tradition och hjärta.' : 'Made with tradition and heart.'}</div>
</div> </div>
</div> </div>
</footer> </footer>
+109 -58
View File
@@ -3,11 +3,11 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Link from "next/link"; import Link from "next/link";
import { useCart } from "./CartContext"; import { useCart } from "./CartContext";
import { ShoppingBag, ArrowRight } from "lucide-react"; import { ShoppingBag, ArrowRight, Home, UtensilsCrossed, MapPin, Star, Phone, X } from "lucide-react";
import LanguageSwitcher from "./LanguageSwitcher"; import LanguageSwitcher from "./LanguageSwitcher";
import { useLanguage } from "@/lib/language-context"; import { useLanguage } from "@/lib/language-context";
import { getTranslation } from "@/lib/translations"; import { getTranslation } from "@/lib/translations";
import { motion } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
/** /**
@@ -147,19 +147,17 @@ export default function Navbar({ variant = "default" }: NavbarProps) {
</Link> </Link>
</div> </div>
{/* Mobile Hamburger */} {/* Mobile Hamburger + Cart (compact, high touch target) */}
<div className="md:hidden flex items-center gap-3"> <div className="md:hidden flex items-center gap-2">
{/* MOBILE CART ICON (always visible even when hamburger is closed) */} {/* MOBILE CART ICON (always visible) */}
<button <button
onClick={openCart} onClick={openCart}
className="relative flex items-center justify-center w-9 h-9 rounded-full hover:bg-[#F5F1E9] transition-colors" className="relative flex items-center justify-center w-10 h-10 rounded-full hover:bg-[#F5F1E9] active:bg-[#EDE6D9] transition-colors"
aria-label="Open cart" aria-label="Open cart"
> >
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <ShoppingBag className="h-5 w-5 text-[#101724]" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" />
</svg>
{totalItems > 0 && ( {totalItems > 0 && (
<span className="absolute -top-1 -right-1 bg-[#B38B4D] text-white text-[10px] font-medium min-w-[16px] h-[16px] rounded-full flex items-center justify-center px-1"> <span className="absolute -top-1 -right-1 bg-[#B38B4D] text-white text-[10px] font-bold min-w-[18px] h-[18px] rounded-full flex items-center justify-center px-1 tabular-nums">
{totalItems} {totalItems}
</span> </span>
)} )}
@@ -167,69 +165,122 @@ export default function Navbar({ variant = "default" }: NavbarProps) {
<button <button
onClick={() => setIsOpen(!isOpen)} onClick={() => setIsOpen(!isOpen)}
className="text-[#B38B4D] p-2 -mr-2" className="text-[#B38B4D] p-2 -mr-1 active:text-[#8C6B3A] transition-colors"
aria-label="Toggle menu" aria-label="Toggle menu"
aria-expanded={isOpen}
> >
<div className="space-y-1.5"> <div className="space-y-1.5">
<span className={`block h-px w-6 bg-current transition-all ${isOpen ? "rotate-45 translate-y-1.5" : ""}`} /> <span className={`block h-px w-6 bg-current transition-all duration-200 ${isOpen ? "rotate-45 translate-y-1.5" : ""}`} />
<span className={`block h-px w-6 bg-current transition-all ${isOpen ? "opacity-0" : ""}`} /> <span className={`block h-px w-6 bg-current transition-all duration-200 ${isOpen ? "opacity-0" : ""}`} />
<span className={`block h-px w-6 bg-current transition-all ${isOpen ? "-rotate-45 -translate-y-1.5" : ""}`} /> <span className={`block h-px w-6 bg-current transition-all duration-200 ${isOpen ? "-rotate-45 -translate-y-1.5" : ""}`} />
</div> </div>
</button> </button>
</div> </div>
</div> </div>
{/* Mobile Menu - Stunning Elegant Drawer */} {/* Modern Mobile Menu Drawer (slide-in, animated, high-contrast, touch-friendly) */}
{isOpen && ( <AnimatePresence>
<div className="md:hidden fixed inset-0 z-[60] bg-[#101724]/60 backdrop-blur-md"> {isOpen && (
<div className="ml-auto h-full w-[82%] max-w-[320px] bg-[#fbf7ef] p-8 shadow-2xl border-l border-[#c99a2e]/10"> <div className="md:hidden fixed inset-0 z-[65]">
<div className="flex items-center justify-between mb-10"> {/* Backdrop */}
<div className="flex items-center gap-3"> <motion.div
<img className="absolute inset-0 bg-[#101724]/70 backdrop-blur-md"
src="/images/logo/logo1.png" initial={{ opacity: 0 }}
alt="Shahi Kitchen" animate={{ opacity: 1 }}
className="h-12 w-12 rounded-xl object-contain" exit={{ opacity: 0 }}
/> transition={{ duration: 0.15 }}
<span className="font-serif text-xl text-[#101724]">Shahi Kitchen</span> onClick={closeMenu}
</div> />
<button onClick={closeMenu} className="grid h-10 w-10 place-items-center rounded-full bg-[#f3f5f7] text-[#101724]">
<span className="text-2xl leading-none">×</span>
</button>
</div>
<div className="flex flex-col gap-5 text-xl font-medium text-[#101724]"> {/* Sliding Panel - modern, full-bleed on small phones, elegant on larger */}
{navLinks.map((link) => ( <motion.div
<Link className="absolute right-0 top-0 bottom-0 w-[82%] max-w-[340px] bg-[#fbf7ef] shadow-2xl border-l border-[#c99a2e]/10 flex flex-col overflow-y-auto"
key={link.href} initial={{ x: '100%' }}
href={link.href} animate={{ x: 0 }}
exit={{ x: '100%' }}
transition={{ type: 'spring', stiffness: 320, damping: 32 }}
>
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-[#EDE6D9]">
<div className="flex items-center gap-3">
<img
src="/images/logo/logo1.png"
alt="Shahi Kitchen"
className="h-10 w-10 rounded-xl object-contain"
/>
<div>
<div className="font-serif text-[19px] leading-none text-[#101724]">Shahi Kitchen</div>
<div className="text-[10px] uppercase tracking-[1.5px] text-[#8a6a25] -mt-0.5">Gothenburg</div>
</div>
</div>
<button
onClick={closeMenu} onClick={closeMenu}
className="py-1 border-b border-[#e5e1d7] pb-4 hover:text-[#0f5a4a] transition-colors" className="w-10 h-10 flex items-center justify-center rounded-full bg-white/70 active:bg-[#EDE6D9] text-[#101724] transition-colors"
aria-label="Close menu"
> >
{link.label} <X className="h-5 w-5" />
</button>
</div>
{/* Primary Nav Links - modern list with icons + active state for visibility */}
<div className="px-3 py-4">
{navLinks.map((link) => {
const active = isActive(link.href);
const Icon =
link.href === '/' ? Home :
link.href === '/menu' ? UtensilsCrossed :
link.href === '/locations' ? MapPin :
link.href.includes('experience') ? Star : Phone;
return (
<Link
key={link.href}
href={link.href}
onClick={closeMenu}
className={`flex items-center gap-4 px-4 py-3.5 mx-1 my-0.5 rounded-2xl text-[17px] font-medium transition-all active:scale-[0.985] ${
active
? 'bg-[#101724] text-white shadow-sm'
: 'text-[#101724] hover:bg-white active:bg-[#EDE6D9]'
}`}
>
<Icon className={`h-5 w-5 flex-shrink-0 ${active ? 'text-[#c99a2e]' : 'text-[#B38B4D]'}`} />
<span>{link.label}</span>
{active && (
<span className="ml-auto text-xs tracking-widest opacity-70">CURRENT</span>
)}
</Link>
);
})}
</div>
{/* Secondary actions + Language */}
<div className="mt-auto px-5 pb-8 pt-4 border-t border-[#EDE6D9] bg-white/40 space-y-3">
<div className="px-1">
<div className="text-[10px] uppercase tracking-[2px] text-[#8a6a25] mb-1.5 px-1">LANGUAGE</div>
<LanguageSwitcher />
</div>
<Link
href="/#contact"
onClick={closeMenu}
className="block w-full rounded-2xl bg-gradient-to-r from-[#c99a2e] via-[#d4a73d] to-[#c99a2e] py-4 text-center text-[15px] font-bold text-[#241806] shadow active:scale-[0.985] transition-transform"
>
{t.reserve}
</Link> </Link>
))}
</div>
<div className="mt-10 space-y-3"> <button
<LanguageSwitcher /> onClick={() => { openCart(); closeMenu(); }}
className="block w-full rounded-2xl border-2 border-[#c99a2e]/40 bg-white py-4 text-[15px] font-semibold text-[#101724] active:bg-[#F5F1E9] active:border-[#c99a2e] transition-all"
>
{t.cart} {totalItems > 0 && `(${totalItems})`}
</button>
<Link <p className="text-center text-[11px] text-[#8A8478] pt-1">Tap to open WhatsApp for orders &amp; bookings</p>
href="/#contact" </div>
onClick={closeMenu} </motion.div>
className="block w-full rounded-full bg-gradient-to-r from-[#c99a2e] to-[#d4a73d] py-4 text-center text-base font-bold text-[#241806] shadow-lg"
>
{t.reserve}
</Link>
<button
onClick={() => { openCart(); closeMenu(); }}
className="block w-full rounded-full border border-[#c99a2e]/40 bg-white py-4 text-base font-semibold text-[#101724]"
>
{t.cart} {totalItems > 0 && `(${totalItems})`}
</button>
</div>
</div> </div>
</div> )}
)} </AnimatePresence>
</nav> </nav>
); );
} }
+4 -2
View File
@@ -13,7 +13,7 @@ const LanguageContext = createContext<LanguageContextType | undefined>(undefined
const LANGUAGE_STORAGE_KEY = 'shahi-kitchen-language'; const LANGUAGE_STORAGE_KEY = 'shahi-kitchen-language';
export function LanguageProvider({ children }: { children: ReactNode }) { export function LanguageProvider({ children }: { children: ReactNode }) {
const [language, setLanguageState] = useState<Language>('en'); const [language, setLanguageState] = useState<Language>('sv');
// Load language from localStorage on mount // Load language from localStorage on mount
useEffect(() => { useEffect(() => {
@@ -21,11 +21,13 @@ export function LanguageProvider({ children }: { children: ReactNode }) {
if (savedLang && ['en', 'sv', 'hi', 'ur'].includes(savedLang)) { if (savedLang && ['en', 'sv', 'hi', 'ur'].includes(savedLang)) {
setLanguageState(savedLang); setLanguageState(savedLang);
} else { } else {
// Optional: Try to detect browser language // Default to Swedish (sv) for the Swedish restaurant audience.
// Only auto-switch for strong browser matches on other supported languages.
const browserLang = navigator.language.toLowerCase(); const browserLang = navigator.language.toLowerCase();
if (browserLang.startsWith('sv')) setLanguageState('sv'); if (browserLang.startsWith('sv')) setLanguageState('sv');
else if (browserLang.startsWith('hi')) setLanguageState('hi'); else if (browserLang.startsWith('hi')) setLanguageState('hi');
else if (browserLang.startsWith('ur')) setLanguageState('ur'); else if (browserLang.startsWith('ur')) setLanguageState('ur');
// else stay 'sv' (no 'en' override — users can manually switch to English)
} }
}, []); }, []);
+49 -1
View File
@@ -1,8 +1,8 @@
export type Language = 'en' | 'sv' | 'hi' | 'ur'; export type Language = 'en' | 'sv' | 'hi' | 'ur';
export const languages: { code: Language; name: string; native: string; flag: string }[] = [ export const languages: { code: Language; name: string; native: string; flag: string }[] = [
{ code: 'en', name: 'English', native: 'English', flag: '🇬🇧' },
{ code: 'sv', name: 'Swedish', native: 'Svenska', flag: '🇸🇪' }, { code: 'sv', name: 'Swedish', native: 'Svenska', flag: '🇸🇪' },
{ code: 'en', name: 'English', native: 'English', flag: '🇬🇧' },
{ code: 'hi', name: 'Hindi', native: 'हिंदी', flag: '🇮🇳' }, { code: 'hi', name: 'Hindi', native: 'हिंदी', flag: '🇮🇳' },
{ code: 'ur', name: 'Urdu', native: 'اردو', flag: '🇵🇰' }, { code: 'ur', name: 'Urdu', native: 'اردو', flag: '🇵🇰' },
]; ];
@@ -78,6 +78,18 @@ export const translations = {
// Common // Common
add: 'Add', add: 'Add',
viewCart: 'View Cart', viewCart: 'View Cart',
// Full Menu Page
menu: {
title: 'Our Menu',
subtitle: 'Traditional recipes. Generous portions. Made with heart.',
searchPlaceholder: 'Search dishes...',
showVegetarian: 'Show Vegetarian',
vegetarianOnly: 'Vegetarian Only',
clearFilters: 'Clear filters',
noResults: 'No dishes found matching your filters.',
dishes: 'dishes',
},
}, },
sv: { sv: {
@@ -143,6 +155,18 @@ export const translations = {
add: 'Lägg till', add: 'Lägg till',
viewCart: 'Visa Varukorg', viewCart: 'Visa Varukorg',
// Full Menu Page
menu: {
title: 'Vår Meny',
subtitle: 'Traditionella recept. Generösa portioner. Tillagade med hjärta.',
searchPlaceholder: 'Sök rätter...',
showVegetarian: 'Visa vegetariskt',
vegetarianOnly: 'Endast vegetariskt',
clearFilters: 'Rensa filter',
noResults: 'Inga rätter matchar dina filter.',
dishes: 'rätter',
},
}, },
hi: { hi: {
@@ -177,6 +201,18 @@ export const translations = {
viewFullMenu: 'पूरी मेन्यू देखें — 40+ व्यंजन', viewFullMenu: 'पूरी मेन्यू देखें — 40+ व्यंजन',
}, },
// Full Menu Page (fallback to English for now)
menu: {
title: 'Our Menu',
subtitle: 'Traditional recipes. Generous portions. Made with heart.',
searchPlaceholder: 'Search dishes...',
showVegetarian: 'Show Vegetarian',
vegetarianOnly: 'Vegetarian Only',
clearFilters: 'Clear filters',
noResults: 'No dishes found matching your filters.',
dishes: 'dishes',
},
experience: { experience: {
badge: 'शाही तरीका', badge: 'शाही तरीका',
title: 'दावत जैसी गर्माहट।\nमहल जैसा सुकून।', title: 'दावत जैसी गर्माहट।\nमहल जैसा सुकून।',
@@ -242,6 +278,18 @@ export const translations = {
viewFullMenu: 'مکمل مینو دیکھیں — 40+ پکوان', viewFullMenu: 'مکمل مینو دیکھیں — 40+ پکوان',
}, },
// Full Menu Page (fallback to English for now)
menu: {
title: 'Our Menu',
subtitle: 'Traditional recipes. Generous portions. Made with heart.',
searchPlaceholder: 'Search dishes...',
showVegetarian: 'Show Vegetarian',
vegetarianOnly: 'Vegetarian Only',
clearFilters: 'Clear filters',
noResults: 'No dishes found matching your filters.',
dishes: 'dishes',
},
experience: { experience: {
badge: 'شاہی طریقہ', badge: 'شاہی طریقہ',
title: 'دعوت جیسی گرمی۔\nمحل جیسا سکون۔', title: 'دعوت جیسی گرمی۔\nمحل جیسا سکون۔',
Binary file not shown.
Binary file not shown.
Binary file not shown.