Skip to main content

Settings

Color Mode
Theme Skin
Background

Appearance preferences are saved in this browser only.

Environment
Current Environment Dev

Built with JEKYLL_ENV=development. Changes auto-reload under jekyll serve.

Theme & Build
Jekyll v3.10.0
Last Build Aug 23, 18:10
Page Location
Page Info
Layout default
Collection docs
Path _docs/features/cookbook-collection.md
URL /docs/features/cookbook-collection/
Date 2026-08-23

Cookbook Collection — Recipes That Scale Themselves

Publish recipes as structured front matter — ingredients, ratios, timings — rendered with serving scaling, US↔metric conversion, and baker's percentages.

Cookbook Collection

The recipes collection turns front matter into a working recipe page. You describe the recipe as data — ingredients with amounts and units, steps, timings, a yield — and the theme renders the fact bar, the ingredient checklist, the method, a baker’s-percentage table, and a control bar that rescales every quantity and switches the whole page between the units it was written in, metric, and US customary.

The theme ships a demo cookbook — The Zer0 Kitchen — that exercises everything on this page.

What the reader gets

  • Scaling. Change the yield and every amount follows, including the ratio table’s weights. The percentages do not move, because a ratio does not care how much you make.
  • Conversion. Cups become grams and grams become cups, using the densities in _data/ingredient_densities.yml. Oven temperatures follow the same switch.
  • A formula, not just a list. Recipes that declare a ratio_basis: get a table of every ingredient as a percentage of it — the way bread, pastry and sauces are actually written down.
  • A page that works without JavaScript. Every amount is rendered by Jekyll exactly as authored. The scaler is progressive enhancement: with scripting off, the controls never appear and the recipe is still complete and correct.

Enable the collection

Add the collection and its front matter defaults to your site’s _config.yml (the theme’s own config carries the same block):

collections:
  recipes:
    output: true
    title: Cookbook
    icon: bi-egg-fried
    permalink: /:collection/:name/

defaults:
  - scope:
      path: pages/_recipes   # match your collections_dir
      type: recipes
    values:
      layout: recipe
      comments: false
      sidebar:
        nav: auto

Two optional data files tune the presentation. Both degrade gracefully — without them, courses fall back to their raw key and volumes simply never convert to weights.

File Purpose
_data/recipe_courses.yml Course sections on the index — title, tagline, icon. Sections render in file order.
_data/ingredient_densities.yml Grams per US cup per ingredient, keyed by the slugified name. This is what makes volume↔weight conversion possible.

Write a recipe

One file per recipe under pages/_recipes/:

pages/_recipes/
  index.md                 # layout: cookbook — cover + recipe index
  no-knead-focaccia.md     # layout: recipe (the collection default)

Recipe front matter

Key Required Purpose
title yes Recipe name (page <h1>)
description no Lead paragraph, card blurb, and meta description
cookbook no Slug tying this recipe to a layout: cookbook landing page
course no Section key looked up in _data/recipe_courses.yml
cuisine, difficulty no Fact-bar entries (difficulty: easy / intermediate / advanced)
yield no {amount, unit, singular}amount is the base the scaler works from
times no {prep, cook, rest, rest_label, total} in minutes; total is summed if absent
oven no {temp_f, temp_c, mode} — give either scale, the other is derived
equipment no List of strings
ingredients no List of items, or of {group, items} groups — see below
steps no List of steps, or of {section, items} sections — see below
ratio, ratio_basis no ratio_basis names the 100% ingredient and switches the ratio table on
notes no List of Markdown strings — “Cook’s notes”
nutrition no {basis, calories, protein, …} per serving
source, source_url no Attribution line under the title
scaler no false hides the control bar
units no false hides the unit switch but keeps scaling

Ingredients

An ingredient is a hash. Only item is required:

ingredients:
  - group: Dough              # optional grouping; omit for a flat list
    items:
      - item: bread flour
        qty: 500
        unit: g
      - item: large eggs
        singular: large egg   # used when the amount scales down to one
        qty: 2
      - item: garlic
        qty: 4
        qty_max: 6            # renders and scales as a range: "4–6 cloves"
        unit: cloves
        prep: lightly smashed
        optional: true
      - item: kosher salt
        qty: 1
        unit: tsp
        grams_per_cup: 145    # pin a density the data file does not know
        note: Diamond Crystal — Morton's is nearly twice as dense.
Key Purpose
item Ingredient name (required)
qty, qty_max Amount, and the top of a range. Numbers, not strings — 0.5, not "1/2"
unit As you’d write it: g, cups, tbsp, cloves, or nothing at all
prep Trailing clause: “finely chopped”
note Sub-line under the ingredient
singular Singular form, used once the amount scales to one or below
optional true adds an “optional” badge
link Links the ingredient name
grams_per_cup Density override, beating _data/ingredient_densities.yml
weigh false keeps this amount a volume even in metric
scale false pins the amount when the recipe is rescaled
ratio false leaves it out of the ratio table
baker_percent Pins the percentage instead of computing it

Quantities render with vulgar fractions (0.5 → ½) for cups, spoons and countable things, and as decimals for metric weights — a scale shows 40.5 g, not 40½ g.

Steps

A step is a plain string or a hash:

steps:
  - section: "The night before"    # optional; numbering continues across sections
    items:
      - title: Mix
        text: Stir until no dry flour remains. The dough will be **wet**.
        time: 5                     # minutes
        temp_f: 450                 # converts with the unit switch
        image: /assets/images/step-1.jpg
        image_alt: Shaggy dough in a bowl
        note: Do not knead it.

Step text is markdownified, so links and emphasis work.

Unit conversion, precisely

Switching to Metric or US rewrites every quantity on the page:

  • Mass ↔ mass, volume ↔ volume always work: g ⇄ oz, ml ⇄ cups.
  • Volume ↔ weight works only where a density is known — from the ingredient’s own grams_per_cup: or from _data/ingredient_densities.yml, matched on the slugified ingredient name (Bread flourbread-flour). Metric turns a known volume into grams; US turns a known weight back into cups and spoons. Set weigh: false to opt an ingredient out.
  • Units are chosen for readability. 3 tsp becomes 1 tbsp; 1500 g becomes 1.5 kg. US amounts round to a quarter above one cup and an eighth below, because that is what measuring spoons can do.
  • Temperatures show both scales by default and narrow to the selected one.
  • Nutrition never scales. A serving is a serving however many you make.

The reader’s unit choice is remembered across recipes, and each recipe remembers its own scale. A link can carry either: ?servings=24 or ?scale=2.

The ratio table

Declare which ingredient is 100%:

ratio: "5 flour : 4 water"   # optional prose summary
ratio_basis: flour           # matched as a substring — covers every "… flour"

Every ingredient that can be resolved to a weight gets a row and a percentage; ones that cannot (a clove of garlic, an egg, a volume with no density) are listed without a percentage rather than guessed at. The table also totals the batch and divides it by the yield, which is the number a baker actually wants — grams per roll, per loaf, per pizza ball.

Percentages are computed by Jekyll at build time, so they are correct with JavaScript disabled and can never drift from the ingredient list.

The cookbook landing page

---
title: The Zer0 Kitchen
layout: cookbook
cookbook: zer0-kitchen     # omit to index every recipe on the site
permalink: /recipes/
---

The page renders a cover, your Markdown body, a jump-nav of its courses, and the recipe index grouped into course sections.

Components

Drop any of these on any page. Pages outside the recipe layout need recipe_tools: true in their front matter to load the scaler script.

Include Purpose
components/recipe-index.html Grid of recipes, grouped by course (cookbook, heading, grouped)
components/recipe-card.html One recipe’s card (recipe, heading_level)
components/recipe-meta.html Fact bar (recipe)
components/recipe-scaler.html Scaling + unit controls (base_yield, yield_unit, units)
components/recipe-ingredients.html Ingredient checklist (ingredients, checklist, id_prefix)
components/recipe-steps.html Numbered method (steps, heading)
components/recipe-ratio.html Baker’s-percentage table (recipe, basis)
components/recipe-nutrition.html Per-serving nutrition (recipe)
components/recipe-qty.html One scalable quantity (qty, unit, grams_per_cup, scale)
components/recipe-temp.html A convertible temperature (f, c)

A recipe index on a home page is one line:

{% include components/recipe-index.html heading="What we're cooking" %}

And a convertible temperature drops straight into prose:

Heat the oven to {% include components/recipe-temp.html f=375 %}.

Structured data

Every recipe page emits one schema.org/Recipe JSON-LD block — name, image, author, times as ISO 8601 durations, yield, category, cuisine, keywords, ingredients, instructions, and calories — so recipes are eligible for search rich results without any extra plugin.

Files

Path Role
_layouts/recipe.html, _layouts/cookbook.html The two layouts
_includes/components/recipe-*.html The component set
assets/js/recipe-scaler.js Scaling and conversion engine (loaded only on recipe pages)
_sass/components/_recipe.scss Styles, including a print stylesheet
_data/recipe_courses.yml, _data/ingredient_densities.yml Course metadata and densities

The unit table in recipe-scaler.js mirrors the one in components/recipe-grams.html — the build-time table feeds the ratio percentages, the runtime one feeds conversion. Change them together.