- To get started, edit the page.tsx file. -
-- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -
-diff --git a/README.md b/README.md index e215bc4..f7a90b0 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,75 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# 🌙 Luna Cocktail & More — QR Menü -## Getting Started +**Luna Cocktail and More** için tasarlanmış premium dijital QR menü uygulaması. -First, run the development server: +## ✨ Özellikler + +- 🎨 **Premium Dark Theme** — Altın aksanlı, lüks bar atmosferi +- 📱 **Tam Responsive** — Mobil öncelikli tasarım, QR kod ile erişime optimize +- 🧭 **Sticky Kategori Navigasyonu** — Yatay kaydırmalı, aktif kategori takibi +- ⭐ **Animasyonlu Hero** — Yıldız efektli, logo entegrasyonlu açılış ekranı +- 🍸 **Akıllı Fiyat Gösterimi** — Tek/Dbl, Kadeh/Şişe formatlarını otomatik algılar +- 🏷️ **Tat Profili Etiketleri** — Kokteyllerin tat profilini görsel etiketlerle sunar +- 🔝 **Yukarı Dön Butonu** — Uzun menüde kolay navigasyon +- 📊 **JSON Tabanlı Menü** — `menu/menu.json` dosyasını düzenleyerek menüyü güncelleyin + +## 🛠️ Teknolojiler + +- **Next.js 16** — React framework +- **TypeScript** — Tip güvenli geliştirme +- **Tailwind CSS 4** — Utility-first CSS +- **Google Fonts** — Playfair Display, Inter, Outfit + +## 🚀 Kurulum ```bash +# Bağımlılıkları yükle +npm install + +# Geliştirme sunucusunu başlat npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev + +# Production build +npm run build +npm start ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +## 📂 Proje Yapısı -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +``` +lunaqrmenu/ +├── app/ +│ ├── globals.css # Tüm stiller +│ ├── layout.tsx # Root layout & meta +│ └── page.tsx # Ana menü sayfası +├── menu/ +│ └── menu.json # Menü verileri +├── public/ +│ ├── logo.jpg # Yuvarlak logo +│ ├── logo.png # Menü kapak görseli +│ └── logo1.png # Hero logosu +└── package.json +``` -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +## 📝 Menü Güncelleme -## Learn More +`menu/menu.json` dosyasını düzenleyerek menüyü güncelleyebilirsiniz. Desteklenen fiyat formatları: -To learn more about Next.js, take a look at the following resources: +```json +// Basit fiyat +"price": "590 tl" -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +// Tek/Dbl fiyat +"price": { "single": "370 tl", "double": "560 tl" } -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +// Kadeh/Şişe fiyat +"price": { "glass": "240 tl", "bottle": "1200 tl" } +``` -## Deploy on Vercel +## 📱 QR Kod -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +Sitenizi yayınladıktan sonra URL'den QR kod oluşturarak masalara yerleştirebilirsiniz. -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +--- + +**Luna Cocktail & More** ☽ *Follow us [@lunacocktaill](https://instagram.com/lunacocktaill)* diff --git a/app/globals.css b/app/globals.css index a2dc41e..4cb043e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,10 +1,5 @@ @import "tailwindcss"; -:root { - --background: #ffffff; - --foreground: #171717; -} - @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); @@ -12,15 +7,655 @@ --font-mono: var(--font-geist-mono); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } +:root { + --background: #0a0a0f; + --foreground: #e8e4dc; + --gold: #c9a96e; + --gold-light: #dfc594; + --gold-dark: #a68544; + --surface: #121218; + --surface-elevated: #1a1a24; + --border-subtle: rgba(201, 169, 110, 0.12); + --border-glow: rgba(201, 169, 110, 0.25); + --text-secondary: #8a8690; + --text-muted: #5a5660; + --accent-purple: #7c5cbf; + --accent-rose: #b44a65; + --accent-teal: #4a9b8e; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + scroll-behavior: smooth; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: 'Inter', sans-serif; + min-height: 100vh; + overflow-x: hidden; } + +/* ===== SCROLLBAR ===== */ +::-webkit-scrollbar { + width: 6px; +} + +::-webkit-scrollbar-track { + background: var(--background); +} + +::-webkit-scrollbar-thumb { + background: var(--gold-dark); + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--gold); +} + +/* ===== HERO ===== */ +.hero { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + min-height: 100vh; + min-height: 100dvh; + padding: 2rem 1rem; + text-align: center; + overflow: hidden; +} + +.hero::before { + content: ''; + position: absolute; + inset: 0; + background: + radial-gradient(ellipse 600px 400px at 50% 30%, rgba(201, 169, 110, 0.06) 0%, transparent 70%), + radial-gradient(ellipse 400px 300px at 20% 80%, rgba(124, 92, 191, 0.04) 0%, transparent 70%), + radial-gradient(ellipse 400px 300px at 80% 70%, rgba(180, 74, 101, 0.04) 0%, transparent 70%); + pointer-events: none; +} + +.hero-moon { + position: absolute; + top: 8%; + right: 15%; + width: 120px; + height: 120px; + border-radius: 50%; + background: radial-gradient(circle at 35% 35%, rgba(201, 169, 110, 0.15), rgba(201, 169, 110, 0.03)); + box-shadow: + 0 0 60px rgba(201, 169, 110, 0.08), + 0 0 120px rgba(201, 169, 110, 0.04); + animation: moonPulse 6s ease-in-out infinite; + pointer-events: none; +} + +@keyframes moonPulse { + + 0%, + 100% { + opacity: 0.6; + transform: scale(1); + } + + 50% { + opacity: 1; + transform: scale(1.05); + } +} + +.hero-stars { + position: absolute; + inset: 0; + pointer-events: none; + overflow: hidden; +} + +.star { + position: absolute; + width: 2px; + height: 2px; + background: var(--gold-light); + border-radius: 50%; + animation: twinkle var(--duration) ease-in-out infinite; + opacity: 0; +} + +@keyframes twinkle { + + 0%, + 100% { + opacity: 0; + transform: scale(0.5); + } + + 50% { + opacity: var(--max-opacity); + transform: scale(1); + } +} + +.hero-brand { + position: relative; + z-index: 2; + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + animation: fadeSlideUp 1.2s ease-out; +} + +.hero-illustration { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 0; + opacity: 0.04; + pointer-events: none; +} + +.hero-illustration img { + width: auto; + height: 85%; + max-height: 90vh; + object-fit: contain; + filter: invert(1); +} + +.hero-logo { + width: 220px; + height: 220px; + border-radius: 50%; + object-fit: cover; + border: 2px solid rgba(201, 169, 110, 0.2); + box-shadow: + 0 0 40px rgba(201, 169, 110, 0.1), + 0 0 80px rgba(201, 169, 110, 0.05); + filter: invert(1); + transition: transform 0.4s ease, box-shadow 0.4s ease; +} + +.hero-logo:hover { + transform: scale(1.05); + box-shadow: + 0 0 50px rgba(201, 169, 110, 0.15), + 0 0 100px rgba(201, 169, 110, 0.08); +} + +@media (max-width: 480px) { + .hero-logo { + width: 160px; + height: 160px; + } +} + +.hero-title { + font-family: 'Playfair Display', serif; + font-size: clamp(2.8rem, 8vw, 5rem); + font-weight: 500; + line-height: 1.1; + color: #fff; + margin-bottom: 0.5rem; + background: linear-gradient(135deg, #fff 0%, var(--gold-light) 50%, #fff 100%); + background-size: 200% auto; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: shimmerTitle 6s ease-in-out infinite; +} + +@keyframes shimmerTitle { + + 0%, + 100% { + background-position: 0% center; + } + + 50% { + background-position: 200% center; + } +} + +.hero-subtitle { + font-family: 'Inter', sans-serif; + font-size: 0.85rem; + font-weight: 300; + letter-spacing: 0.4em; + color: var(--text-secondary); + margin-top: 0.75rem; +} + +.hero-divider { + width: 60px; + height: 1px; + background: linear-gradient(90deg, transparent, var(--gold), transparent); + margin: 2rem auto; + opacity: 0.6; +} + +.hero-scroll-cta { + position: absolute; + bottom: 2rem; + left: 0; + right: 0; + margin: 0 auto; + width: fit-content; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.75rem; + color: var(--text-muted); + font-size: 0.7rem; + letter-spacing: 0.2em; + animation: fadeSlideUp 1.5s ease-out 0.5s both; + cursor: pointer; + transition: color 0.3s; + text-decoration: none; +} + +.hero-scroll-cta:hover { + color: var(--gold); +} + +.scroll-arrow { + width: 20px; + height: 20px; + border-right: 1px solid var(--gold-dark); + border-bottom: 1px solid var(--gold-dark); + transform: rotate(45deg); + animation: bounceArrow 2s ease-in-out infinite; +} + +@keyframes bounceArrow { + + 0%, + 100% { + transform: rotate(45deg) translate(0, 0); + opacity: 0.4; + } + + 50% { + transform: rotate(45deg) translate(4px, 4px); + opacity: 1; + } +} + +@keyframes fadeSlideUp { + from { + opacity: 0; + transform: translateY(30px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +/* ===== CATEGORY NAVIGATION ===== */ +.category-nav { + position: sticky; + top: 0; + z-index: 100; + background: rgba(10, 10, 15, 0.85); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border-bottom: 1px solid var(--border-subtle); + padding: 0; + transition: box-shadow 0.3s; +} + +.category-nav.scrolled { + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.5); +} + +.category-nav-inner { + display: flex; + align-items: center; + gap: 0; + overflow-x: auto; + scrollbar-width: none; + -ms-overflow-style: none; + padding: 0 1rem; + max-width: 900px; + margin: 0 auto; +} + +.category-nav-inner::-webkit-scrollbar { + display: none; +} + +.category-nav-btn { + flex-shrink: 0; + padding: 1rem 1.25rem; + font-family: 'Inter', sans-serif; + font-size: 0.7rem; + font-weight: 500; + letter-spacing: 0.15em; + color: var(--text-muted); + background: none; + border: none; + cursor: pointer; + transition: color 0.3s, border-color 0.3s; + border-bottom: 2px solid transparent; + white-space: nowrap; + position: relative; +} + +.category-nav-btn:hover { + color: var(--foreground); +} + +.category-nav-btn.active { + color: var(--gold); + border-bottom-color: var(--gold); +} + +/* ===== MENU SECTION ===== */ +.menu-container { + max-width: 800px; + margin: 0 auto; + padding: 0 1.5rem 4rem; +} + +.category-section { + padding-top: 3rem; + margin-bottom: 2rem; + animation: fadeIn 0.6s ease-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +.category-header { + text-align: center; + margin-bottom: 2.5rem; + position: relative; +} + +.category-title { + font-family: 'Playfair Display', serif; + font-size: clamp(1.6rem, 4vw, 2.2rem); + font-weight: 500; + color: #fff; + letter-spacing: 0.05em; + position: relative; + display: inline-block; +} + +.category-title::after { + content: ''; + display: block; + width: 40px; + height: 1px; + background: var(--gold); + margin: 0.75rem auto 0; + opacity: 0.5; +} + +.category-item-count { + font-family: 'Inter', sans-serif; + font-size: 0.7rem; + font-weight: 400; + color: var(--text-muted); + letter-spacing: 0.15em; + margin-top: 0.5rem; +} + +/* ===== MENU ITEMS ===== */ +.menu-items { + display: flex; + flex-direction: column; + gap: 0; +} + +.menu-item { + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: 1.25rem 0; + border-bottom: 1px solid var(--border-subtle); + transition: background 0.3s, border-color 0.3s; + gap: 1rem; +} + +.menu-item:last-child { + border-bottom: none; +} + +.menu-item:hover { + border-color: var(--border-glow); +} + +.item-info { + flex: 1; + min-width: 0; +} + +.item-name { + font-family: 'Outfit', sans-serif; + font-size: 1.05rem; + font-weight: 500; + color: #fff; + margin-bottom: 0.3rem; + transition: color 0.3s; +} + +.menu-item:hover .item-name { + color: var(--gold-light); +} + +.item-meta { + display: flex; + flex-direction: column; + gap: 0.2rem; +} + +.item-ingredients { + font-family: 'Inter', sans-serif; + font-size: 0.78rem; + font-weight: 300; + color: var(--text-secondary); + line-height: 1.5; +} + +.item-taste-profile { + display: inline-flex; + flex-wrap: wrap; + gap: 0.35rem; + margin-top: 0.3rem; +} + +.taste-tag { + font-family: 'Inter', sans-serif; + font-size: 0.65rem; + font-weight: 500; + letter-spacing: 0.08em; + padding: 0.2rem 0.55rem; + border-radius: 100px; + background: rgba(201, 169, 110, 0.08); + color: var(--gold); + border: 1px solid rgba(201, 169, 110, 0.15); +} + +.item-grape { + font-family: 'Inter', sans-serif; + font-size: 0.75rem; + font-weight: 400; + color: var(--accent-purple); + font-style: italic; + opacity: 0.85; +} + +.item-price-area { + flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.2rem; +} + +.item-price { + font-family: 'Outfit', sans-serif; + font-size: 1rem; + font-weight: 600; + color: var(--gold); + white-space: nowrap; +} + +.item-price-multi { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.15rem; +} + +.price-label { + font-family: 'Inter', sans-serif; + font-size: 0.65rem; + font-weight: 400; + letter-spacing: 0.1em; + color: var(--text-muted); +} + +.price-row { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.price-row .item-price { + font-size: 0.9rem; +} + +/* ===== FOOTER ===== */ +.menu-footer { + text-align: center; + padding: 3rem 1.5rem 2rem; + border-top: 1px solid var(--border-subtle); + max-width: 600px; + margin: 0 auto; +} + +.footer-note { + font-family: 'Inter', sans-serif; + font-size: 0.78rem; + font-weight: 300; + color: var(--text-muted); + line-height: 1.7; + font-style: italic; +} + +.footer-brand { + margin-top: 2rem; + font-family: 'Playfair Display', serif; + font-size: 1rem; + color: var(--gold-dark); + opacity: 0.5; + letter-spacing: 0.1em; +} + +/* ===== ANIMATIONS ===== */ +.reveal { + opacity: 0; + transform: translateY(20px); + transition: opacity 0.6s ease-out, transform 0.6s ease-out; +} + +.reveal.visible { + opacity: 1; + transform: translateY(0); +} + +/* ===== RESPONSIVE ===== */ +@media (max-width: 480px) { + .hero-moon { + width: 80px; + height: 80px; + top: 12%; + right: 10%; + } + + .category-nav-btn { + padding: 0.85rem 1rem; + font-size: 0.65rem; + } + + .menu-container { + padding: 0 1rem 3rem; + } + + .menu-item { + padding: 1rem 0; + } + + .item-name { + font-size: 0.95rem; + } + + .item-price { + font-size: 0.9rem; + } +} + +/* ===== BACK TO TOP ===== */ +.back-to-top { + position: fixed; + bottom: 2rem; + right: 2rem; + width: 44px; + height: 44px; + border-radius: 50%; + background: rgba(201, 169, 110, 0.12); + border: 1px solid rgba(201, 169, 110, 0.2); + color: var(--gold); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + opacity: 0; + visibility: hidden; + transform: translateY(10px); + transition: all 0.3s ease; + z-index: 50; + backdrop-filter: blur(10px); +} + +.back-to-top.visible { + opacity: 1; + visibility: visible; + transform: translateY(0); +} + +.back-to-top:hover { + background: rgba(201, 169, 110, 0.2); + border-color: var(--gold); + transform: translateY(-2px); +} + +.back-to-top svg { + width: 18px; + height: 18px; +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index 976eb90..91cebcb 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,20 +1,16 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Luna Cocktail & More | Menü", + description: + "Luna Cocktail and More - Özel kokteyllerimiz, şaraplarımız ve daha fazlası. QR menü ile keşfedin.", + keywords: "cocktail, kokteyl, bar, menü, luna, drinks", + openGraph: { + title: "Luna Cocktail & More | Menü", + description: "Özel kokteyllerimiz, şaraplarımız ve daha fazlası.", + type: "website", + }, }; export default function RootLayout({ @@ -23,11 +19,20 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - -
{children} + + + + + + + {children} ); } diff --git a/app/page.tsx b/app/page.tsx index 3f36f7c..e3f40a0 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,65 +1,324 @@ -import Image from "next/image"; +"use client"; + +import { useEffect, useState, useRef, useCallback } from "react"; +import Image from "next/image"; +import menuData from "../menu/menu.json"; + +/* ────── Helpers ────── */ +interface MenuItem { + name: string; + ingredients?: string; + taste_profile?: string; + grape_variety?: string; + price: + | string + | { single?: string; double?: string; glass?: string; bottle?: string }; +} + +interface Category { + id: string; + title: string; + items: MenuItem[]; +} + +interface MenuData { + restaurant_name: string; + footer_note: string; + categories: Category[]; +} + +const data = menuData as MenuData; + +/* ────── Stars component ────── */ +interface Star { + id: number; + left: string; + top: string; + duration: string; + delay: string; + maxOpacity: number; +} + +function HeroStars() { + const [stars, setStars] = useState- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -
-