Cookbook — Printable Recipe Card
A compact, editorial recipe card built to print beautifully on real index stock. It pairs a serif title, a tiny meta row for time and servings, a warm gradient thumbnail, and a condensed two-column layout that splits ingredients from method. A size toggle switches between three-by-five, four-by-six, and full-page formats, while a dedicated print stylesheet strips the chrome to clean black-on-white that fits a single page. Recipe content is fictional and for layout demonstration only.
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-sm: 0 1px 2px rgba(43, 38, 34, 0.1);
--sh-lg: 0 10px 30px rgba(43, 38, 34, 0.1);
--serif: "Fraunces", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
body {
margin: 0;
font-family: var(--sans);
line-height: 1.6;
color: var(--ink);
background:
radial-gradient(900px 500px at 12% -8%, rgba(232, 163, 61, 0.14), transparent 60%),
radial-gradient(800px 500px at 92% 0%, rgba(214, 69, 43, 0.1), transparent 55%),
var(--cream);
min-height: 100vh;
}
/* ---------- Top bar ---------- */
.topbar {
position: sticky;
top: 0;
z-index: 5;
background: rgba(255, 253, 248, 0.86);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--line);
}
.topbar-inner {
max-width: 920px;
margin: 0 auto;
padding: 14px 20px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.brand {
margin: 0;
font-family: var(--serif);
font-weight: 600;
font-size: 1.05rem;
letter-spacing: 0.2px;
}
.controls {
display: flex;
align-items: center;
gap: 12px;
}
.seg {
display: inline-flex;
background: var(--paper);
border: 1px solid var(--line-2);
border-radius: 999px;
padding: 3px;
box-shadow: var(--sh-sm);
}
.seg-btn {
font-family: var(--sans);
font-size: 0.82rem;
font-weight: 600;
color: var(--ink-2);
background: transparent;
border: 0;
padding: 7px 14px;
border-radius: 999px;
cursor: pointer;
transition: background 0.16s ease, color 0.16s ease;
}
.seg-btn:hover {
color: var(--ink);
}
.seg-btn.is-active {
background: var(--ink);
color: var(--paper);
}
.seg-btn:focus-visible {
outline: 2px solid var(--tomato);
outline-offset: 2px;
}
.btn-print {
font-family: var(--sans);
font-size: 0.88rem;
font-weight: 600;
color: var(--paper);
background: var(--tomato);
border: 0;
border-radius: 999px;
padding: 9px 18px;
cursor: pointer;
box-shadow: var(--sh-sm);
display: inline-flex;
align-items: center;
gap: 6px;
transition: background 0.16s ease, transform 0.08s ease;
}
.btn-print:hover {
background: var(--tomato-d);
}
.btn-print:active {
transform: translateY(1px);
}
.btn-print:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 2px;
}
/* ---------- Stage ---------- */
.stage {
max-width: 920px;
margin: 0 auto;
padding: 40px 20px 64px;
display: flex;
flex-direction: column;
align-items: center;
gap: 18px;
}
/* ---------- Recipe card (index card look) ---------- */
.card {
background: var(--paper);
border: 1px solid var(--line-2);
border-radius: var(--r-md);
box-shadow: var(--sh-lg);
position: relative;
overflow: hidden;
width: 100%;
}
/* warm ruled-card accent line */
.card::before {
content: "";
position: absolute;
inset: 0 0 auto 0;
height: 6px;
background: linear-gradient(90deg, var(--tomato), var(--saffron) 55%, var(--clay));
}
.card-head {
display: flex;
gap: 18px;
padding: 26px 26px 14px;
align-items: flex-start;
}
.thumb {
flex: 0 0 auto;
width: 92px;
height: 92px;
border-radius: var(--r-md);
border: 1px solid var(--line);
display: grid;
place-items: center;
position: relative;
overflow: hidden;
background:
radial-gradient(60% 60% at 30% 28%, rgba(255, 255, 255, 0.55), transparent 60%),
radial-gradient(120% 120% at 75% 80%, var(--tomato), var(--tomato-d) 70%),
var(--saffron);
box-shadow: inset 0 0 0 4px rgba(255, 253, 248, 0.6);
}
.thumb-emoji {
font-size: 2.1rem;
filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.25));
}
.head-text {
min-width: 0;
}
.kicker {
margin: 0 0 6px;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--tomato-d);
}
.recipe-title {
margin: 0;
font-family: var(--serif);
font-weight: 600;
font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2rem);
line-height: 1.15;
letter-spacing: -0.01em;
}
.recipe-tagline {
margin: 6px 0 0;
font-size: 0.92rem;
color: var(--ink-2);
font-style: italic;
}
.meta-row {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 0;
padding: 0 26px 18px;
border-bottom: 1px dashed var(--line-2);
}
.meta-row li {
display: inline-flex;
flex-direction: column;
gap: 2px;
padding: 6px 12px;
border-radius: var(--r-sm);
background: rgba(232, 163, 61, 0.1);
min-width: 64px;
}
.meta-k {
font-size: 0.66rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
}
.meta-v {
font-family: var(--serif);
font-weight: 600;
font-size: 0.98rem;
color: var(--ink);
}
.card-body {
display: grid;
grid-template-columns: 0.85fr 1.15fr;
gap: 26px;
padding: 22px 26px;
}
.col-h {
margin: 0 0 10px;
font-family: var(--serif);
font-weight: 600;
font-size: 1.05rem;
display: flex;
align-items: center;
gap: 7px;
}
.col--ing {
border-right: 1px solid var(--line);
padding-right: 22px;
}
.ing-list,
.step-list {
margin: 0;
padding: 0;
}
.ing-list {
list-style: none;
}
.ing-list li {
position: relative;
padding: 4px 0 4px 16px;
font-size: 0.88rem;
color: var(--ink-2);
border-bottom: 1px dotted var(--line);
}
.ing-list li::before {
content: "";
position: absolute;
left: 0;
top: 13px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--sage);
}
.ing-em {
margin-left: 2px;
}
.step-list {
list-style: none;
counter-reset: step;
}
.step-list li {
counter-increment: step;
position: relative;
padding: 6px 0 8px 30px;
font-size: 0.9rem;
color: var(--ink);
}
.step-list li::before {
content: counter(step);
position: absolute;
left: 0;
top: 5px;
width: 21px;
height: 21px;
border-radius: 50%;
background: var(--tomato);
color: var(--paper);
font-family: var(--sans);
font-size: 0.72rem;
font-weight: 700;
display: grid;
place-items: center;
}
.card-foot {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 8px;
padding: 14px 26px 22px;
border-top: 1px dashed var(--line-2);
font-size: 0.76rem;
color: var(--muted);
}
.foot-src em {
color: var(--ink-2);
}
.hint {
margin: 0;
font-size: 0.82rem;
color: var(--muted);
text-align: center;
}
/* ---------- Size variants ---------- */
.card--3x5 {
max-width: 480px;
}
.card--3x5 .card-head {
padding: 18px 18px 10px;
gap: 12px;
}
.card--3x5 .thumb {
width: 64px;
height: 64px;
}
.card--3x5 .thumb-emoji {
font-size: 1.5rem;
}
.card--3x5 .recipe-title {
font-size: 1.3rem;
}
.card--3x5 .recipe-tagline {
font-size: 0.82rem;
}
.card--3x5 .meta-row {
padding: 0 18px 12px;
gap: 6px;
}
.card--3x5 .card-body {
padding: 14px 18px;
gap: 16px;
}
.card--3x5 .ing-list li,
.card--3x5 .step-list li {
font-size: 0.8rem;
}
.card--3x5 .card-foot {
padding: 10px 18px 16px;
}
.card--4x6 {
max-width: 600px;
}
.card--full {
max-width: 820px;
}
.card--full .card-head {
padding: 34px 36px 18px;
}
.card--full .thumb {
width: 120px;
height: 120px;
}
.card--full .thumb-emoji {
font-size: 2.8rem;
}
.card--full .card-body {
padding: 28px 36px;
gap: 36px;
}
.card--full .meta-row {
padding: 0 36px 22px;
}
.card--full .card-foot {
padding: 18px 36px 28px;
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 16px);
background: var(--ink);
color: var(--paper);
font-size: 0.85rem;
font-weight: 500;
padding: 11px 18px;
border-radius: 999px;
box-shadow: var(--sh-lg);
opacity: 0;
pointer-events: none;
transition: opacity 0.22s ease, transform 0.22s ease;
z-index: 20;
}
.toast.show {
opacity: 1;
transform: translate(-50%, 0);
}
/* ---------- Responsive ---------- */
@media (max-width: 720px) {
.card-body {
grid-template-columns: 1fr;
gap: 18px;
}
.col--ing {
border-right: 0;
border-bottom: 1px solid var(--line);
padding-right: 0;
padding-bottom: 18px;
}
}
@media (max-width: 420px) {
.card-head {
flex-direction: column;
}
.topbar-inner {
justify-content: center;
}
}
/* ---------- Print ---------- */
@media print {
@page {
margin: 12mm;
}
body {
background: #fff;
color: #000;
}
.topbar,
.hint,
.toast {
display: none !important;
}
.stage {
padding: 0;
max-width: none;
}
.card {
box-shadow: none;
border: 1px solid #000;
border-radius: 0;
max-width: none;
width: 100%;
page-break-inside: avoid;
}
.card::before {
background: #000;
height: 3px;
}
.kicker,
.recipe-title,
.recipe-tagline,
.meta-k,
.meta-v,
.col-h,
.ing-list li,
.step-list li,
.card-foot,
.foot-src em {
color: #000;
}
.meta-row li {
background: transparent;
border: 1px solid #000;
}
.thumb {
background: #fff;
border: 1px solid #000;
box-shadow: none;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.step-list li::before {
background: #000;
color: #fff;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.ing-list li::before {
background: #000;
}
}(function () {
"use strict";
var card = document.getElementById("card");
var segBtns = Array.prototype.slice.call(document.querySelectorAll(".seg-btn"));
var printBtn = document.getElementById("printBtn");
var hint = document.getElementById("hint");
var toastEl = document.getElementById("toast");
var toastTimer = null;
var SIZES = {
"3x5": { cls: "card--3x5", label: "3×5 index card" },
"4x6": { cls: "card--4x6", label: "4×6 recipe card" },
full: { cls: "card--full", label: "Full page" }
};
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
if (toastTimer) clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2200);
}
function setSize(size) {
if (!SIZES[size]) return;
Object.keys(SIZES).forEach(function (key) {
card.classList.remove(SIZES[key].cls);
});
card.classList.add(SIZES[size].cls);
segBtns.forEach(function (btn) {
var active = btn.getAttribute("data-size") === size;
btn.classList.toggle("is-active", active);
if (active) {
btn.setAttribute("aria-pressed", "true");
} else {
btn.removeAttribute("aria-pressed");
}
});
if (hint) {
hint.textContent =
"Size: " + SIZES[size].label + " — press Print to fit one page.";
}
}
segBtns.forEach(function (btn) {
btn.addEventListener("click", function () {
var size = btn.getAttribute("data-size");
setSize(size);
toast("Card sized to " + SIZES[size].label);
});
});
if (printBtn) {
printBtn.addEventListener("click", function () {
toast("Opening print dialog…");
// let the toast paint before the blocking print() call
setTimeout(function () {
window.print();
}, 120);
});
}
// default
setSize("4x6");
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Cookbook — Printable Recipe Card</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,wght@9..144,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>
<header class="topbar" role="banner">
<div class="topbar-inner">
<p class="brand"><span aria-hidden="true">🍅</span> Stealthis Cookbook</p>
<div class="controls" role="toolbar" aria-label="Recipe card controls">
<div class="seg" role="group" aria-label="Card size">
<button type="button" class="seg-btn" data-size="3x5">3×5</button>
<button type="button" class="seg-btn is-active" data-size="4x6" aria-pressed="true">4×6</button>
<button type="button" class="seg-btn" data-size="full">Full</button>
</div>
<button type="button" id="printBtn" class="btn-print">
<span aria-hidden="true">🖨️</span> Print
</button>
</div>
</div>
</header>
<main class="stage" role="main">
<article class="card card--4x6" id="card" aria-label="Recipe card: Roasted Tomato & Saffron Orzo">
<div class="card-head">
<div class="thumb" aria-hidden="true">
<span class="thumb-emoji">🍅</span>
</div>
<div class="head-text">
<p class="kicker">Weeknight Mains · No. 47</p>
<h1 class="recipe-title">Roasted Tomato & Saffron Orzo</h1>
<p class="recipe-tagline">Blistered cherry tomatoes, toasted orzo, a whisper of saffron and lemon.</p>
</div>
</div>
<ul class="meta-row" aria-label="Recipe details">
<li><span class="meta-k">Prep</span><span class="meta-v">10 min</span></li>
<li><span class="meta-k">Cook</span><span class="meta-v">25 min</span></li>
<li><span class="meta-k">Serves</span><span class="meta-v">4</span></li>
<li><span class="meta-k">Level</span><span class="meta-v">Easy</span></li>
</ul>
<div class="card-body">
<section class="col col--ing" aria-labelledby="ing-h">
<h2 class="col-h" id="ing-h"><span aria-hidden="true">🥕</span> Ingredients</h2>
<ul class="ing-list">
<li>1½ cups orzo pasta</li>
<li>2 cups cherry tomatoes <span class="ing-em">🍅</span></li>
<li>3 cloves garlic, sliced <span class="ing-em">🧄</span></li>
<li>1 pinch saffron threads</li>
<li>2 tbsp olive oil</li>
<li>1 small shallot, minced</li>
<li>2½ cups vegetable stock</li>
<li>Zest + juice of 1 lemon <span class="ing-em">🍋</span></li>
<li>¼ cup torn basil <span class="ing-em">🌿</span></li>
<li>Sea salt & black pepper</li>
<li>Shaved pecorino, to finish</li>
</ul>
</section>
<section class="col col--steps" aria-labelledby="steps-h">
<h2 class="col-h" id="steps-h"><span aria-hidden="true">🌿</span> Method</h2>
<ol class="step-list">
<li>Steep saffron in 2 tbsp warm stock; set aside to bloom.</li>
<li>Roast tomatoes & garlic in oil at 220°C until blistered, ~12 min.</li>
<li>Toast orzo with shallot until golden and nutty, 2–3 min.</li>
<li>Add saffron stock and remaining stock; simmer, stirring, 9 min.</li>
<li>Fold in roasted tomatoes, lemon zest & juice. Season well.</li>
<li>Finish with basil and shaved pecorino. Serve warm.</li>
</ol>
</section>
</div>
<footer class="card-foot">
<span class="foot-src">From <em>The Slow Pantry</em>, p. 47</span>
<span class="foot-yield">Yield · 4 bowls · ~520 kcal each</span>
</footer>
</article>
<p class="hint" id="hint">Tip: choose a card size, then press Print to fit one page.</p>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Printable Recipe Card
A self-contained recipe card that looks like a real index card and prints like one too. The face carries a serif kicker and title, a warm CSS-gradient thumbnail standing in for food photography, and a tiny meta row summarizing prep, cook, servings, and difficulty. Below it, a two-column body keeps the ingredient list and numbered method side by side, collapsing to a single column on narrow screens.
Two interactions drive it. A segmented size toggle swaps the card between 3×5, 4×6, and full-page widths, updating the live hint text and aria-pressed state as you go. The Print button opens the browser print dialog after a brief toast, and a dedicated @media print block hides the toolbar, flattens everything to crisp black-on-white, keeps the card from breaking across pages, and trims to a single sheet.
Everything is vanilla HTML, CSS, and JavaScript — no frameworks, images, or libraries. Landmarks, a toolbar role, focus-visible outlines, and an aria-live status region keep it keyboard-friendly and screen-reader aware.
Illustrative UI only — recipes & nutrition data are fictional, not dietary advice.