Files
Steffi Müller a482bf5b09 Add Phase 1 Pocket Pascal UI: home, pantry, week plan, and plate.
Replace the hello-world shell with the elevated design system, bilingual screens, recipe/extras data, and the interactive Mein Teller view.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 12:22:32 +02:00

89 lines
2.9 KiB
TypeScript

import type { ReactNode } from "react";
import { cx } from "../lib/cx";
export interface NavItem {
key: string;
icon: ReactNode;
label: string;
active?: boolean;
onClick?: () => void;
href?: string;
}
export interface BottomNavProps {
items: NavItem[];
className?: string;
}
/**
* Floating Glass Pill bottom navigation.
*
* - Fixed to the bottom, centered, floats above content (not edge-to-edge).
* - Respects `env(safe-area-inset-bottom)` so it clears the iOS home indicator.
* - Active item gets the accent gradient applied directly to icon + label
* (bg-clip-text) rather than a pill behind it, so it reads against the
* glass background instead of competing with it.
*/
export function BottomNav({ items, className }: BottomNavProps) {
return (
<nav
className={cx("fixed inset-x-0 bottom-0 z-50 flex justify-center pointer-events-none", className)}
style={{ paddingBottom: "calc(env(safe-area-inset-bottom) + 12px)" }}
>
<div
className="relative flex items-center gap-1 rounded-full px-2 py-2 pointer-events-auto"
style={{ boxShadow: "var(--shadow-ambient)" }}
>
<div
className="absolute inset-0 rounded-full"
style={{
background: "var(--glass-fill)",
backdropFilter: "blur(var(--glass-blur))",
WebkitBackdropFilter: "blur(var(--glass-blur))",
}}
/>
<div className="glass-border pointer-events-none absolute inset-0 rounded-full" />
{items.map((item) => {
const content = (
<>
<span
aria-hidden
className={cx(
"text-xl leading-none",
item.active ? "bg-clip-text text-transparent" : "text-[var(--ink-soft)]"
)}
style={item.active ? { backgroundImage: "var(--gradient-accent)" } : undefined}
>
{item.icon}
</span>
<span
className={cx(
"font-body text-[10px] font-bold leading-none",
item.active ? "bg-clip-text text-transparent" : "text-[var(--ink-soft)]"
)}
style={item.active ? { backgroundImage: "var(--gradient-accent)" } : undefined}
>
{item.label}
</span>
</>
);
const itemClass = cx(
"relative z-10 flex min-w-[48px] min-h-[48px] flex-col items-center justify-center gap-0.5",
"elevation-transition rounded-full px-3 no-select active:translate-y-px active:brightness-95"
);
return item.href ? (
<a key={item.key} href={item.href} className={itemClass}>
{content}
</a>
) : (
<button key={item.key} type="button" onClick={item.onClick} className={itemClass}>
{content}
</button>
);
})}
</div>
</nav>
);
}