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>
This commit is contained in:
62
handover/component-library/components/GlassCard.tsx
Normal file
62
handover/component-library/components/GlassCard.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
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";
|
||||
Reference in New Issue
Block a user