Juicer
English

Frontmatter

Every YAML key juicer (and the bundled themes) read from a page’s frontmatter.

The YAML block at the top of every content file. Juicer reads a fixed set of keys; everything else passes through to templates as .page.<key>. This page catalogues the engine-level keys (read by juicer itself) and points at the theme-specific keys (read by one bundled theme each, documented under that theme’s section).

---
title: Hello, World
date: 2024-03-12
tags: [intro, meta]
summary: A short description used in list pages and OpenGraph tags.
draft: false
---

The body of the page goes here.

Engine-level keys

These are read by juicer itself. Every bundled theme can rely on them being present (or normalized to a sensible default) on every page record in templates.

Page identity

KeyTypeWhat
titleStringPage title. Falls back to the first heading in the body, then to the filename when both are absent.
summaryStringExplicit page summary. Auto-derived from a <!--more--> marker or the first paragraph (capped at 30 words) when absent.
layoutStringOverride the default layout. For a single page, file is the default; for _index.md, folder. Setting layout: home (etc.) renders the page through _default/home.html instead.

Publishing & visibility

KeyTypeWhat
dateISO-8601 string or YAML datePublication date. Three input shapes recognized: 2024-03-12T10:30:00Z (with offset), 2024-03-12T10:30:00 (local, assumed UTC), 2024-03-12 (date only, midnight UTC).
draftBooleanWhen true, the page is excluded from the build unless --drafts is passed. Drafts are invisible to TOC, sitemap, search index, and section listings.
staticBooleanMark a page as non-post even though it lives in a posts-style section. Excluded from .site.posts, .site.pagesByYear, and tag/author archive listings. Used for about/colophon/contact pages on a blog.
---
title: Big announcement
date: 2024-12-25T09:00:00Z
draft: false
---
---
title: About this site
static: true
---
Note

The date rule: only pages with explicit date: frontmatter are included in .site.posts, .site.pagesByYear, and dateArchives output. When date: is absent, juicer falls back to the source file’s filesystem mtime for sort order — it’s a deliberate split: silently sorting by mtime is convenient; silently treating every page as a dated post is not.

Taxonomy

KeyTypeWhat
tagsString or [String]Tags for the post. A single string is auto-wrapped to a one-element list. Generates /tags/<slug>/ archive pages (gated on a tag-page.html layout existing).
categoriesString or [String]A second taxonomy axis. Same slugification and archive pattern but at /categories/<slug>/. Use one, the other, or both.
---
title: Functional programming in Scala 3
tags: [scala, functional, language]
categories: tutorials
---

Slugification: lowercase → ASCII-fold (cafécafe) → runs of non-alphanumerics → -. So "Functional Programming" becomes functional-programming.

Ordering

KeyTypeWhat
weightIntExplicit sort position within a section. Lower weights sort first. Pages without a weight come after weighted pages.
sortByString(on section _index.md) Override the site-wide sortBy. One of "date", "title", "weight".
paginateInt(on section _index.md) Override the site-wide paginate slice size.

Authors

KeyTypeWhat
authorStringSingle author ID — references an id in [[authors]] in site.toml. Singular shorthand for authors: [<id>].
authors[String]Multiple author IDs. When both author: and authors: are absent, .page.author is null and .page.authors is the empty list.
---
title: A co-authored post
authors: [ed, alice]
---

Author IDs that don’t match any [[authors]].id fall through to a stub {id: "<typo>"} record so templates don’t fail; the per-author archive is empty in that case.

Series

KeyTypeWhat
seriesStringSeries name. Pages with the same series: value (case-sensitive) form an ordered group.
seriesOrderIntPosition within the series. Pages without seriesOrder sort by date ascending after the ordered pages.
---
title: OS Internals, Part 1
series: OS Internals
seriesOrder: 1
---

Aliases (redirects)

KeyTypeWhat
aliasesString or [String]Old URLs that should redirect to this page. Each generates a static HTML stub at the old URL with a <meta http-equiv="refresh"> to the canonical URL.
---
title: Setting up a Scala 3 project
aliases:
  - /old-blog-name/setting-up-scala/
  - /2023/setting-up/
---

Each alias must include both leading and trailing slashes: /old-url/, not old-url or /old-url.

Events

These keys are read on pages in the eventsSection (default events) — the section juicer treats as the source of calendar events.

KeyTypeWhat
recurringString"weekly" for a weekly recurring event. The event expands onto every matching weekday in .site.calendar.
recurringDayStringDay name ("Monday", …, "Sunday") for the recurrence. Defaults to the start date’s day of week when absent.
---
title: Tuesday Coffee
date: 2024-03-12T09:00:00
recurring: weekly
recurringDay: Tuesday
---

Photos

KeyTypeWhat
photos[String] or [{src, caption?, alt?}]Photos for the page. Aggregated site-wide into .site.photos (sorted by page date desc).
---
title: Picnic in the park
date: 2024-06-12
photos:
  - "/img/picnic-01.jpg"
  - { src: "/img/picnic-02.jpg", caption: "The pie table", alt: "A long folding table loaded with summer pies" }
---

OpenGraph / social cards

These override defaults that the ogTags template helper otherwise computes from .page.title, .page.summary, .page.image, etc.

KeyTypeWhat
descriptionStringFallback for the OpenGraph description when summary isn’t appropriate.
imageStringDefault page image — used by og:image and as a card thumbnail by themes that show one.
ogTitleStringOverride the page title in OpenGraph and Twitter card tags.
ogDescriptionStringOverride the description in OpenGraph and Twitter card tags.
ogImageStringOverride the image in OpenGraph and Twitter card tags.
---
title: A long, descriptive, SEO-targeted post title
summary: A long summary tuned for the page's own list rendering.
ogTitle: A short, punchy title for social cards
ogDescription: A different summary that fits in a card preview.
ogImage: /img/posts/short-title-card.png
---

Computed (not set by you)

These fields appear on .page.<key> in templates but are not read from frontmatter — they’re set by juicer during build:

KeyWhat
permalinkCanonical site-relative URL — /posts/the-post/.
relPermalinkSame as permalink (Hugo parity).
urlSame as permalink.
dateISO2024-03-12T00:00:00Z (only when date: is set).
dateLongMarch 12, 2024 (only when date: is set).
dateShort2024-03-12 (only when date: is set).
slugURL stem (last path segment).
wordCountRendered-body word count.
readingTimeceil(wordCount / 200) minutes, floor 1 for non-empty pages.
isSectiontrue for _index.md pages.
parent, ancestorsSection’s _index record, then root → parent chain.
next, prevSibling navigation by section page order.
pages, subsections(_index.md only) Children of the section.
series (block){name, pages, prev, next, index, total} — populated when the page declares a series:.

The full .page reference, including these, lives in Template data → .page.

Cascade — inherited frontmatter

A section’s _index.md may declare a cascade: map; every key inside it is inherited by every descendant page that doesn’t set the same key in its own frontmatter. This lets a section establish defaults — author, layout, license, custom flags — without repeating them on every page.

# content/posts/_index.md
---
title: Posts
cascade:
  author: ed
  tone: editorial
  license: CC-BY
---

Every page under /posts/ now picks up .page.author = "ed", .page.tone = "editorial", .page.license = "CC-BY" automatically. A specific post can override any of them:

# content/posts/guest-piece.md
---
title: Guest piece
author: alice    # overrides the cascaded "ed"
---

Resolution rules

  • The page’s own frontmatter wins over any cascade value.
  • Nearer ancestors win over farther ancestors. Root _index.md‘s cascade is the weakest; a deeply-nested section’s cascade overrides everything above it.
  • A section’s cascade does NOT apply to its own _index.md. Cascade declares defaults for descendants, not for the declaring page itself. The section’s own _index.md only inherits cascades from sections above it.
  • Sections inherit cascade from ancestor sections. A nested section’s _index.md picks up cascades from every ancestor section, then merges its own (own wins).
  • Missing / empty cascade is a no-op. Pages without any cascade in their ancestor chain see exactly the frontmatter they wrote.

What can be cascaded

Any frontmatter key the engine or a theme reads. Useful candidates:

  • author / authors — section-wide attribution.
  • layout — force every page in a section through a specific template (e.g. layout: project across a portfolio section).
  • tags / categories — section-wide taxonomy; pages add their own and override the default list if needed.
  • Custom keys consumed by your theme — tone, region, license, noindex, …

Less useful (but legal) to cascade: date, title, summary — these are intrinsically per-page and rarely benefit from a default. There’s no enforcement; cascade is a blanket mechanism and the author is responsible for picking keys that make sense to inherit.

Theme-specific frontmatter

The bundled themes recognize their own frontmatter keys on top of the engine surface. Those are documented per theme:

ThemeWhat it reads on top
juicerblogstatic, layout: home/archive, series
juicerstudytoc, summary (as lead), minutes
juicerlandinglayout: home, summary (as subtitle), lang
juicerportfoliolayout: project, year, tagline, role, client, tools, category, hero, heroAlt, caption, gallery, link
juicerdocsEngine-level keys only — no theme-specific frontmatter.

Any other key

Anything you write in frontmatter that juicer doesn’t read is exposed on .page.<key> for templates to use. So adding a custom field is just a matter of writing it:

---
title: Cherry pie
yield: 8 servings
prepTime: 45 min
cookTime: 1 h 10 min
---
<dl>
  <dt>Yield</dt><dd>{{ .page.yield }}</dd>
  <dt>Prep time</dt><dd>{{ .page.prepTime }}</dd>
  <dt>Cook time</dt><dd>{{ .page.cookTime }}</dd>
</dl>

Search

Esc
to navigate to open Esc to close