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:
Steffi Müller
2026-08-04 12:22:32 +02:00
parent 3785d08e97
commit a482bf5b09
60 changed files with 7366 additions and 72 deletions

View File

@@ -0,0 +1,58 @@
# Pocket Pascal — Elevated Surface Component Library
Drop this folder's contents into your Cursor React + TypeScript + Tailwind project. It's framework-agnostic (no Next.js-specific imports), works in Vite or Next.
> **Pivoted from glassmorphism to elevation (2026-08-03).** The first pass used real see-through glass (12% white fill, 16px blur) — over a plain white/dark background it barely reads as anything, which is exactly the feedback that came back: "I don't see the glass effect." Rather than fight a page background that has nothing distinct behind it to refract, the system now uses **low-transparency solid surfaces (~90% opaque) with visible elevation shadows**, and buttons that visibly **press inward** on tap (inset shadow + 1px downward shift + slight darkening) instead of just scaling down. The `--glass-*` variable names in `theme.css` were kept to minimize the diff across components, but they now mean "surface fill / surface blur," not transparent glass — see the comment block at the top of `theme.css`.
## Setup
1. **Copy files**
- `styles/theme.css` → your global stylesheet, imported once in your app root (e.g. `main.tsx` or `app/layout.tsx`), **after** `@tailwind base/components/utilities`.
- `lib/cx.ts`, `components/*.tsx` → into your `src/` at matching paths, or adjust the relative imports.
- `public/fonts/*.woff2` → your project's `public/fonts/` (already the correct static latin-subset files, no need to re-fetch from Google).
- `ComponentShowcase.tsx` → mount on a scratch route to sanity-check everything renders before wiring into real screens.
2. **Tailwind config** — enable class-based dark mode so the `.dark` class in `theme.css` works:
```ts
// tailwind.config.ts
export default {
darkMode: "class",
content: ["./src/**/*.{ts,tsx}"],
theme: { extend: {} },
};
```
No plugin needed — `backdrop-filter`, `active:`, and arbitrary values (`bg-[image:var(--x)]`) are all core Tailwind v3+. This library leans on CSS variables + inline `style` for the gradient/blur values rather than baking them into the Tailwind theme, so it works the same whether or not you extend the config further.
3. **Toggle dark mode** by adding/removing the `dark` class on `<html>` (or any ancestor — see `ComponentShowcase.tsx` for a self-contained example that wraps in a `div` instead, for demo purposes only; in a real app put it on `<html>`).
## What's in here
| File | Purpose |
|---|---|
| `styles/theme.css` | All design tokens: surface fill/blur/bevel-border, three-state shadows (resting/elevated/pressed), brand colors, surface + action gradients, `@font-face`, the `.glass-border` masked-bevel utility, PWA polish resets. |
| `components/GlassCard.tsx` | Primary container. `variant="default" \| "interactive"`. Real elevation shadow, settles down 1px on tap. |
| `components/BottomNav.tsx` | Floating pill nav, safe-area aware, gradient-on-active-icon, items press inward on tap. |
| `components/Button.tsx` | `variant="primary" \| "secondary" \| "fab"`, plus `loading`/`disabled` states. Press state = inset shadow + 1px shift + darken, not just scale. |
| `components/PlateChart.tsx` | SVG donut styled as a ceramic plate (rim + recessed well + gapped gradient slices + center label). |
| `ComponentShowcase.tsx` | Demo page rendering all of the above over the ambient page gradient, with a light/dark toggle. |
## Deliberate deviations from the original brief — and why
**Text color on gradient buttons/badges is `--ink` (near-black), not white.**
The brand's action gradients (lime → teal, coral → amber) are light/high-luminance by design — they're the same tokens already WCAG-audited for the rest of the Pocket Pascal system (see `/docs/CONCEPT.html`, decision log). White text on them fails AA contrast outright; a text-shadow doesn't fix a 2:1 ratio. Dark `--ink` text on these gradients measures 5.610.6:1 in both themes. If you introduce a new, genuinely dark gradient (e.g. for a destructive/caution CTA), white text + `text-shadow: 0 1px 2px rgba(0,0,0,0.25)` is fine there — just check the ratio first.
**`--gradient-caution` was added** (coral → deep red) for destructive actions, since the brief's two named gradients (`primary`, `accent`) didn't cover that case and the brand already has a `--color-caution` token.
**Fonts are loaded via local `@font-face` + static `.woff2` files, not `next/font` or a Google Fonts `<link>`.** Keeps `theme.css` portable across Vite/Next, and avoids a runtime dependency on Google's CDN. The five files in `public/fonts/` are the exact latin-subset statics used everywhere else in this project.
**`.glass-border` uses a masked pseudo-gradient, not `border-image`.** `border-image` ignores `border-radius` — it would square off every rounded corner. The mask-composite technique in `theme.css` respects `border-radius: inherit`, so it works on the 3xl card, the pill nav, and the 2xl buttons without extra per-component CSS.
**Pressed state is inset-shadow + 1px shift + darken, applied via `active:` Tailwind classes referencing CSS vars (`active:shadow-[var(--shadow-pressed)]`), not JS state or `active:scale`.** Scale alone reads as "shrinking," not "pushed in" — the eye needs the shadow to flip from outside (elevated) to inside (pressed) to sell the depth illusion. `Button` and the `BottomNav` items use this; `GlassCard`'s interactive variant deliberately uses a milder version (settle 1px + soften shadow, no inset) since a card is a container you tap *into*, not a switch you press.
## Known follow-ups (not done here)
- `PlateChart` gap is computed from a **physical pixel gap → angular gap** at a fixed average radius; if you resize the chart a lot at runtime, re-check that 2px still reads as a visible gap at very small sizes (below ~120px it may visually disappear).
- No unit/visual regression tests included — this is a first-pass scaffold for Cursor, not a hardened library.
- `BottomNav` active-icon gradient uses `background-clip: text`, which needs `-webkit-background-clip: text` too in some older WebKit builds; not added here since Tailwind's `bg-clip-text` utility already handles the prefix.