PWS-12: Match About page typography to recipe content

The About page is hand-written JSX, so it never picked up the recipe
styles, and its prose margins stacked with space-y and pt-6. Share the
recipe tab body classes through MARKDOWN_BODY_CLASSES and use them on
About, along with the recipe page header and column layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Olomana (multica agent) 2026-11-06 18:53:47 -08:00
parent 4efb759eec
commit 6c92f2d896
5 changed files with 42 additions and 38 deletions

View File

@ -39,6 +39,7 @@ components/
RecipeGridCard.tsx # Recipe grid card for listing page RecipeGridCard.tsx # Recipe grid card for listing page
RecipeTabs.tsx # Tabbed recipe sections (client) RecipeTabs.tsx # Tabbed recipe sections (client)
RecipeMarkdown.tsx # Renders a parsed markdown tree to React (server) 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) RecipeCardPanel.tsx # Recipe Card tab: card image previews (server)
RecipeCardImage.tsx # Card preview that enlarges to full screen on click (client) RecipeCardImage.tsx # Card preview that enlarges to full screen on click (client)
RecipePageLayout.tsx # Recipe detail page layout (server component) RecipePageLayout.tsx # Recipe detail page layout (server component)

View File

@ -18,4 +18,5 @@
- Card fonts are read from `node_modules/@fontsource/*` at build time; satori needs woff/ttf (not woff2) and doesn't support variable fonts - Card fonts are read from `node_modules/@fontsource/*` at build time; satori needs woff/ttf (not woff2) and doesn't support variable fonts
- `lib/recipes.ts` uses Node.js `fs` — server-side only; never import in client components - `lib/recipes.ts` uses Node.js `fs` — server-side only; never import in client components
- Build warnings about `<img>` vs `<Image />` in RecipeMarkdown and RecipeCardImage are intentional - Build warnings about `<img>` vs `<Image />` in RecipeMarkdown and RecipeCardImage are intentional
- The About page is hand-written JSX, not markdown; it shares body typography with recipe tabs through `MARKDOWN_BODY_CLASSES`. Change spacing there rather than per page. Class strings must live in `components/` or `app/` — Tailwind doesn't scan `lib/`
- Never add redundant ARIA roles on semantic elements (`<main>`, `<aside>`, `<footer>` already carry implicit roles) - Never add redundant ARIA roles on semantic elements (`<main>`, `<aside>`, `<footer>` already carry implicit roles)

View File

@ -1,4 +1,5 @@
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { MARKDOWN_BODY_CLASSES } from '@/components/markdown-styles';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'About - PWS Recipes', title: 'About - PWS Recipes',
@ -7,48 +8,44 @@ export const metadata: Metadata = {
export default function AboutPage() { export default function AboutPage() {
return ( return (
<div className="max-w-3xl space-y-8"> <article className="max-w-4xl mx-auto">
<div className="space-y-4"> <header className="mb-8 space-y-4">
<h1 className="text-4xl font-bold text-gray-900 dark:text-white"> <h1 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white">
About PWS Recipes About PWS Recipes
</h1> </h1>
<p className="text-lg text-gray-600 dark:text-gray-400"> <p className="text-xl text-gray-600 dark:text-gray-400">
Explore recipes in a user-friendly manner on a website that prioritizes your cooking experience over monetizing your clicks. Explore recipes in a user-friendly manner on a website that prioritizes your cooking experience over monetizing your clicks.
</p> </p>
</div> </header>
<div className="prose dark:prose-invert max-w-none"> <div className={`space-y-8 ${MARKDOWN_BODY_CLASSES}
<section className="space-y-4"> [&_h2]:text-2xl [&_h2]:font-bold [&_h2]:text-gray-900 dark:[&_h2]:text-white [&_h2]:mb-3
<h2 className="text-2xl font-semibold text-gray-900 dark:text-white"> `}>
The PWS Mission <section>
</h2> <h2>The PWS Mission</h2>
<p className="text-gray-600 dark:text-gray-400"> <p>
We believe that cooking should be accessible, enjoyable, and creative. Our goal while collecting and organizing these recipes is to make knowledge readily available and keep your cooking experience a cathartic and fun one. We believe that cooking should be accessible, enjoyable, and creative. Our goal while collecting and organizing these recipes is to make knowledge readily available and keep your cooking experience a cathartic and fun one.
</p> </p>
</section> </section>
<section className="space-y-4 pt-6"> <section>
<h2 className="text-2xl font-semibold text-gray-900 dark:text-white"> <h2>Content Structure</h2>
Content Structure <p>
</h2>
<p className="text-gray-600 dark:text-gray-400">
Each recipe is organized in its own folder with: Each recipe is organized in its own folder with:
</p> </p>
<ul className="list-disc list-inside space-y-2 text-gray-600 dark:text-gray-400 ml-4"> <ul>
<li>Markdown file with metadata frontmatter, recipe content, and instructions</li> <li>Markdown file with metadata frontmatter, recipe content, and instructions</li>
<li>Assets folder for images and other media</li> <li>Assets folder for images and other media</li>
</ul> </ul>
</section> </section>
<section className="space-y-4 pt-6"> <section>
<h2 className="text-2xl font-semibold text-gray-900 dark:text-white"> <h2>Technology</h2>
Technology <p>
</h2>
<p className="text-gray-600 dark:text-gray-400">
Built with modern web technologies including Next.js, React, TypeScript, and Tailwind CSS to ensure fast performance. Built with modern web technologies including Next.js, React, TypeScript, and Tailwind CSS to ensure fast performance.
</p> </p>
</section> </section>
</div> </div>
</div> </article>
); );
} }

View File

@ -1,6 +1,7 @@
'use client'; 'use client';
import { useRef, useState, type KeyboardEvent, type ReactNode } from 'react'; import { useRef, useState, type KeyboardEvent, type ReactNode } from 'react';
import { MARKDOWN_BODY_CLASSES } from './markdown-styles';
export interface RecipeTab { export interface RecipeTab {
id: string; id: string;
@ -65,21 +66,7 @@ export default function RecipeTabs({ tabs }: { tabs: RecipeTab[] }) {
id={`tab-panel-${tab.id}`} id={`tab-panel-${tab.id}`}
aria-labelledby={`tab-${tab.id}`} aria-labelledby={`tab-${tab.id}`}
hidden={tab.id !== activeId} hidden={tab.id !== activeId}
className="p-8 max-w-none text-base leading-relaxed className={`p-8 ${MARKDOWN_BODY_CLASSES}`}
[&_h3]:text-lg [&_h3]:font-semibold [&_h3]:text-gray-900 dark:[&_h3]:text-white [&_h3]:mt-5 [&_h3]:mb-2
[&_p]:text-gray-700 dark:[&_p]:text-gray-300 [&_p]:my-2
[&_strong]:text-gray-900 dark:[&_strong]:text-white
[&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:space-y-2 [&_ol]:my-4
[&_ul]:list-disc [&_ul]:pl-6 [&_ul]:space-y-2 [&_ul]:my-4
[&_li]:text-gray-700 dark:[&_li]:text-gray-300
[&_li]:marker:text-gray-500 dark:[&_li]:marker:text-gray-400
[&_code]:text-gray-900 dark:[&_code]:text-gray-100 [&_code]:bg-gray-100 dark:[&_code]:bg-gray-800 [&_code]:rounded [&_code]:px-1.5 [&_code]:py-0.5
[&_pre]:bg-gray-100 dark:[&_pre]:bg-gray-900
[&_table]:border-collapse
[&_th]:border [&_th]:border-gray-300 dark:[&_th]:border-gray-600
[&_td]:border [&_td]:border-gray-300 dark:[&_td]:border-gray-600
[&_figure]:my-6
[&_figcaption]:mt-2 [&_figcaption]:text-center [&_figcaption]:text-sm [&_figcaption]:italic [&_figcaption]:text-gray-600 dark:[&_figcaption]:text-gray-400"
> >
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6"> <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6">
{tab.title} {tab.title}

View File

@ -0,0 +1,18 @@
// Typography for markdown-style body content (recipe tabs, About page).
// Lives in components/ because Tailwind only scans components/ and app/ for class names,
// and outside RecipeTabs because a 'use client' module can't share values with server components.
export const MARKDOWN_BODY_CLASSES = `max-w-none text-base leading-relaxed
[&_h3]:text-lg [&_h3]:font-semibold [&_h3]:text-gray-900 dark:[&_h3]:text-white [&_h3]:mt-5 [&_h3]:mb-2
[&_p]:text-gray-700 dark:[&_p]:text-gray-300 [&_p]:my-2
[&_strong]:text-gray-900 dark:[&_strong]:text-white
[&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:space-y-2 [&_ol]:my-4
[&_ul]:list-disc [&_ul]:pl-6 [&_ul]:space-y-2 [&_ul]:my-4
[&_li]:text-gray-700 dark:[&_li]:text-gray-300
[&_li]:marker:text-gray-500 dark:[&_li]:marker:text-gray-400
[&_code]:text-gray-900 dark:[&_code]:text-gray-100 [&_code]:bg-gray-100 dark:[&_code]:bg-gray-800 [&_code]:rounded [&_code]:px-1.5 [&_code]:py-0.5
[&_pre]:bg-gray-100 dark:[&_pre]:bg-gray-900
[&_table]:border-collapse
[&_th]:border [&_th]:border-gray-300 dark:[&_th]:border-gray-600
[&_td]:border [&_td]:border-gray-300 dark:[&_td]:border-gray-600
[&_figure]:my-6
[&_figcaption]:mt-2 [&_figcaption]:text-center [&_figcaption]:text-sm [&_figcaption]:italic [&_figcaption]:text-gray-600 dark:[&_figcaption]:text-gray-400`;