Cookbook — Recipe Feature / Food Story (long-form)
A magazine-style long-form food story with a full-bleed gradient hero, editorial serif typography, a drop-cap intro, pull-quotes, captioned CSS food photos, and an embedded print-friendly recipe card. A reading-progress bar tracks scroll depth, a sticky byline follows the reader, and a jump-to-recipe button smooth-scrolls past the prose straight to the ingredients and method. Built with vanilla HTML, CSS, and JavaScript using only gradients and emoji for food imagery.
MCP
Code
:root {
--cream: #faf6ef;
--paper: #fffdf8;
--ink: #2b2622;
--ink-2: #5c534a;
--muted: #8a7f73;
--tomato: #d6452b;
--tomato-d: #b8351e;
--saffron: #e8a33d;
--sage: #7c8a6b;
--clay: #c8775a;
--line: rgba(43, 38, 34, 0.12);
--line-2: rgba(43, 38, 34, 0.2);
--ok: #3f8f5f;
--warn: #d98a2b;
--danger: #c8412b;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 22px;
--sh-1: 0 1px 2px rgba(43, 38, 34, 0.1);
--sh-2: 0 10px 30px rgba(43, 38, 34, 0.1);
--serif: "Fraunces", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--cream);
color: var(--ink);
font-family: var(--sans);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
:focus-visible {
outline: 3px solid var(--saffron);
outline-offset: 2px;
border-radius: 4px;
}
/* ---------- Reading progress ---------- */
.progress {
position: fixed;
top: 0; left: 0; right: 0;
height: 4px;
background: transparent;
z-index: 60;
}
.progress__bar {
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--saffron), var(--tomato));
transition: width 0.08s linear;
}
/* ---------- Topbar ---------- */
.topbar {
position: sticky;
top: 0;
z-index: 50;
display: flex;
align-items: center;
gap: 1rem;
padding: 0.7rem 1.25rem;
background: rgba(255, 253, 248, 0.85);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.topbar__brand {
font-family: var(--serif);
font-weight: 700;
font-size: 1.05rem;
color: var(--ink);
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
.topbar__mark { font-size: 1.2rem; }
.topbar__nav {
display: flex;
gap: 1.1rem;
margin-left: auto;
}
.topbar__nav a {
color: var(--ink-2);
text-decoration: none;
font-size: 0.9rem;
font-weight: 500;
}
.topbar__nav a:hover { color: var(--tomato); }
.topbar__jump { margin-left: 0.5rem; }
/* ---------- Buttons ---------- */
.btn {
font-family: var(--sans);
font-weight: 600;
font-size: 0.85rem;
border: 0;
cursor: pointer;
border-radius: 999px;
padding: 0.55rem 1.1rem;
transition: transform 0.12s ease, background 0.18s ease, box-shadow 0.18s ease;
}
.btn:active { transform: translateY(1px) scale(0.99); }
.btn--jump {
background: var(--tomato);
color: #fff;
box-shadow: var(--sh-1);
}
.btn--jump:hover { background: var(--tomato-d); }
.btn--ghost {
background: transparent;
color: var(--ink);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
}
.btn--ghost:hover {
background: rgba(232, 163, 61, 0.14);
border-color: var(--saffron);
}
/* ---------- Hero ---------- */
.hero {
position: relative;
isolation: isolate;
min-height: clamp(440px, 70vh, 660px);
display: flex;
align-items: flex-end;
overflow: hidden;
}
.hero__photo {
position: absolute;
inset: 0;
z-index: -1;
background:
radial-gradient(60% 50% at 20% 25%, rgba(232, 163, 61, 0.55), transparent 70%),
radial-gradient(70% 60% at 85% 30%, rgba(214, 69, 43, 0.6), transparent 70%),
radial-gradient(80% 70% at 60% 95%, rgba(124, 138, 107, 0.45), transparent 75%),
linear-gradient(135deg, #7a2417, #b8351e 45%, #8a4a1f 100%);
}
.hero__photo::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(20, 12, 8, 0.7), rgba(20, 12, 8, 0.05) 55%, transparent);
}
.hero__emoji {
position: absolute;
filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.35));
opacity: 0.9;
animation: float 9s ease-in-out infinite;
}
.hero__emoji--1 { font-size: 5rem; top: 16%; left: 12%; }
.hero__emoji--2 { font-size: 4rem; top: 30%; right: 16%; animation-delay: -3s; }
.hero__emoji--3 { font-size: 3.4rem; top: 55%; left: 28%; animation-delay: -6s; }
@keyframes float {
0%, 100% { transform: translateY(0) rotate(-4deg); }
50% { transform: translateY(-16px) rotate(4deg); }
}
@media (prefers-reduced-motion: reduce) {
.hero__emoji { animation: none; }
html { scroll-behavior: auto; }
}
.hero__inner {
position: relative;
max-width: 820px;
margin: 0 auto;
width: 100%;
padding: 2.5rem 1.5rem;
color: #fff;
}
.overline {
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 0.72rem;
font-weight: 700;
color: var(--saffron);
margin: 0 0 0.8rem;
}
.overline--center { text-align: center; color: var(--tomato); }
.hero__title {
font-family: var(--serif);
font-weight: 600;
font-size: clamp(2.1rem, 6vw, 4rem);
line-height: 1.04;
margin: 0 0 0.9rem;
letter-spacing: -0.01em;
text-wrap: balance;
}
.hero__dek {
font-size: clamp(1.05rem, 2.4vw, 1.4rem);
max-width: 38ch;
margin: 0 0 1.6rem;
color: rgba(255, 255, 255, 0.92);
}
/* ---------- Byline ---------- */
.byline {
display: flex;
align-items: center;
gap: 0.9rem;
flex-wrap: wrap;
}
.byline.is-sticky {
position: sticky;
top: 56px;
z-index: 40;
}
.byline__avatar {
width: 44px;
height: 44px;
flex: 0 0 auto;
border-radius: 50%;
display: grid;
place-items: center;
font-family: var(--serif);
font-weight: 700;
font-size: 1.2rem;
color: #fff;
background: linear-gradient(135deg, var(--clay), var(--tomato));
border: 2px solid rgba(255, 255, 255, 0.55);
}
.byline__meta { display: flex; flex-direction: column; line-height: 1.3; }
.byline__name { font-weight: 700; font-size: 0.95rem; }
.byline__sub { font-size: 0.8rem; color: rgba(255, 255, 255, 0.8); }
.byline__jump { margin-left: auto; }
/* ---------- Story ---------- */
.story {
max-width: 680px;
margin: 0 auto;
padding: 3rem 1.5rem 2rem;
font-size: 1.18rem;
color: var(--ink-2);
}
.story p { margin: 0 0 1.5rem; }
.lede {
font-size: 1.32rem;
color: var(--ink);
}
.dropcap {
float: left;
font-family: var(--serif);
font-weight: 700;
font-size: 4.2rem;
line-height: 0.78;
padding: 0.1rem 0.7rem 0 0;
color: var(--tomato);
}
.story__h2 {
font-family: var(--serif);
font-weight: 600;
font-size: 1.85rem;
color: var(--ink);
margin: 2.5rem 0 1rem;
letter-spacing: -0.01em;
}
.story__sign {
font-family: var(--serif);
font-style: italic;
color: var(--muted);
}
/* ---------- Figures ---------- */
.figure {
margin: 2.2rem 0;
}
.figure--wide {
margin-left: calc(-1 * min(8vw, 80px));
margin-right: calc(-1 * min(8vw, 80px));
}
.figure__photo {
border-radius: var(--r-lg);
aspect-ratio: 16 / 10;
display: grid;
place-items: center;
font-size: 3.4rem;
box-shadow: var(--sh-2);
border: 6px solid var(--paper);
}
.figure__photo--sofrito {
background:
radial-gradient(50% 60% at 30% 40%, rgba(232, 163, 61, 0.85), transparent 70%),
radial-gradient(60% 60% at 75% 65%, rgba(124, 138, 107, 0.8), transparent 72%),
linear-gradient(135deg, #e8a33d, #c8775a 60%, #7c8a6b);
}
.figure__photo--simmer {
aspect-ratio: 21 / 9;
background:
radial-gradient(40% 70% at 50% 60%, rgba(20, 10, 6, 0.5), transparent 70%),
radial-gradient(60% 60% at 30% 30%, rgba(214, 69, 43, 0.9), transparent 70%),
linear-gradient(135deg, #b8351e, #7a2417 70%, #4a1a10);
}
.figure figcaption {
margin-top: 0.7rem;
font-size: 0.9rem;
color: var(--muted);
font-family: var(--sans);
line-height: 1.5;
}
/* ---------- Pull quotes ---------- */
.pullquote {
margin: 2.4rem 0;
padding: 0 0 0 1.4rem;
border-left: 4px solid var(--saffron);
}
.pullquote--right {
border-left: 0;
border-right: 4px solid var(--tomato);
padding: 0 1.4rem 0 0;
text-align: right;
}
.pullquote p {
font-family: var(--serif);
font-weight: 500;
font-style: italic;
font-size: 1.55rem;
line-height: 1.3;
color: var(--ink);
margin: 0 0 0.5rem;
}
.pullquote cite {
font-style: normal;
font-size: 0.85rem;
color: var(--muted);
font-family: var(--sans);
}
/* ---------- Recipe card ---------- */
.recipe {
padding: 1rem 1.5rem 4rem;
scroll-margin-top: 70px;
}
.recipe__card {
max-width: 760px;
margin: 0 auto;
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-2);
padding: 2.4rem 2rem;
}
.recipe__title {
font-family: var(--serif);
font-weight: 600;
font-size: clamp(1.8rem, 4vw, 2.6rem);
text-align: center;
margin: 0.2rem 0 0.5rem;
color: var(--ink);
}
.recipe__dek {
text-align: center;
color: var(--ink-2);
max-width: 46ch;
margin: 0 auto 1.6rem;
}
.recipe__stats {
list-style: none;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.6rem;
padding: 0;
margin: 0 0 2rem;
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
padding: 1rem 0;
}
.recipe__stats li {
display: flex;
flex-direction: column;
align-items: center;
min-width: 88px;
padding: 0 0.6rem;
}
.recipe__stats li + li { border-left: 1px solid var(--line); }
.recipe__statk {
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
font-weight: 600;
}
.recipe__statv {
font-family: var(--serif);
font-weight: 600;
font-size: 1.2rem;
color: var(--ink);
}
.recipe__cols {
display: grid;
grid-template-columns: 0.85fr 1.15fr;
gap: 2rem;
}
.recipe__h3 {
font-family: var(--serif);
font-weight: 600;
font-size: 1.25rem;
margin: 0 0 0.8rem;
color: var(--tomato);
}
.recipe__ing ul { margin: 0; padding-left: 1.1rem; color: var(--ink-2); }
.recipe__ing li { margin-bottom: 0.5rem; }
.recipe__steps ol { margin: 0; padding-left: 1.2rem; color: var(--ink-2); }
.recipe__steps li { margin-bottom: 0.7rem; }
.recipe__foot {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.7rem;
margin-top: 2rem;
padding-top: 1.4rem;
border-top: 1px solid var(--line);
}
.recipe__note {
font-size: 0.85rem;
color: var(--muted);
margin-left: auto;
}
/* ---------- Footer ---------- */
.foot {
text-align: center;
padding: 2rem 1.5rem 3rem;
color: var(--muted);
font-size: 0.85rem;
border-top: 1px solid var(--line);
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 1.5rem;
transform: translate(-50%, 20px);
background: var(--ink);
color: var(--paper);
padding: 0.7rem 1.2rem;
border-radius: 999px;
font-size: 0.88rem;
font-weight: 500;
box-shadow: var(--sh-2);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.25s ease;
z-index: 80;
}
.toast.is-show {
opacity: 1;
transform: translate(-50%, 0);
}
/* ---------- Responsive ---------- */
@media (max-width: 720px) {
.recipe__cols { grid-template-columns: 1fr; gap: 1.4rem; }
.figure--wide { margin-left: 0; margin-right: 0; }
.topbar__nav { display: none; }
.byline.is-sticky { position: static; }
.recipe__note { margin-left: 0; width: 100%; }
}
@media (max-width: 420px) {
.topbar__jump { display: none; }
.recipe__stats li { min-width: 70px; }
}
/* ---------- Print ---------- */
@media print {
.progress, .topbar, .hero, .story, .foot, .toast, .recipe__foot { display: none !important; }
body { background: #fff; }
.recipe__card { box-shadow: none; border: 1px solid #000; }
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
}, 2400);
}
/* ---------- Reading progress bar ---------- */
var bar = document.getElementById("progressBar");
var progressWrap = document.querySelector(".progress");
function updateProgress() {
var doc = document.documentElement;
var scrollTop = window.scrollY || doc.scrollTop;
var max = (doc.scrollHeight - doc.clientHeight) || 1;
var pct = Math.min(100, Math.max(0, (scrollTop / max) * 100));
if (bar) bar.style.width = pct.toFixed(1) + "%";
if (progressWrap) progressWrap.setAttribute("aria-valuenow", Math.round(pct));
}
/* ---------- Sticky byline ---------- */
var byline = document.getElementById("byline");
var bylinePlaceholder = null;
function updateStickyByline() {
if (!byline) return;
// Make byline sticky once the hero has scrolled mostly past.
var hero = document.querySelector(".hero");
if (!hero) return;
var heroBottom = hero.getBoundingClientRect().bottom;
if (heroBottom < 80) {
if (!byline.classList.contains("is-sticky")) byline.classList.add("is-sticky");
} else {
byline.classList.remove("is-sticky");
}
}
function onScroll() {
updateProgress();
updateStickyByline();
}
var ticking = false;
window.addEventListener("scroll", function () {
if (!ticking) {
window.requestAnimationFrame(function () {
onScroll();
ticking = false;
});
ticking = true;
}
}, { passive: true });
window.addEventListener("resize", onScroll);
onScroll();
/* ---------- Jump to recipe ---------- */
var recipe = document.getElementById("recipe");
function jumpToRecipe() {
if (!recipe) return;
recipe.scrollIntoView({ behavior: "smooth", block: "start" });
// Move focus for accessibility after the scroll settles.
setTimeout(function () {
recipe.focus({ preventScroll: true });
}, 600);
toast("Skipping to the good part 🍝");
}
document.querySelectorAll("[data-jump]").forEach(function (btn) {
btn.addEventListener("click", jumpToRecipe);
});
/* ---------- Back to top ---------- */
document.querySelectorAll("[data-top]").forEach(function (btn) {
btn.addEventListener("click", function () {
var top = document.getElementById("top");
if (top) top.scrollIntoView({ behavior: "smooth", block: "start" });
});
});
/* ---------- Print ---------- */
document.querySelectorAll("[data-print]").forEach(function (btn) {
btn.addEventListener("click", function () {
toast("Opening print view…");
setTimeout(function () { window.print(); }, 250);
});
});
/* ---------- Smooth anchor nav ---------- */
document.querySelectorAll('.topbar__nav a[href^="#"]').forEach(function (a) {
a.addEventListener("click", function (e) {
var id = a.getAttribute("href").slice(1);
var target = document.getElementById(id);
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: "smooth", block: "start" });
}
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>The Slow Sunday Ragù — A Food Story</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,[email protected],500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Reading progress -->
<div class="progress" role="progressbar" aria-label="Reading progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress__bar" id="progressBar"></div>
</div>
<header class="topbar">
<a class="topbar__brand" href="#top">
<span class="topbar__mark">🍅</span> Saffron & Sage
</a>
<nav class="topbar__nav" aria-label="Sections">
<a href="#story">Story</a>
<a href="#recipe">Recipe</a>
</nav>
<button class="btn btn--jump topbar__jump" data-jump aria-label="Jump to the recipe">Jump to recipe ↓</button>
</header>
<main id="top">
<!-- HERO -------------------------------------------------------------->
<header class="hero" role="banner">
<div class="hero__photo" aria-hidden="true">
<span class="hero__emoji hero__emoji--1">🍅</span>
<span class="hero__emoji hero__emoji--2">🌿</span>
<span class="hero__emoji hero__emoji--3">🧄</span>
</div>
<div class="hero__inner">
<p class="overline">Sunday Suppers · Issue No. 14</p>
<h1 class="hero__title">The Slow Sunday Ragù That Taught Me to Wait</h1>
<p class="hero__dek">Six hours, one battered pot, and a sauce that asks nothing of you but patience — a love letter to the long simmer.</p>
<div class="byline" id="byline">
<span class="byline__avatar" aria-hidden="true">M</span>
<div class="byline__meta">
<span class="byline__name">By Mira Delacroix</span>
<span class="byline__sub">Food Editor · <span class="byline__time">9 min read</span> · June 15, 2026</span>
</div>
<button class="btn btn--jump byline__jump" data-jump>Jump to recipe ↓</button>
</div>
</div>
</header>
<!-- STORY ------------------------------------------------------------->
<article class="story" id="story">
<p class="lede"><span class="dropcap">T</span>here is a particular silence that settles over a kitchen at two in the afternoon, when the onions have gone soft and gold and the whole apartment smells of something that won't be ready for hours. I used to fear that silence. Now I chase it.</p>
<p>My grandmother never owned a timer. She judged her ragù by the way the surface of the sauce moved — a lazy blip, never a boil — and by the color, which shifted from bright tomato red to a deep, brooding brick over the course of an afternoon. "The sauce tells you," she'd say, waving a wooden spoon stained the color of autumn. For years I thought this was mysticism. It is, in fact, chemistry, and the most generous kind: it rewards anyone willing to do almost nothing for a very long time.</p>
<figure class="figure">
<div class="figure__photo figure__photo--sofrito" aria-hidden="true">
<span>🥕🧅🌿</span>
</div>
<figcaption>The <em>soffritto</em> — carrot, onion, celery — cooked low until it nearly melts. This is where flavor is built, slowly, before a single tomato enters the pot.</figcaption>
</figure>
<blockquote class="pullquote">
<p>"Good ragù is not cooked. It is coaxed."</p>
<cite>— Nonna Lucia, on the only rule that matters</cite>
</blockquote>
<p>The mistake most people make is heat. They want the sauce to hurry, so they crank the burner and stand over it, stirring anxiously, and what they get is something thin and sharp that tastes of impatience. A true Sunday ragù barely simmers. You set it, you crack a window, you go read on the couch. The collagen in the meat slowly surrenders; the tomatoes lose their acid edge; the wine cooks down into something round and dark. None of it can be rushed, and that is the entire point.</p>
<h2 class="story__h2">A pot, a window, an afternoon</h2>
<p>I make mine in a wide enameled pot — the kind that holds heat like a grudge. The meat is browned in batches so it sears rather than steams, then set aside while the soffritto does its quiet work. Tomato paste goes in to caramelize until it smells almost like toffee. Then the wine, the crushed tomatoes, a parmesan rind, and the browned meat back home in the pot. After that, your only job is to leave it alone.</p>
<figure class="figure figure--wide">
<div class="figure__photo figure__photo--simmer" aria-hidden="true">
<span>🍲</span>
</div>
<figcaption>Hour four. The surface barely moves — one slow bubble, then another. The color has turned from red to brick.</figcaption>
</figure>
<p>Somewhere around hour four the apartment changes. The smell stops being "cooking" and becomes a kind of weather, something you live inside. Neighbors text. People wander into the kitchen "just to check." This is the real recipe — not the quantities below, but the gravity a long-simmering pot exerts on a Sunday, pulling everyone toward the stove.</p>
<blockquote class="pullquote pullquote--right">
<p>"You are not making dinner. You are making an afternoon."</p>
</blockquote>
<p>When it's finally done — when the meat falls apart at the nudge of a spoon and the fat has risen into a glossy, garnet-colored slick — you fold it into wide ribbons of fresh pasta, shower it with parmesan, and eat it slowly, because by now you've learned how. The recipe is below. Take your time with it. That's the only ingredient that matters.</p>
<p class="story__sign">— M.D.</p>
</article>
<!-- RECIPE CARD ------------------------------------------------------->
<section class="recipe" id="recipe" aria-label="Recipe card" tabindex="-1">
<div class="recipe__card">
<p class="overline overline--center">The Recipe</p>
<h2 class="recipe__title">Slow Sunday Ragù</h2>
<p class="recipe__dek">A low-and-slow meat sauce for fresh pasta. Mostly hands-off, deeply hands-on with patience.</p>
<ul class="recipe__stats" aria-label="Recipe facts">
<li><span class="recipe__statk">Prep</span><span class="recipe__statv">25 min</span></li>
<li><span class="recipe__statk">Cook</span><span class="recipe__statv">6 hr</span></li>
<li><span class="recipe__statk">Serves</span><span class="recipe__statv">6</span></li>
<li><span class="recipe__statk">Level</span><span class="recipe__statv">Easy-ish</span></li>
</ul>
<div class="recipe__cols">
<div class="recipe__ing">
<h3 class="recipe__h3">Ingredients</h3>
<ul>
<li>2 tbsp olive oil</li>
<li>1 lb (450 g) ground beef chuck</li>
<li>½ lb (225 g) ground pork</li>
<li>1 onion, 1 carrot, 1 celery rib — finely diced</li>
<li>3 cloves garlic, minced</li>
<li>3 tbsp tomato paste</li>
<li>1 cup (240 ml) dry red wine</li>
<li>1 can (28 oz) crushed tomatoes 🍅</li>
<li>1 parmesan rind + 1 bay leaf</li>
<li>½ cup (120 ml) whole milk</li>
<li>Salt, pepper, a fistful of patience</li>
</ul>
</div>
<div class="recipe__steps">
<h3 class="recipe__h3">Method</h3>
<ol>
<li>Brown the beef and pork in batches over high heat; remove and set aside.</li>
<li>Lower the heat. Soften the onion, carrot, celery and garlic until nearly melting, 12–15 min.</li>
<li>Stir in tomato paste; cook until it darkens and smells sweet, ~3 min.</li>
<li>Deglaze with wine; scrape the pot and reduce by half.</li>
<li>Return the meat. Add crushed tomatoes, parmesan rind and bay. Bring to the barest simmer.</li>
<li>Cover loosely and cook 5–6 hours, stirring now and then. It should blip, never boil.</li>
<li>Stir in milk in the final 30 min. Season. Toss with fresh pasta and lots of parmesan.</li>
</ol>
</div>
</div>
<div class="recipe__foot">
<button class="btn btn--ghost" data-print>🖨️ Print recipe</button>
<button class="btn btn--ghost" data-top>↑ Back to story</button>
<span class="recipe__note">🌿 Tip: make it a day ahead — it's even better reheated.</span>
</div>
</div>
</section>
</main>
<footer class="foot">
<p>Saffron & Sage · Sunday Suppers · A fictional food story for demo purposes.</p>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Recipe Feature / Food Story (long-form)
A polished editorial template for a long-form food feature. The piece opens with a full-bleed hero built from warm CSS gradients and floating food emoji, an overline kicker, a big serif headline, a dek, and a byline with avatar and read time. The body uses a drop-cap intro, generous serif/sans typography, alternating pull-quotes, and inline “photo” figures (gradient placeholders with captions) that look intentional rather than empty.
Interactions are all vanilla JS. A reading-progress bar pinned to the top fills as you scroll, the byline becomes sticky once the hero scrolls past, and a “Jump to recipe” button (in the topbar, hero byline, and elsewhere) smooth-scrolls down to an embedded recipe card, moving focus there for keyboard and screen-reader users. The recipe card itself is a clean, print-friendly layout with prep/cook/serves stats, a two-column ingredients-and-method grid, and print plus back-to-story actions.
It is responsive from ~360px to desktop — the recipe grid and wide figures collapse to a single column by 720px — and respects prefers-reduced-motion and @media print.
Illustrative UI only — recipes & nutrition data are fictional, not dietary advice.