mirror of
https://github.com/runyanjake/cooking.git
synced 2026-11-12 08:04:51 -08:00
Every top-level :::name block is now a tab, shown in file order, so authors can add custom tabs and reorder them in the markdown. Titles come from the name or a :::name[Title] label. The recipe card is an ordinary :::card tab. Its body lists what it prints (::ingredients, or a block written just for the card), replacing ::card, the fallback to full sections, and its fixed tab position. All recipes are migrated, with pages and card images unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
74 lines
4.0 KiB
Markdown
74 lines
4.0 KiB
Markdown
# Cooking Site — Project Context
|
|
|
|
## What This Is
|
|
|
|
A personal recipe website. Content-first, no-nonsense. The name of the third homepage illustration (`recipe_sites_suck.svg`) sums up the philosophy: no popups, no life stories, just recipes.
|
|
|
|
## Tech Stack
|
|
|
|
- **Next.js 15** with App Router, TypeScript, Tailwind CSS
|
|
- **Markdown** (`.md`) for recipe content with YAML frontmatter; every tab is a markdown directive (`:::ingredients`, `:::card`)
|
|
- **unified + remark-gfm + remark-directive** to parse content server-side; `mdast-util-to-hast` + `hast-util-to-jsx-runtime` to render React
|
|
- **satori + sharp** to generate printable recipe card PNGs at build time
|
|
- **Static site generation (SSG)** — all pages are prerendered at build time
|
|
- **No database** — recipes are markdown files on disk
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
app/ # Next.js App Router pages
|
|
page.tsx # Homepage (server component)
|
|
layout.tsx # Root layout with Header/Footer
|
|
recipes/
|
|
page.tsx # Recipe listing (server, passes data to RecipesClient)
|
|
[category]/[slug]/
|
|
page.tsx # Recipe detail (server, renders the markdown's tabs via RecipePageLayout)
|
|
assets/[...file]/
|
|
route.ts # Serves recipe images from recipes/ (prerendered at build)
|
|
card/[image]/
|
|
route.ts # Recipe card PNGs (card.png, or front.png + back.png)
|
|
|
|
components/
|
|
Header.tsx / Footer.tsx # Site chrome
|
|
RecipesClient.tsx # Recipe listing with filter state
|
|
RecipeLayout.tsx # Recipe listing layout: filter button, Sidebar + results
|
|
Sidebar.tsx # Responsive sidebar shell (mobile drawer, desktop sticky column)
|
|
RecipesSidebar.tsx # Search + category/tag facet lists with result counts
|
|
FacetGroup.tsx # Collapsible sidebar facet section
|
|
ActiveFilters.tsx # Removable chips for active filters (above results)
|
|
RecipeGridCard.tsx # Recipe grid card for listing page
|
|
RecipeTabs.tsx # Tabbed recipe sections (client)
|
|
RecipeMarkdown.tsx # Renders a parsed markdown tree to React (server)
|
|
markdown-styles.ts # Shared body typography classes (recipe tabs, About page)
|
|
RecipeCardPanel.tsx # Recipe Card tab: card image previews (server)
|
|
RecipeCardImage.tsx # Card preview that enlarges to full screen on click (client)
|
|
RecipePageLayout.tsx # Recipe detail page layout (server component)
|
|
|
|
lib/
|
|
recipes.ts # Recipe file loader with in-memory cache; reads from recipes/
|
|
recipe-content.ts # Parses a recipe body into intro / tabs / outro; validates directives
|
|
recipe-content.test.ts # Format tests; also parses every real recipe (npm test)
|
|
recipe-card.tsx # Card layout (measured with satori) and PNG rendering
|
|
recipe-urls.ts # Client-safe helper that maps ./assets/ paths to served URLs
|
|
|
|
recipes/ # ALL recipe content lives here (markdown + images together)
|
|
[category]/
|
|
recipe-slug/
|
|
recipe-slug.md
|
|
assets/
|
|
hero.jpg
|
|
...
|
|
|
|
public/
|
|
assets/ # Site-level images (homepage SVGs)
|
|
authors.json # Author metadata
|
|
```
|
|
|
|
## Design Principles
|
|
|
|
- **Content first**: recipe pages are minimal — no sidebar, just the recipe
|
|
- **Server components by default**: only add `'use client'` when interactivity is needed
|
|
- **The markdown is the recipe**: tabs, their titles and order, and what the card prints are all written in the `.md` file. The renderer only turns what's written into the page; never add hardcoded tab names, ordering, or fallbacks
|
|
- **No taxonomy file**: categories and tags are derived directly from frontmatter across all recipes — no external registry to keep in sync
|
|
- **Content is top level**: markdown and images are colocated in `recipes/`; images are served by a statically prerendered route handler, so there's no copy step. `public/` is only for site branding and data
|