Colors

--bg
#F7F5F2
--surface
#FFFFFF
--accent
#A67C52
--accent-hover
#9A724A
--accent-strong
#7A5737
--metal
#C9CDD1
--metal-dark
#8B8F94
--green
#3F5F3D
--text
#2E2E2E
--text-muted
#6B6560
--border
#DAD6CF

Typography

Playfair Display — Headings

Heading 1 — Cook multiple recipes

Heading 2 — Shopping List

Heading 3 — Produce

Inter — Body

Body — Paste a recipe URL and ShopChopCook builds you a unified shopping list, a mise en place checklist, and a cook schedule.

Small / Muted — Works with most recipe sites.

Buttons

RecipeFormUrlInput

Single-URL form used on the home page and all marketing pages. Validates YouTube links inline; shows contextual error messages for blocked sites (403), no structured data (422), and unreachable URLs (502). Warning display handled by RecipeFormUrlWarning. Component: components/recipe-form/UrlInput.vue.

Default (light)

Works with most recipe sites. Just paste and go.

Hero (dark)

Works with most recipe sites. Just paste and go.

StateTrigger
YouTube warningURL contains youtube.com or youtu.be — submit disabled
422 errorSite has no structured recipe data
403 errorSite blocked access
502 errorURL unreachable

Home Callouts

SEO link cards on the home page. Source: pages/index.vue .seo-links section.

RecipeFormPlanForm

Multi-URL recipe form used on the plan page. Supports 1–3 URLs, saved recipe picker, edit/cancel flow, and YouTube + error warnings via RecipeFormUrlWarning. Component: components/recipe-form/PlanForm.vue.

Planning mode (no plan yet)

Add your recipes

Plan exists (view mode)
Prop / EventDescription
v-model:urlsURL array — parent-owned, supports up to 3 entries
v-model:editingEditing state — parent reads this to show/hide plan tabs
mealPlanNull = planning mode; non-null = view mode
isPlanOwnerShows edit controls vs Remix plan button
error / errorIs403 / errorIsNoRecipeError state set by parent after API call
@planUser submitted — parent calls parse + generate API
@duplicateUser clicked Remix plan
@cancelUser cancelled edit — parent should clear errors

LiveScheduleStep

Used on the plan page cook schedule "Step by Step" tab. Component: components/live/LiveScheduleStep.vue.

Light
  1. Roast Chicken
    Preheat oven to 425°F
    5m
  2. Roast Chicken
    Season and truss the chicken
    10m Pat completely dry first
  3. Roast Chicken
    Roast until internal temp 165°F
    1h hands-off
  4. Roasted Potatoes
    Cut potatoes into wedges
    8m
Dark
  1. Roast Chicken
    Preheat oven to 425°F
    5m
  2. Roast Chicken
    Season and truss the chicken
    10m Pat completely dry first
  3. Roast Chicken
    Roast until internal temp 165°F
    1h hands-off
  4. Roasted Potatoes
    Cut potatoes into wedges
    8m
ElementToken
Active left bordervar(--accent-strong)
Passive left bordervar(--border)
Recipe tagvar(--accent)
Step labelvar(--text)
Duration / metavar(--text-muted)

LiveActiveCard

Active task cards in the HTML cook view (cook.vue, live.vue). Component: components/live/LiveActiveCard.vue.

Light
Roast Chicken
Season and truss the chicken
8:32 left
Roasted Potatoes
Roast in oven
22:10 left
Dark
Roast Chicken
Season and truss the chicken
8:32 left
Roasted Potatoes
Roast in oven
22:10 left
ElementToken
Active left border + Done btnvar(--accent)
Passive / paused left bordervar(--border)
Countdown textvar(--accent)
Accent glow (active, hands-on)box-shadow: 0 0 0 2px rgba(166,124,82,0.15)
Pause btnbtn-ghost
Done btnbtn-wood

LiveUpcomingList

Upcoming task list in cook.vue and live.vue. Component: components/live/LiveUpcomingList.vue. Long-press to expand notes.

Light
Roasted Potatoes
Toss with oil and herbs +3m
Roast Chicken
Rest the chicken (do not cut) +12m
Green Beans
Sauté with garlic and butter +18m
Dark
Roasted Potatoes
Toss with oil and herbs +3m
Roast Chicken
Rest the chicken (do not cut) +12m
Green Beans
Sauté with garlic and butter +18m
ElementToken
Container.card (var(--surface) + var(--border))
Countdown / timevar(--accent)
Recipe tagvar(--text-muted)
Row dividervar(--border)
Start Now btn bgvar(--green)#3F5F3D light / #4A7046 dark

Typography Roles

Semantic classes defined in main.styl. Components apply these for font rules and keep only color + layout in scoped styles. Five sizes total: 0.65 (badge), 0.72, 0.85, 1.0, 1.25, 1.8.

ROAST CHICKEN
.text-recipe — 0.72rem · 600 · uppercase · 0.06em
PAUSED
.text-badge — 0.65rem · 700 · uppercase · 0.08em
Season and truss the chicken
.text-step — 1rem · 500 · line-height 1.35
Season and truss the chicken
.text-step-active — Playfair Display · 1.25rem · 600 · line-height 1.25 — active cook view only
10 min · +3m
.text-meta — 0.85rem · 500 · tabular-nums
Pat completely dry first
.text-note — 0.85rem · italic · line-height 1.5
8:32
.text-timer — Playfair Display · 1.8rem · 700 · 0.02em · tabular-nums — active cook view only

NowLineGL

WebGL background + Canvas 2D overlay. Full-plan mode shown below. Source: components/NowLineGL.vue. Colors from composables/useChartColors.ts.

Light
Dark
Color tokenLightDarkUsed for
active#9B6F46#D2A874Hands-on bar — wood accent, dominant
passivergba(160,170,180,0.45)rgba(190,200,210,0.32)Hands-off bar — metal/neutral, cool + quiet
upcomingrgba(166,124,82,0.48)rgba(196,154,108,0.36)Future task bar — desaturated wood, visible
pausedrgba(120,120,140,0.55)rgba(150,150,170,0.55)Paused bar
taskLabel#ffffff#F1E7D8Task name text inside bar
recipeLabelrgba(80,70,60,0.60)rgba(200,170,130,0.55)Recipe name — context, not focus
nowLine#7A5737#C49A6CVertical now-line — 2px, accent-strong
nowGlowrgba(166,124,82,0.18)rgba(196,154,108,0.22)Warm glow band behind now-line
timeTickrgba(0,0,0,0.15)rgba(255,255,255,0.15)Tick marks — quiet, non-distracting
timeLabelrgba(0,0,0,0.65)rgba(255,255,255,0.65)Time axis labels — clear hierarchy
rowBgrgba(0,0,0,0.035)rgba(255,255,255,0.05)Alternating row stripe — scanability

WebGL bg (dark): vec3(0.155, 0.125, 0.095) / vec3(0.195, 0.160, 0.120) with gold glow at now-line position. Light: vec3(0.969, 0.961, 0.949) / vec3(0.950, 0.943, 0.933).

PlanGanttGL

Static WebGL background + Canvas 2D overlay. Tasks grouped by recipe. Re-draws on resize and theme change. Source: components/PlanGanttGL.vue. Colors from composables/useChartColors.ts.

Light
Dark
Color tokenLightDarkUsed for
active#9B6F46#D2A874Hands-on bar — wood accent, dominant
passivergba(160,170,180,0.45)rgba(190,200,210,0.32)Hands-off bar — metal/neutral, subordinate
taskLabel#ffffff#F1E7D8Task name text inside bar
recipeLabelrgba(80,70,60,0.60)rgba(200,170,130,0.55)Recipe name — context, not focus
timeTickrgba(0,0,0,0.15)rgba(255,255,255,0.15)Tick marks — quiet, non-distracting
timeLabelrgba(0,0,0,0.65)rgba(255,255,255,0.65)Time axis labels — clear hierarchy
rowBgrgba(0,0,0,0.035)rgba(255,255,255,0.05)Alternating group stripe — scanability

All colors shared via useChartColors() composable. WebGL bg same as NowLineGL (static, no pulse/glow). Layout: LABEL_H=13 + BAR_H=22 per task row, TASK_GAP=6 within recipe, GROUP_GAP=14 between recipes.

GanttGL

WebGL + Canvas 2D Gantt chart. Accepts raw RecipeGantt[] data and handles CPM scheduling internally. Renders each recipe as a horizontal row with colour-coded bars per task. Supports an optional currentMinute prop to draw a live "now" line. Source: components/GanttGL.vue.

Light
Dark
Light — with now line (currentMinute=20)
Dark — with now line (currentMinute=20)
Color tokenLightDarkUsed for
Recipe colours[0.478,0.329,0.188] / [0.620,0.455,0.239] / [0.678,0.545,0.341]Cycling warm-wood palette per recipe
passivergb(122,130,139)rgb(74,65,56)Hands-off bar — muted, recedes behind active bars
bgrgb(247,245,242)rgb(24,20,15)Canvas background
gridrgba(0,0,0,0.06)rgba(255,255,255,0.07)Vertical tick lines
seprgba(0,0,0,0.04)rgba(255,255,255,0.05)Row separator lines
tickTextrgba(46,46,46,0.75)rgba(234,224,213,0.65)Time axis labels
rowTextrgba(46,46,46,1)rgba(234,224,213,1)Recipe name + task labels
NOW_COLrgba(240,69,69,1)Now-line — red, 2px
PropTypeNotes
ganttRecipeGantt[]Raw recipe + task data. CPM scheduling computed internally.
currentMinutenumber?If provided, draws a red vertical now-line at that minute.

Layout constants: LABEL_W=240 · PX_MIN=9px per minute · AXIS_H=32 · REC_H=34 recipe label row · ROW_H=56 task row · BAR_H=26 bar height · BAR_TOP=15 offset. Scrollable horizontally via .ggl-scroll.

AppTopbar

Sticky top navigation bar. Uses a 3-column CSS grid (1fr auto 1fr) so the center nav stays truly centred regardless of what's in the left or right columns. Component: components/AppTopbar.vue. Shared styles in assets/css/main.styl.

Live example

Layout

SlotClassContents
#left.topbar-leftLogo + Plan/Cook toggle (PlanCookToggle). display:flex; gap:0.75rem.
#center.topbar-centerSection pills (PlanNav or CookNav). Centred with justify-content:center.
default.topbar-actionsAuth buttons, share button, locale, theme toggle. justify-self:end.

Section pills — .section-pill

PropertyValueNotes
Font size (desktop)0.83remReduced to 0.72rem below 700px
Font weight500 · active: 600
Default colorvar(--text-muted)#6B6560 light / #8A8076 dark
Active colorvar(--wood-dark)#A0744A light / #C49A6C dark
Active indicatorborder-bottom: 2px solid var(--wood-dark)Flush with topbar bottom border
Hover colorvar(--text)Border shows var(--border) on hover
Icon wrapper.pill-iconHidden below 700px to save space

Plan / Cook toggle — .pct

PropertyValueNotes
Font size0.83rem
Font weight500
Default colorvar(--text-muted)
Active colorvar(--text)Active segment gets background: var(--bg)
Border1px solid var(--border)Wraps the whole toggle; divider between segments
Border radiusvar(--radius)6px
Mobile.pct hidden; .pct-mobile shownSingle directional link, 0.72rem, hidden below 700px toggle

Share / Copy link button — .plan-share-btn

BreakpointAppearance
> 700pxGhost button — 1px solid var(--border), icon + "Copy link" label, 0.82rem. Hover: border-color: var(--wood), bg: var(--ghost-hover)
≤ 700pxBorderless icon button — no border, color: var(--text-muted), min-height: 44px, label hidden
≤ 300pxHidden (display:none)

Topbar colors

TokenLightDarkUsed for
--surface#FFFFFF#231E17Topbar background
--border#DAD6CF#3A342CBottom border, toggle border, pill hover indicator
--text#2E2E2E#F0E8DCActive pill, active toggle segment, share button text
--text-muted#6B6560#8A8076Inactive pills, toggle default, mobile icon buttons
--wood-dark#A0744A#C49A6CActive pill color + underline
--wood#D2B48C#9A6E42Share button hover border
--ghost-hover#fdf8f3#2A231AShare button hover background
--bg#F7F5F2#18140FActive toggle segment background

Responsive behaviour

BreakpointChanges
> 700pxFull layout. Auth links, locale, theme toggle visible. Plan/Cook toggle visible. Pill icons visible.
≤ 700pxAuth links hidden (.topbar-desktop-only). Hamburger menu shown. Plan/Cook toggle collapses to single .pct-mobile link. Pill icons hidden. Topbar padding reduced to 0.5rem 1rem. Pills shrink to 0.72rem.
≤ 350pxShop + Chop pill links hidden (.plan-nav-pill).
≤ 300pxShare button and fullscreen button hidden.

Props

PropTypeEffect
herobooleanSwitches to .hero-topbar class — transparent background, used on the home page hero
hideMenubooleanSuppresses the mobile hamburger menu — used on /cook/live where the fullscreen button is the sole mobile action

Icons

Sprite icons via AppIcon component (<AppIcon name="…" :size="16" />). All use currentColor and respond to light/dark mode. Sprite defined in AppSprite.vue, injected once in app.vue.

Sprite icons

Shopping list
icon-shopping · section pill, home card
Grocery bag
icon-bag
Knife
icon-knife · section pill (mise)
Cutting board
icon-board
Timer
icon-timer
Clock
icon-clock · home card (schedule)
Flame
icon-flame · home card (cook)
Utensils
icon-utensils · home card (dinner party)
Prep list
icon-prep
Gantt
icon-gantt · section pill (schedule)
Play
icon-play · cook mode, resume button
Pause
icon-pause · active card, controls menu
Reset
icon-reset · controls menu
Check
icon-check
Plus
icon-plus
Chevron right
icon-chevron-right
Menu
icon-menu
Share
icon-share · copy link button

Additional sprite icons

Controls menu
icon-controls · LiveControlsMenu.vue
Audio settings
icon-audio · LiveAudioMenu.vue
Google logo
icon-google · fixed brand colours · dark only