- Adopt BoardUI (Pro init): theme tokens, Inter, .dark mode, Button, SegmentedControl, Meter, Skeleton and ThemeToggle; remap the app's color aliases onto BoardUI tokens and swap lucide for Remix icons - Retint BoardUI's neutral ramp to cool slate with a salmon accent; drop the dotted background; raise muted text contrast - Simplify the landing page and remove the overlap explorer - Planner: explicit "I have this" toggle on grocery lines, pantry panel above the menu with the staples list spelled out, sticky aisle header fix, and a "Download PDF" plan export (jsPDF, lazy-loaded) - Recipes: replace the family cabbage soup with Cade's Cabbage Soup (adds Bone-In Ham Steak to the catalog); remove Ham, Bean & Cabbage Soup - Remove unused BoardUI components, dependencies and exports Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
5.5 KiB
TypeScript
108 lines
5.5 KiB
TypeScript
import { RiArrowRightLine as ArrowRight, RiHeart3Line as BookHeart } from "@remixicon/react";
|
|
import type { Metadata } from "next";
|
|
import Link from "next/link";
|
|
import { ViewTransition } from "react";
|
|
import { ApplianceIcon } from "@/app/components/Icons";
|
|
import { SiteHeader } from "@/app/components/SiteHeader";
|
|
import { APPLIANCES } from "@/app/lib/plan";
|
|
import { RECIPES } from "@/app/lib/recipes";
|
|
import type { Recipe } from "@/app/types";
|
|
|
|
export const metadata: Metadata = {
|
|
title: "Cayden's Corner",
|
|
description: "Homemade family recipes: tried, tested and loved.",
|
|
};
|
|
|
|
const FAMILY = RECIPES.filter((r) => r.styles.includes("caydens_corner"));
|
|
|
|
const SECTIONS: { title: string; recipes: Recipe[] }[] = [
|
|
{ title: "Appetizers & Side Dishes", recipes: FAMILY.filter((r) => r.kind === "side") },
|
|
{ title: "Main Dishes", recipes: FAMILY.filter((r) => r.kind === "main" || r.kind === "breakfast") },
|
|
{ title: "Desserts & Bakes", recipes: FAMILY.filter((r) => r.kind === "dessert") },
|
|
].map((s) => ({ ...s, recipes: [...s.recipes].sort((a, b) => a.title.localeCompare(b.title)) }));
|
|
|
|
export default function CaydensCornerPage() {
|
|
return (
|
|
<div className="min-h-dvh">
|
|
<SiteHeader active="caydens-corner" />
|
|
<main className="mx-auto max-w-7xl px-4 py-10 sm:px-6">
|
|
<section className="rounded-2xl border border-brand-border bg-brand-950 px-6 py-10 sm:px-10">
|
|
<p className="inline-flex items-center gap-2 font-mono text-xs font-semibold tracking-widest text-brand-300 uppercase">
|
|
<BookHeart className="size-4" strokeWidth={2} aria-hidden />
|
|
Cayden's Corner · Heirloom Cookbook
|
|
</p>
|
|
<h1 className="mt-2 font-serif text-3xl font-semibold tracking-tight text-balance sm:text-5xl">
|
|
Family Recipes
|
|
</h1>
|
|
<p className="mt-3 max-w-2xl text-base leading-relaxed text-ink-muted sm:text-lg">
|
|
{FAMILY.length} homemade family favorites—passed down, tested at the dinner table, and loved. Comfort classics,
|
|
holiday staples, and plenty of scratch-baked desserts.
|
|
</p>
|
|
<div className="mt-6 flex flex-wrap items-center gap-4 border-t border-brand-border/40 pt-5 text-sm text-ink-muted">
|
|
<p>
|
|
Written originally for a table of four with leftovers; the planner scales every ingredient automatically to your household.
|
|
</p>
|
|
<Link
|
|
href="/plan"
|
|
className="trim-cap ml-auto inline-flex min-h-10 items-center gap-1.5 rounded-lg border border-line-strong bg-surface px-4 py-2 text-sm font-semibold text-ink transition-colors hover:bg-raised"
|
|
>
|
|
Plan a family-recipe week
|
|
<ArrowRight className="size-4" aria-hidden />
|
|
</Link>
|
|
</div>
|
|
</section>
|
|
|
|
{SECTIONS.map((section) => (
|
|
<section key={section.title} className="mt-12" aria-labelledby={`section-${section.title}`}>
|
|
<div className="flex items-baseline justify-between border-b border-line pb-2.5">
|
|
<h2 id={`section-${section.title}`} className="font-serif text-2xl font-semibold tracking-tight">
|
|
{section.title}
|
|
</h2>
|
|
<span className="font-mono text-xs text-ink-faint">{section.recipes.length} recipes</span>
|
|
</div>
|
|
|
|
<ul className="mt-5 grid gap-3.5 sm:grid-cols-2 lg:grid-cols-3">
|
|
{section.recipes.map((r) => {
|
|
const applianceNames =
|
|
r.requiredAppliances.map((a) => APPLIANCES.find((x) => x.id === a)?.label).join(", ") || "No cooking";
|
|
return (
|
|
<li key={r.id}>
|
|
<Link
|
|
href={`/recipes/${r.id}`}
|
|
className="group flex h-full flex-col rounded-3xl border border-border-button-default bg-surface p-4 transition-colors hover:border-line-strong hover:bg-raised/60"
|
|
>
|
|
<ViewTransition name={`recipe-title-${r.id}`}>
|
|
<span className="font-serif text-lg leading-snug font-semibold text-ink group-hover:text-brand-300">
|
|
{r.title}
|
|
</span>
|
|
</ViewTransition>
|
|
<span className="mt-1.5 line-clamp-2 text-sm leading-relaxed text-ink-muted">{r.description}</span>
|
|
|
|
<span className="mt-auto flex items-center justify-between border-t border-line pt-3 mt-4 text-xs text-ink-faint">
|
|
<span className="font-mono tabular-nums">
|
|
{r.prepTime >= 120 ? `${Math.round(r.prepTime / 60)} hr` : `${r.prepTime} min`} · serves {r.servings}
|
|
</span>
|
|
<span className="inline-flex items-center gap-1.5" title={applianceNames}>
|
|
{r.requiredAppliances.length > 0 ? (
|
|
<>
|
|
{r.requiredAppliances.map((a) => (
|
|
<ApplianceIcon key={a} id={a} className="size-3.5 text-ink-muted" />
|
|
))}
|
|
<span className="sr-only">Requires {applianceNames}</span>
|
|
</>
|
|
) : (
|
|
<span>No cooking</span>
|
|
)}
|
|
</span>
|
|
</span>
|
|
</Link>
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
</section>
|
|
))}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|