Journey spec · Screens C1, C2, C3 · All breakpoints
Fill day slots, get variety-aware suggestions, review score. C1 is the app home screen.
The weekly planner is the app home screen. It lets the planner see all seven dinner slots for the current week at a glance, select any day to view the full meal, and access swap and cook actions without leaving the screen. For household members (read-only role) all edit and swap actions are hidden — the rest of the layout is identical.
The core pain point this app solves is meal repetition across the week. The variety score is therefore always on screen on all three breakpoints — it never hides in a tab, modal, or collapsed region. Ingredient repeat warnings appear inline at the tile level on desktop and in the detail panel across all breakpoints.
Five stacked fixed-height regions: top nav → variety banner → week strip → selected day card → scrollable remaining list. The tab bar is position: fixed at the bottom. Only the remaining days list scrolls.
7-column grid, each chip 8px top+bottom padding. A 4px dot below the date is the only meal indicator: grey = no meal, green = meal planned, yellow-text = today, green-dark = selected. Today chip: yellow-tint bg + yellow-light border. Selected: green-tint + green-light. Tapping any chip updates the selected day panel immediately below.
Meal name in Fraunces 20px weight 400. Tags row (time, effort, attributes) then two equal-width action buttons. shadow-card elevation. When today is selected: 2px yellow border + yellow-tint background — the only 2px border in the system.
Always the first element below the nav. Never collapses. Score in Fraunces 28px weight 300. Warning row (yellow-text colour, yellow dot) appears when any ingredient appears in 2+ meals this week.
Shows only days after the selected day. Date column: 36px wide. Meal name: Fraunces 14px. Meta: 11px muted DM Sans. Swap button: right-aligned 11px. Rows separated by 1px color-subtle dividers. Empty days are not shown — their absence is visible only via the dot in the strip.
The tablet uses the same five-region stack and the same interaction model as mobile. Key differences: the selected meal card expands to a two-column layout (info left, action buttons right); the remaining days list becomes a 2-column card grid; the variety banner gains a labelled progress bar; and the bottom navigation becomes an inline horizontal pill bar rather than a fixed screen overlay. A "Today" shortcut chip appears between the prev/next week buttons.
Two-column layout: meal info (name 22px, tags, description) on the left, three stacked full-width action buttons on the right (min-width 120px). Description line is tablet and desktop only.
2-column CSS grid with 8px gap. Each card: date column (30px) + 1px vertical divider + meal name + meta + right arrow. Empty slots: dashed border, transparent bg, + icon right-aligned.
Bottom nav becomes inline (not fixed), rendered as horizontal labelled pill items. Active item: green-tint bg + green-dark text. A "Today" shortcut chip sits between the prev/next week buttons.
Desktop is a fundamentally different layout. The screen switches from a vertical single-column stack to a three-panel horizontal model: left sidebar (224px, fixed height, sticky), main calendar area (flex 1, only panel that scrolls), and right detail panel (280px, fixed). The day strip is replaced entirely by a 7-column calendar grid. The bottom tab bar is replaced by a persistent left sidebar nav. This is a deliberate layout change — not responsive scaling.
Sidebar 224px fixed + calendar flex 1 scrollable + detail panel 280px fixed. Sidebar and detail panel never scroll. Only the calendar area scrolls vertically. All three are 100% viewport height.
Each tile uses flex: 1 to fill its column. Column header has a 2px bottom border — yellow for today, green for selected. Tiles with repeated ingredients show a badge-warn chip (rgba(242,193,46,.25) bg). Empty slots: dashed border, centred + icon.
Bottom of the sidebar. Never scrolls out of view. Includes score, bar, message, ingredient-specific warning, and a "Review variety" button linking to screen C3.
All values are real production sizes. Previews above are scaled for visual presentation only — never measure from them. Use tokens from color-palette-spec v1.0, typography-spec v1.0, and spacing-shape-spec v1.0.
/* C1 Implementation rules * 1. Default route on first load. App home screen. * 2. Variety score ALWAYS VISIBLE on all breakpoints. Never hidden or in a tab/modal. * 3. Today chip: background yellow-tint (#FDF6D8), border 1px yellow-light (#F9E08A). * 4. Selected chip: background green-tint (#E8F5EA), border 1px green-light (#AEDCB0). * 5. Today meal card (mobile/tablet): 2px solid yellow (#F2C12E) border + yellow-tint bg. * Today tile (desktop): same 2px yellow border. This is the ONLY 2px border in the system. * 6. Meal names: always --font-display (Fraunces). All UI chrome: --font-sans (DM Sans). * 7. Chip dot: --color-border = no meal. --green = meal. --yellow-text = today. --green-dark = selected. * 8. Desktop: sidebar position sticky, height 100vh. Detail panel also sticky. Only cal-area scrolls. * 9. Ingredient repeat warnings: badge-warn (rgba(242,193,46,.25) bg + --yellow-text color) on tiles. * In detail panel: repeat rows use --yellow-text for name AND quantity text. * 10. Household member role: hide swap buttons, hide "+ Add meal" button. Show read-only badge. * 11. Empty day slot mobile: not shown in the remaining days list (only shown via dot in strip). * Empty day slot tablet/desktop: dashed 1px --color-border, transparent bg, centred + (18px --color-border). * 12. Meal description text (13px DM Sans muted, line-height 1.5): tablet + desktop only. Hidden on mobile. */
| Region / element | Real production value | Notes | Breakpoints |
|---|---|---|---|
| Layout & breakpoints | |||
| Mobile breakpoint | < 768px | Single-column vertical stack, fixed bottom tab bar, body full-width | mobile |
| Tablet breakpoint | 768px – 1024px | Same stack structure, wider panels, inline bottom nav. No fixed tab bar. | tablet |
| Desktop breakpoint | > 1024px | 3-panel horizontal: sidebar + calendar + detail panel | desktop |
| Desktop sidebar width | 224px fixed | position: sticky; height: 100vh. Contains logo, nav, variety score. | desktop |
| Desktop detail panel width | 280px fixed | position: sticky; height: 100vh. Hidden on mobile and tablet. | desktop |
| Desktop calendar gap | gap: 8px between columns (--space-2) | grid-template-columns: repeat(7, 1fr) | desktop |
| Desktop calendar padding | 20px all sides (--space-5) | Inside the scrollable cal-area div | desktop |
| Mobile page gutter | 16px each side (--space-4) | Applied to all content regions individually, not a wrapper | mobile |
| Tablet page gutter | 20px–24px each side (--space-5 / --space-6) | Variety banner and selected card: 20px. Week header and strip: 20px. | tablet |
| Tablet remaining grid gap | 8px (--space-2) | 2-column CSS grid | tablet |
| Navigation | |||
| Mobile top nav padding | 10px 16px (--space-2 --space-4) | position: sticky; top: 0; z-index: 10; border-bottom: --border-default | mobile |
| Mobile top nav title | Fraunces 20px weight 500, letter-spacing -0.02em | "This week" — updates to week range on scroll if implemented | mobile |
| Tablet top nav padding | 14px 20px (--space-3 --space-5) | Title: Fraunces 24px weight 500. Right side adds "+ Add meal" button. | tablet |
| Desktop topbar padding | 10px 24px (--space-2 --space-6) | Title: Fraunces 20px weight 500. Week nav inline left. Today + Add meal right. | desktop |
| Desktop sidebar nav item | padding: 7px 6px; font-size: 13px; border-radius: --radius-md (6px) | Active: green-tint bg + green-dark text + weight 500. Icon 16px, 20px wide. | desktop |
| Mobile tab bar | position: fixed; bottom: 0; padding-bottom: 20px (safe area) | 4 items. Active icon: green-tint bg. Active label: green-dark color. Label: 10px DM Sans weight 500. | mobile |
| Tablet nav bar | position: static; inline at bottom; padding: 10px 20px 14px | Horizontal labelled pills. Active: green-tint bg + green-dark text. | tablet |
| Nav prev/next buttons | DM Sans 11–12px, padding: 4–5px 8–10px, radius-sm (4px), color-subtle bg, border-default | Mobile: icon-only (⟨ ⟩) 32x32px. Tablet/desktop: text "‹ Prev" / "Next ›". | all |
| "Today" shortcut chip | DM Sans 10–11px weight 500, color-subtle bg, border-default, radius-sm | Between prev/next buttons. Tablet and desktop only. | tablet, desktop |
| "+ Add meal" button | DM Sans 12–13px weight 500, green bg (#3D8C4A), white text, radius-md (6px), padding 7–8px 14–18px | Planner role only. Hidden for household members. | tablet, desktop |
| Variety score | |||
| Component background | yellow-tint (#FDF6D8) bg, 1px yellow-light (#F9E08A) border, radius-lg (10px) | Identical on all breakpoints | all |
| Mobile position | margin: 16px 16px 0 (top + sides). First content after nav. | padding: 8px 16px (--space-2 --space-4). Never collapses. | mobile |
| Tablet position | margin: 14px 20px 0. padding: 12px 16px. | Same structure, slightly larger | tablet |
| Desktop position | Bottom of sidebar. margin: 0 8px 14px. padding: 10px 12px. | Always visible. Never scrolled by any panel. | desktop |
| Score number | Fraunces 28px mobile / 28px tablet / 40px desktop, weight 300, letter-spacing -0.02em, line-height 1 | Followed inline by denominator "/10" in DM Sans 12–16px muted | all |
| Eyebrow label | DM Sans 8–10px, weight 500, letter-spacing 0.08–0.1em, uppercase, --yellow-text (#8A6800) | Always first text inside the component | all |
| Status message | DM Sans 9–11px, --color-text-muted | E.g. "Good variety this week" | all |
| Warning row | DM Sans 9–11px, --yellow-text, flex row, 4–5px yellow dot (border-radius 50%) | Only shown when any ingredient appears in 2 or more meals | all |
| Progress bar | height: 3–5px, track: yellow-light, fill: yellow, radius: 99px | Width = variety score as percentage (score 8 = 80%) | all |
| "Review variety" button | DM Sans 10px weight 500, color-page bg, yellow-light border, yellow-text color, radius-sm, padding 5px | Desktop sidebar only. Links to screen C3 (variety review). | desktop |
| Day strip (mobile + tablet) | |||
| Strip grid | grid-template-columns: repeat(7, 1fr); gap: 2–3px mobile / 5–6px tablet | padding: 0 16px 8–12px | mobile, tablet |
| Chip padding | 5–8px top+bottom / 1–4px left+right | border-radius: --radius-md (6px) mobile / --radius-lg (10px) tablet | mobile, tablet |
| Chip abbreviation | DM Sans 7–9px, weight 500, tracking 0.05–0.06em, uppercase, --color-text-muted | 2 letters on mobile (Mo Tu), 3 letters on tablet (Mon Tue) | mobile, tablet |
| Chip date number | DM Sans 11–14px, weight 500, --color-text, line-height 1–1.1 | mobile, tablet | |
| Chip dot | 3–4px diameter circle, border-radius: 50% | No meal: --color-border. Has meal: --green (#3D8C4A). Today: --yellow-text (#8A6800). Selected: --green-dark (#2E6E39). | mobile, tablet |
| Today chip | background: --yellow-tint (#FDF6D8); border: 1px solid --yellow-light (#F9E08A) | Abbreviation colour: --yellow-text. Dot colour: --yellow-text. | mobile, tablet |
| Selected chip | background: --green-tint (#E8F5EA); border: 1px solid --green-light (#AEDCB0) | Abbreviation colour: --green-dark. Dot colour: --green-dark. | mobile, tablet |
| Calendar grid (desktop only) | |||
| Column header day name | DM Sans 9–10px, weight 500, tracking 0.08em, uppercase, --color-text-muted | margin-bottom: 3px | desktop |
| Column header date badge | 24x24px, border-radius: 4px (--radius-sm), DM Sans 12–14px weight 500, --color-text | Today col: yellow (#F2C12E) bg. Selected col: green-tint bg + green-dark text. Default: no bg. | desktop |
| Column header bottom border | 2px solid | Default: --color-border. Today column: --yellow (#F2C12E). Selected column: --green (#3D8C4A). | desktop |
| Tile padding | 8px 8px 10px (top / sides / bottom) | border-radius: --radius-lg (10px). flex: 1 to fill column height. | desktop |
| Tile eyebrow | DM Sans 8–9px, weight 500, tracking 0.06–0.08em, uppercase | Default: --color-text-muted. Today tile: --yellow-text. Selected tile: --green-dark. | desktop |
| Tile meal name | Fraunces 11–13px, weight 400, letter-spacing -0.01em, line-height 1.3 | No text truncation — tile height grows to fit | desktop |
| Tile at rest | background: --color-surface; border: 1px --color-border; box-shadow: --shadow-card | Hover: border-color --green-light; box-shadow: --shadow-raised | desktop |
| Today tile | border: 2px solid --yellow (#F2C12E); background: --yellow-tint (#FDF6D8) | ONLY 2px border in the system | desktop |
| Selected tile | border: 2px solid --green (#3D8C4A); background: --green-tint (#E8F5EA) | Also 2px — matches today tile treatment | desktop |
| Ingredient repeat badge | background: rgba(242,193,46,0.25); color: --yellow-text (#8A6800); DM Sans 10px weight 500; padding: 2px 6px; border-radius: 3px | Replaces or supplements normal tags. Shows "⚠ [ingredient]". | desktop |
| Empty day tile | border: 1px dashed --color-border; background: transparent; box-shadow: none; min-height: 60px | Centred content: + icon (16–18px --color-border) + "Add meal" label (9–10px --color-border) | desktop |
| Selected day / detail panel | |||
| Mobile: selected area padding | 16px all sides (--space-4) | Section above rule: eyebrow label left, date right | mobile |
| Mobile: meal card padding | 10–16px all sides | background: --color-surface; border: 1px --color-border; radius: --radius-lg (10px); box-shadow: --shadow-card | mobile |
| Meal name — mobile | Fraunces 20px, weight 400, letter-spacing -0.02em, line-height 1.25, margin-bottom: 8px | --color-text | mobile |
| Meal name — tablet | Fraunces 22px, weight 400, same other values | --color-text | tablet |
| Meal name — desktop detail panel | Fraunces 17px, weight 400, line-height 1.3, margin-bottom: 6px | Smaller because panel is narrower (280px) | desktop |
| Meal description | DM Sans 13px, --color-text-muted, line-height 1.5 | Tablet + desktop only. Hidden on mobile. | tablet, desktop |
| Actions — mobile | 2 buttons, flex row, equal width, DM Sans 12px weight 500, padding: 8px 12px, radius-md | "Cook now" (green primary) + "Swap meal" (ghost) | mobile |
| Actions — tablet | 3 buttons, stacked column in right half of card (min-width 120px), DM Sans 12px, padding: 7px 12px | "Cook now" (primary) + "View recipe" (outline) + "Swap meal" (ghost) | tablet |
| Actions — desktop | 3 buttons, stacked column, full panel width (280px minus padding), DM Sans 11px, padding: 6px | "View recipe" (primary) + "Cook mode" (outline) + "Swap meal" (ghost) | desktop |
| Desktop ingredient rows | padding: 4px 0 per row; border-bottom: 1px --color-subtle; DM Sans 11px name + 10px muted qty | Repeat ingredient rows: both name and qty use --yellow-text (#8A6800) | desktop |
| Remaining days list / grid | |||
| Mobile list item padding | 6–12px top + bottom (--space-2 / --space-3) | Separated by 1px --color-subtle bottom border. No left/right border. | mobile |
| Date column — mobile list | width: 26–36px, flex-shrink: 0 | Abbreviation: 7–9px DM Sans uppercase muted. Number: Fraunces 13–17px weight 300. | mobile |
| Row meal name — mobile | Fraunces 11–14px, weight 400, letter-spacing -0.01em, --color-text | white-space: nowrap; overflow: hidden; text-overflow: ellipsis | mobile |
| Row metadata — mobile | DM Sans 9–11px, --color-text-muted | Format: "{time} · {attribute}" e.g. "25 min · Easy" | mobile |
| Swap button | DM Sans 9–11px, padding: 3–4px 6–8px, radius-sm (4px), border-default, color-surface bg | Right-aligned. Planner role only — hidden for household members. | mobile, tablet |
| Tablet grid | grid-template-columns: 1fr 1fr; gap: 8px (--space-2); padding: 0 20px 16px | Each card: radius-lg, shadow-card, flex row. Date col (30px) + 1px vertical divider + content + arrow. | tablet |
| Empty slot — tablet grid | border: 1px dashed --color-border; background: transparent; box-shadow: none | Right side: + icon (16px --color-border). Left: date col + divider as normal. | tablet |
| Empty slot — mobile | Not shown in the remaining list | Absence is communicated via grey dot in the day strip only. | mobile |
/* Desktop: 224px sidebar (variety score at bottom) + topbar + split content: * Left panel (280px, surface bg): week-so-far mini cards + filter reason bullets * Right panel (flex:1, page bg): ranked suggestion cards with Pick action * The context panel is a page section, not a floating card. * Mobile: context banner (green-tint, collapsible) + ranked list below. * Pick → writes week_plan_slot → returns to C1. * "Browse full library" → B1 in selection mode. */
| Element | Value | Notes |
|---|---|---|
| Desktop | ||
| Context panel | 280px, surface bg, border-right | Week meals + current slot (yellow) + filter reasons |
| Suggestions | flex:1, page bg, 20px padding | Same sg-card component as mobile but wider |
| Mobile | ||
| Context banner | green-tint, radius-lg, 10px 12px pad | Day label + filter summary. Collapsed on mobile. |
/* Desktop: sidebar + topbar (breadcrumb ← Planner / Variety review) + content: * Top area: 2-col flex. Left: big score (72px) + progress bar + sub-score rows. Right: 7-day protein grid + effort bar. * Bottom area: full-width warning cards. * Content sits directly on page bg — no wrapper card. * Mobile: stacked. Score → sub-scores → warnings. No protein grid (too small). * Score is COMPUTED (variety CTE from data model), never stored. */
| Element | Value | Notes |
|---|---|---|
| Desktop | ||
| Score area | flex:1. Score: Fraunces 72px/300. Bar: 200px wide, 6px, yellow. | Sub-scores: bordered rows, not in a card. |
| Protein grid | 320px. 7-col grid, 6px gap. Cells: 44px height, colored by protein. | Repeated protein: 2px yellow border on matched cells (Mon + Thu chicken). |
| Effort bar | Proportional flex row. Green/yellow/red segments. | Labels: "Easy ×3", "Medium ×3", "Hard ×1" |
| Warnings | Full width, yellow-tint, radius-lg | Title 13px/500 + body 12px. Below the 2-col area. |
C1 (planner) → C2 (suggestions) → C1 → C3 (variety review) → C1. The actor is the Planner only. Household members see C1 in read-only mode (all action buttons hidden). C1 is the app home screen and the default route.