diff --git a/specs/planner-flip-tiles.html b/specs/planner-flip-tiles.html new file mode 100644 index 0000000..c6b8105 --- /dev/null +++ b/specs/planner-flip-tiles.html @@ -0,0 +1,762 @@ + + +
+ + +Mealplan · Planer · Flip Tiles
++ Klick auf eine gefüllte Kachel → sie dreht sich um. Auf der Rückseite: Rezeptname, Hauptzutaten, Aktionen. + Kein Expansion-Panel mehr. Leere Kacheln bleiben unverändert mit Inline-Vorschlägen. +
+ + + + + +heroImageUrl vorhanden → echtes Foto.
+ Sonst: Farbe nach erstem Protein-Tag (z.B. tagType=protein, tagName=Hähnchen).
+ Wenn kein Protein-Tag → Farbe nach Küchenstil-Tag (tagType=cuisine).
+ Fallback auf --color-surface neutral.
+ Die Farbwerte werden als CSS-Klassen gemappt: protein-haehnchen, cuisine-asiatisch etc.
+ Klicke auf eine Kachel um sie umzudrehen. × auf der Rückseite klappt zurück.
+ +transform:rotateY(180deg) auf dem .card wrapper,
+ backface-visibility:hidden auf beiden Faces, perspective:900px auf der Scene.
+ Transition: .45s cubic-bezier(.4,0,.2,1) (Material-Easing — schnell herein, weich heraus).
+ Der × Button auf der Rückseite stoppt den Klick-Event mit stopPropagation()
+ und dreht die Karte zurück. Kein zusätzlicher State nötig — die Karte ist selbst das State-Element.
+ flex:1) — 7 gleich breite Spalten,
+ volle Höhe (height:100% auf Grid und Kacheln). Kein Layout-Shift, kein After-Scroll.
+ Flip Tiles · Final Spec · Route: /planner
+ Der Wochenplaner hat auf Desktop aktuell ~80 % vertikalen Leerraum unterhalb des 7-Spalten-Kalenders. + Zusätzlich ist das rechte Panel im Leerlauf nicht genutzt. Dieses Spec beschreibt ein vollständiges + Redesign der Desktop-Hauptfläche: Die Kacheln füllen die volle Höhe und Breite, Rezeptdetails werden + über einen CSS-3D-Flip direkt in der Kachel angezeigt, und leere Tage zeigen Inline-Vorschläge. +
++ Das rechte Panel entfällt dauerhaft. Der Rezept-Picker öffnet sich als Slide-in-Drawer ausschließlich + auf Anfrage (Aktion „Gericht tauschen" auf der Kachel-Rückseite). Der Toolbar-Button + „Gericht hinzufügen" entfällt, da jede leere Kachel eine eigene CTA hat. +
+Desktop-Layout: 2 Spalten. Kein persistentes rechtes Panel mehr.
+ +┌─────────────────────────────────────────────────────────────┐ +│ Toolbar (Wochenplaner · 7.–13. Apr ‹ › Heute) │ +├──────────┬──────────────────────────────────────────────────┤ +│ Sidebar │ 7-Spalten-Kachelgrid (flex: 1, height: 100%) │ +│ 184 px │ │ +│ Variety │ Mo Di Mi Do Fr Sa So │ +│ Score │ ████ ████ ████ ████ ████ ░░░░ ░░░░ │ +│ │ ████ ████ ████ ████ ████ ░+░░ ░+░░ │ +│ │ ████ ████ ████ ████ ████ ░Vor░ ░Vor░ │ +└──────────┴──────────────────────────────────────────────────┘+ +
display: grid; grid-template-columns: repeat(7, 1fr); gap: 7px; height: 100%. Kacheln füllen die gesamte verbleibende Breite und Höhe.width: 228px) mit der „Heute Abend"-Karte und dem Leerlauf-Hinweis entfällt vollständig. Koch-Modus ist auf der Kachel-Rückseite zugänglich.
+ heroImageUrl.
+ Dual-Gradient-Overlay (oben + unten dunkel, Mitte klar).
+ Oben: Tageskürzel + Datumsziffer. Unten: Rezeptname, Kochzeit, Tags.
+ box-shadow: var(--sh-card) — kein sichtbarer Ring.
+ box-shadow: 0 0 0 2px var(--yellow), var(--sh-card).
+ Datumsziffer-Badge in --yellow. Tag-Label „Heute" zusätzlich als frosted Tag.
+ box-shadow: 0 0 0 2px var(--green), var(--sh-raised).
+ Karte dreht sich 180° (CSS 3D, siehe §04). Alle anderen Kacheln werden auf 38 % Deckkraft
+ gedimmt und sind nicht klickbar.
+ border: 1.5px dashed var(--color-border)),
+ background: var(--color-surface). Oben: Tageskürzel + Datum.
+ Darunter: + Icon + „Gericht wählen". Rest der Kachel: Inline-Vorschläge (§05).
+ box-shadow gesetzt, nicht via border,
+ um Layout-Shift zu vermeiden. Die Kacheln behalten identische Außenmaße in allen Zuständen.
+
+ Wenn heroImageUrl vorhanden ist, wird das echte Foto als background-image gesetzt.
+ Fehlt es, greift die folgende Prioritätskette:
+
tagType = "protein" finden → Protein-FarbetagType = "cuisine" finden → Küchenstil-Farbebackground: var(--color-surface) (neutral)
+ Die CSS-Klassen (protein-haehnchen, cuisine-asiatisch, …) werden
+ serverseitig aus den Rezept-Tags abgeleitet und als Svelte-Prop übergeben, z.B.
+ colorClass="protein-haehnchen". Das Component setzt die Klasse auf dem Kachel-Wrapper.
+
Jede gefüllte Kachel besteht aus drei verschachtelten Elementen:
+.scene → perspective: 900px; border-radius: var(--radius-lg); cursor: pointer + .card → position: relative; transform-style: preserve-3d + transition: transform .45s cubic-bezier(.4,0,.2,1) + .card.flipped → transform: rotateY(180deg) + .card-front → backface-visibility: hidden; position: absolute; inset: 0 + .card-back → backface-visibility: hidden; transform: rotateY(180deg) + position: absolute; inset: 0; background: var(--color-page)+ +
background-image: url(heroImageUrl) mit background-size: cover::after-Pseudo-Element:linear-gradient(to bottom, rgba(0,0,0,.38) 0%, transparent 28%, transparent 48%, rgba(0,0,0,.62) 100%).ingredient, Vorrats-Zutaten (Staples) gedimmt als .ingredient--staple| Aktion | Stil | Verhalten |
|---|---|---|
| Koch-Modus starten | Primary (grün ausgefüllt) | Navigiert zu /planner/cook/[slotId] |
| Rezept ansehen | Secondary (Rahmen) | Navigiert zu /recipes/[recipeId] |
| Gericht tauschen | Secondary (Rahmen) | Öffnet Rezept-Picker-Drawer (§06) |
| Entfernen | Danger (roter Text, transparenter BG) | Löscht den Slot, Kachel wird leer |
.scene → .card.classList.toggle('flipped')opacity: 0.38; pointer-events: noneevent.stopPropagation(), classList.remove('flipped'), Geschwister-Opacity zurücksetzenslotMap-State vorhanden. Der Flip ist eine rein visuelle Operation.
+ Leere Kacheln haben denselben height: 100% wie gefüllte Kacheln. Kein Flip.
┌─────────────────┐ +│ Sa 12 │ ← Tageskürzel + Datum +│─────────────────│ +│ + │ +│ Gericht wählen │ ← Klick öffnet Rezept-Picker-Drawer +│─────────────────│ +│ VORSCHLÄGE │ +│ Ramen mit Ei [Neues Protein] │ +│ Shakshuka [Kein Overlap] │ +│ Tacos [Aufwand: leicht]│ +│ │ +│ Alle Rezepte → │ +└────────────────────────────────┘+ +
Anstelle numerischer Score-Deltas (die für leere Slots immer positiv sind und daher keine Information tragen) + werden Begründungs-Tags angezeigt:
+ +| Tag | Farbe | Bedeutung |
|---|---|---|
| Neues Protein | Grün | Proteinquelle kommt diese Woche noch nicht vor |
| Kein Overlap | Grün | Keine Zutaten-Überschneidung mit anderen Tagen |
| Aufwand: leicht | Gelb | Kochzeit < 30 Min oder Aufwand = einfach |
| Aufwand: mittel | Neutral | Mittlerer Aufwand |
GET /api/suggestions?weekId=&dayOfWeek= API liefert
+ SuggestionItem { recipe, scoreDelta, hasConflict }. Die Reasoning-Tags werden frontend-seitig
+ aus den Rezept-Tags und dem vorhandenen slotMap abgeleitet, kein Backend-Änderungsbedarf.
+ + Der Rezept-Picker öffnet sich als Slide-in-Drawer von rechts — ausschließlich auf explizite Anfrage. + Er hat keinen persistenten Platz im Layout mehr. +
+ +min(480px, 90vw)RecipePicker-Komponente (aktuell im rechten Panel) wird in einen
+ generischen Drawer gewrappt. Der Drawer-Wrapper ist neu; der Picker selbst bleibt unverändert.
+
+ Dieses Spec betrifft ausschließlich die Desktop-Ansicht (≥ 768px).
+ Das mobile Layout (vertikaler Stack, DayMealCard, ActionSheet) bleibt unverändert.
+ CSS-3D-Flips auf Touch-Geräten haben bekannte Rendering-Unterschiede auf älteren Android-Browsern —
+ ein separates Issue sollte die mobile Interaktion (ggf. Slide-up Sheet statt Flip) spezifizieren.
+
.protein-haehnchen, .cuisine-asiatisch, …).
+ .scene: role="button", tabindex="0", aria-expanded="false|true", aria-label="[Rezeptname] — Details anzeigen".card-back: aria-hidden="true" solange nicht geflipptaria-label="Schließen", type="button"Enter / Space flippt, Escape dreht zurückaria-hidden="true" wenn eine andere geflippt ist