- Body: radial gradient orbs (teal + green) + dot grid overlay + grain - Header: dark mesh gradient, shimmer logo animation, animated gradient tagline, frosted glass lang-switcher pill - CategorySection: glow-ring emoji badge, gradient top line, frosted glass card container, teal/gold decorative divider - MenuItemRow: gradient emoji circle, magic-card CSS hover spotlight, price pill with green gradient border - BottomTabBar: blur/glass tabbar, active emoji glow + scale + pill bg - Page: welcome strip with teal gradient border, hero fade from header Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import type { Category } from '@/data/menu'
|
|
|
|
type Props = {
|
|
categories: Category[]
|
|
lang: string
|
|
}
|
|
|
|
export default function BottomTabBar({ categories, lang }: Props) {
|
|
const l = lang as 'tr' | 'en'
|
|
const [activeId, setActiveId] = useState<string>(categories[0]?.id ?? '')
|
|
|
|
useEffect(() => {
|
|
const observers: IntersectionObserver[] = []
|
|
|
|
categories.forEach((cat) => {
|
|
const el = document.getElementById(cat.id)
|
|
if (!el) return
|
|
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) setActiveId(cat.id)
|
|
})
|
|
},
|
|
{ rootMargin: '-20% 0px -75% 0px', threshold: 0 }
|
|
)
|
|
|
|
observer.observe(el)
|
|
observers.push(observer)
|
|
})
|
|
|
|
return () => observers.forEach((o) => o.disconnect())
|
|
}, [categories])
|
|
|
|
return (
|
|
<nav
|
|
className="tabbar-glass fixed bottom-0 left-0 right-0 z-40"
|
|
style={{ height: 'var(--tabbar-h)' }}
|
|
>
|
|
<div className="flex items-start justify-around pt-2 h-full px-1">
|
|
{categories.map((cat) => {
|
|
const isActive = activeId === cat.id
|
|
return (
|
|
<a
|
|
key={cat.id}
|
|
href={`#${cat.id}`}
|
|
className="tab-item flex flex-col items-center gap-0.5 flex-1 py-1 rounded-lg relative"
|
|
>
|
|
{/* Active pill background */}
|
|
{isActive && (
|
|
<span
|
|
className="tab-pill absolute inset-x-1 inset-y-0"
|
|
aria-hidden="true"
|
|
/>
|
|
)}
|
|
|
|
{/* Emoji */}
|
|
<span
|
|
className="relative text-lg leading-none transition-transform duration-200"
|
|
style={{
|
|
filter: isActive
|
|
? 'drop-shadow(0 0 6px rgba(74,175,168,0.60))'
|
|
: 'none',
|
|
transform: isActive ? 'scale(1.15)' : 'scale(1)',
|
|
}}
|
|
>
|
|
{cat.emoji}
|
|
</span>
|
|
|
|
{/* Label */}
|
|
<span
|
|
className="relative font-body font-medium leading-none tracking-wide text-center truncate w-full text-center transition-colors duration-200"
|
|
style={{
|
|
fontSize: '0.5rem',
|
|
color: isActive ? '#4AAFA8' : 'rgba(245,240,232,0.32)',
|
|
}}
|
|
>
|
|
{cat.label[l]}
|
|
</span>
|
|
|
|
{/* Active dot */}
|
|
{isActive && (
|
|
<span
|
|
className="relative w-1 h-1 rounded-full bg-teal"
|
|
aria-hidden="true"
|
|
/>
|
|
)}
|
|
</a>
|
|
)
|
|
})}
|
|
</div>
|
|
</nav>
|
|
)
|
|
}
|