Files
pocket-pascal/handover/component-library/components/GlassCard.tsx
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

63 lines
2.4 KiB
TypeScript

import { forwardRef, type HTMLAttributes } from "react";
import { cx } from "../lib/cx";
export interface GlassCardProps extends HTMLAttributes<HTMLDivElement> {
/**
* `default` — static, sits at rest elevation.
* `interactive` — lifts on hover-capable devices, settles down 1px + shadow
* softens on press (not a full inward "pressed" look — that's reserved
* for `Button`; a card is a container you tap into, not a switch).
*/
variant?: "default" | "interactive";
}
/**
* Elevated Surface Card — the primary container of the design system.
*
* Low-transparency (not see-through glass): a solid-reading surface fill
* (~90% opaque) with a visible drop shadow for real elevation, plus a faint
* top-light/bottom-shadow bevel on the edge so it reads as raised off the
* page rather than flat or floating-glass.
*
* Structure (three stacked layers, all inside one rounded-3xl shell):
* 1. Base: subtle surface gradient (--surface-gradient) for texture.
* 2. Surface fill: ~90% opaque fill + 8px blur (softens whatever's behind
* it without reading as transparent) + masked bevel border.
* 3. Content: rendered above both, in normal flow.
*/
export const GlassCard = forwardRef<HTMLDivElement, GlassCardProps>(
({ variant = "default", className, children, ...props }, ref) => {
const interactive = variant === "interactive";
return (
<div
ref={ref}
role={interactive ? "button" : undefined}
tabIndex={interactive ? 0 : undefined}
className={cx(
"elevation-transition relative rounded-3xl no-select",
"shadow-[var(--shadow-elevated)]",
interactive && "cursor-pointer active:translate-y-px active:shadow-[var(--shadow-resting)]",
className
)}
style={{ backgroundImage: "var(--surface-gradient)" }}
{...props}
>
{/* surface fill + blur */}
<div
className="absolute inset-0 rounded-3xl"
style={{
background: "var(--glass-fill)",
backdropFilter: "blur(var(--glass-blur))",
WebkitBackdropFilter: "blur(var(--glass-blur))",
}}
/>
{/* bevel border, 1px, radius-safe */}
<div className="glass-border pointer-events-none absolute inset-0 rounded-3xl" />
{/* content */}
<div className="relative z-10">{children}</div>
</div>
);
}
);
GlassCard.displayName = "GlassCard";