Files
kozmos-qr-menu/docs/superpowers/specs/2026-06-03-kozmos-qr-menu-design.md
T
2026-06-03 01:36:14 +03:00

5.8 KiB
Raw Blame History

Kozmos Beach & More — QR Menü Sitesi Tasarım Dokümanı

Tarih: 2026-06-03
Proje: kozmosqr
Stack: Next.js 16.2.7 · React 19.2 · Tailwind CSS v4 · TypeScript


Amaç

Masa başında QR okutulunca açılan, uygulama gerektirmeyen, sadece görüntüleme amaçlı dijital menü. TR/EN dil desteği, mobil-first, PWA-ready.


Tasarım Kararları (Brainstorming Özeti)

Karar Seçim Gerekçe
Ürün kartı stili Minimal liste (emoji + isim + açıklama + fiyat) Hızlı taranır, fotoğraf zorunluluğu yok
Kategori navigasyon Sticky alt tab bar Başparmakla kolay erişim, native his
i18n yaklaşımı URL tabanlı (/tr, /en) Next.js native [lang] dynamic segment
Günün Önerisi / Rakı Gecesi Sonraki sprint'e bırakıldı Şimdilik kapsam dışı

Tasarım Dili

  • Renkler: krem #F5F0E8 · bej #E8DCC8 · turkuaz #4AAFA8 · yeşil #5C8A5C · koyu kahve #3D2B1F
  • Font: Playfair Display (başlıklar, italic vurgu) + Inter (gövde, fiyat, etiketler)
  • His: Bohem, Ege, hasır doku — Magic UI animasyon kalitesi ile desteklenmiş
  • Animasyon: Minimal — scroll geçişleri, tab değiştirme fade

Sayfa Yapısı

┌─────────────────────┐
│  KOZMOS  Beach&More │  sticky header — bg: #3D2B1F
│                TR EN│  dil seçici sağ üst
├─────────────────────┤
│                     │
│  [Aktif kategori    │  scroll area — bg: #F5F0E8
│   içeriği]          │
│                     │
│  🥗 Akdeniz Salatası│  minimal liste satırı
│  Taze sebzeler ₺120 │
│  ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│                     │
│           [WA btn]  │  fixed, sağ alt, bg: #25D366
├─────────────────────┤
│ 🥗  🍽️  🍕  🍹  🍮│  sticky bottom tab bar — bg: #3D2B1F
└─────────────────────┘

Dosya Yapısı

app/
  [lang]/
    layout.tsx          — font yükleme, html lang attr, meta tags
    page.tsx            — menü sayfası (Server Component)
  manifest.ts           — PWA web app manifest
proxy.ts                — / → /tr yönlendirmesi (Next.js 16 proxy)
data/
  menu.ts               — tüm menü verisi (TR+EN, tip tanımlı)
dictionaries/
  tr.json               — UI string'leri Türkçe
  en.json               — UI string'leri İngilizce
public/
  icons/                — PWA icon'ları (192x192, 512x512)

Veri Modeli

// data/menu.ts

type LocalizedString = { tr: string; en: string }

type MenuItem = {
  id: string
  emoji: string
  name: LocalizedString
  description: LocalizedString
  price: number           // TL
  highlight?: boolean     // Günün Önerisi — sonraya hazır alan
}

type Category = {
  id: string
  emoji: string
  label: LocalizedString
  items: MenuItem[]
}

export const categories: Category[] = [ ... ]

Kategoriler (6): Başlangıçlar · Ana Yemekler · Pizzalar · Mezeler · İçecekler · Tatlılar
Her kategoride: 45 ürün, TR+EN isim ve açıklama


i18n Mimarisi

Routing: app/[lang]/page.tsx/tr ve /en URL'leri

proxy.ts:

  • //tr yönlendirir (varsayılan locale)
  • Desteklenmeyen locale → 404

dictionaries/tr.json + en.json:

{
  "nav": { "starters": "Başlangıçlar", "mains": "Ana Yemekler", ... },
  "whatsapp": "Rezervasyon için yazın",
  "currency": "₺"
}

getDictionary (server-only): app/[lang]/dictionaries.ts

  • import 'server-only' ile client bundle'dan korunur
  • hasLocale() ile geçersiz locale → notFound()

Params: Next.js 16 gereği await params — senkron erişim yok.


Bileşen Mimarisi

Bileşenler varsayılan olarak Server Component'tir. 'use client' yalnızca aktif tab detection için gereklidir.

app/[lang]/page.tsx
  └─ <MenuPage lang dict categories />          — Server Component
       ├─ <Header lang />                        — Server Component, dil linkleri
       ├─ <CategorySection id="starters" ...>   — Server Component, her kategori için
       │    └─ <MenuItemRow>                     — Server Component
       ├─ <WhatsAppButton phone />               — Server Component (statik link)
       └─ <BottomTabBar categories />            — CLIENT Component ⚠️

BottomTabBar neden client?
Sayfa tek scroll alanı, tüm kategoriler alt alta sıralı. Tab'lar href="#category-id" anchor linkleri. Aktif tab'ı belirlemek için Intersection Observer kullanılır — bu useEffect + useState gerektirir.

Sayfada sadece bu bir bileşen 'use client' alır.


PWA

app/manifest.ts:

{ name: 'Kozmos Beach & More', short_name: 'Kozmos',
  start_url: '/tr', display: 'standalone',
  background_color: '#F5F0E8', theme_color: '#3D2B1F' }

app/[lang]/layout.tsx meta tags:

  • viewport + theme-color
  • apple-mobile-web-app-capable

Tailwind v4 Yapılandırması

app/globals.css içinde @theme inline ile özel renkler:

@import "tailwindcss";

@theme inline {
  --color-cream:   #F5F0E8;
  --color-beige:   #E8DCC8;
  --color-teal:    #4AAFA8;
  --color-green:   #5C8A5C;
  --color-coffee:  #3D2B1F;
  --font-display:  'Playfair Display', serif;
  --font-body:     'Inter', sans-serif;
}

WhatsApp Butonu

Fixed, sağ alt köşe. href="https://wa.me/90XXXXXXXXXX" — telefon numarası data/menu.ts'ten gelir, hardcode edilmez.


Kapsam Dışı (Sonraki Sprint)

  • Günün Önerisi özel kartı
  • Perşembe Rakı Gecesi banner'ı
  • Gerçek ürün fotoğrafları (Next.js Image optimizasyonu)
  • Admin paneli / menü düzenleme arayüzü