Juicer
English

Configuration

site.toml keys juicerblog reads, the per-page frontmatter conventions, and the directories that turn engine features on.

juicerblog reads its configuration from three places:

  • A small set of top-level site keys (the same ones every juicer theme uses).
  • A namespaced [juicerblog] table for theme-specific palette overrides.
  • Per-page frontmatter for opt-in conventions like the static-page flag and the homepage layout selector.

Anything you don’t set keeps the theme’s defaults. juicerblog uses [juicerblog] so it doesn’t collide with keys other themes might want.

Quick example

A complete site.toml exercising every juicerblog-aware feature:

title    = "My blog"
tagline  = "a place for prose"
author   = "Ed Maxedon"
baseURL  = "https://example.com"

theme    = "juicerblog"
paginate = 5
sortBy   = "date"

# Every post lives at /<year>/<month>/<slug>/.# Every post lives at /<year>/<month>/<slug>/.
[permalinks]
posts = ":year/:month/:slug/"

# Year + month archive pages.# Year + month archive pages.
dateArchives = true

# Author registry — see [Blogging features → Author registry] for the# Author registry — see [Blogging features → Author registry] for the
# full data shape. Each id is referenced from per-post `author:` /# full data shape. Each id is referenced from per-post `author:` /
# `authors:` frontmatter.# `authors:` frontmatter.
[[authors]]
id     = "ed"
name   = "Ed Maxedon"
bio    = "Writes about Scala, languages, and the craft of small tools."
avatar = "/img/avatar-ed.png"

[[authors.links]]
label = "GitHub"
url   = "https://github.com/edadma"

# Optional palette overrides. Defaults are warm-neutral (amber + indigo).# Optional palette overrides. Defaults are warm-neutral (amber + indigo).
[juicerblog]
brand  = "#0f766e"  # teal-700# teal-700
accent = "#7c3aed"  # violet-600# violet-600

Top-level keys

Standard juicer keys, with a note on how juicerblog uses each:

KeyTypeDefaultWhat
titlestring"Untitled"Wordmark in the topbar; appears in <title> tags and the footer
taglinestring(none)Italic line beside the wordmark; rendered as the hero subtitle on the homepage
authorstring"Unnamed"Footer copyright line; also drives the site-wide Atom feed <author> element
baseURLstring(required)Used for absolute permalinks, sitemap, OpenGraph, atom feeds
themestring(required)Set this to "juicerblog"
paginateint10Posts per page on the home + archive listings. juicerblog renders 4–5 well; 10+ starts feeling long
sortBystring"weight"Set to "date" for a blog. juicerblog’s home + archive listings respect this

The [permalinks], dateArchives, and [[authors]] tables are engine-level features documented at Blogging features. juicerblog renders the layouts that surface them; the engine’s job is unchanged.

[juicerblog] keys

A small table for palette overrides. Defaults are:

KeyTypeDefaultWhat
juicerblog.brandcolor#c2410cPrimary brand color — links on hover, brand pill backgrounds, reading-progress bar
juicerblog.accentcolor#4338caDefault link color in prose (indigo-700 — readable against cream paper)
juicerblog.leafcolor#16a34a“Confirmation green” — the copy-button “Copied” flash, success badges

Any CSS color expression works: hex, rgb(...), hsl(...), oklch(...), named colors. Values are dropped into a <style> block in <head> immediately after juicerblog.css, redefining the named CSS variables.

Note

Dark-mode variants of these tokens are derived automatically by the theme — the dark palette tunes the same hues lighter and slightly cooler so they don’t burn against the dark background. The current build doesn’t expose dark-mode overrides as separate config keys; if you need radically different colors per theme, override static/juicerblog.css from your site’s static/ directory.

Per-page frontmatter

Three frontmatter conventions are juicerblog-specific (the engine doesn’t read them; the theme branches on them):

FieldTypeWhat
staticboolWhen true, the page renders without post chrome — no date/reading-time line, no series badge, no prev/next nav. Use it for about / colophon / contact pages so they look like clean essays, not posts. Pages with static: true are also filtered out of the home post list, the per-tag archives, the per-author archives, and .site.posts / .site.pagesByYear.
layoutstringWhen set, juicer renders the page through _default/<layout>.html instead of the default file.html / folder.html. juicerblog ships two non-default layouts: home and archive.
seriesstring(Engine feature.) When set, the post is part of a series — juicerblog adds a progress badge (“Reading on screens · Part 2 of 3”) above the body and an “In this series” sidebar after the body. See Blogging features → Series for the full data shape.

A typical post:

---
title: A whirlwind tour of Scala 3
date: 2024-11-18
author: ed
tags: [scala, language]
summary: Five things Scala 3 gets right.
---

Scala 3 cleans up a decade of accreted complexity…

A static page:

---
title: About this site
author: ed
static: true
summary: Who runs this place and why.
---

This blog exists for one reason…

A homepage:

---
title: My blog
layout: home
---

A short paragraph that renders inside the hero, under the title and tagline.

An archive landing:

---
title: Archive
layout: archive
static: true
summary: Every post on this site, grouped by year.
---

A short intro paragraph that renders above the year-grouped list.

Directory layout

The directories juicerblog cares about under your site root:

my-blog/
├── site.toml
├── content/
│   ├── _index.md            # the homepage (often layout: home)
│   ├── archive.md           # the archive page (layout: archive, static: true)
│   ├── about.md             # static: true
│   └── *.md                 # posts (with date, author, tags, etc.)
├── grammars/
│   ├── scala.tmLanguage.json
│   ├── javascript.tmLanguage.json
│   └── ...
└── static/                  # site-side asset overrides (avatars, og images)
    ├── img/avatar-ed.png
    └── og-default.png

grammars/ is the opt-in for syntax highlighting — covered in its own section below.

Syntax highlighting

juicerblog renders code blocks through juicer’s highlighter integration — TextMate grammars at build time, no JavaScript runtime, no FOUC. The integration is engine-level (any juicer theme benefits); juicerblog ships the .hl-* CSS palette that turns the spans into colors.

To enable highlighting, drop a <lang>.tmLanguage.json file into <src>/grammars/ for each language you want colored:

my-blog/grammars/
├── scala.tmLanguage.json
├── javascript.tmLanguage.json
├── python.tmLanguage.json
└── rust.tmLanguage.json

The <lang> part of the filename matches the fence’s language tag — a fenced block opened with ```scala looks for grammars/scala.tmLanguage.json. Languages with no matching grammar render as plain <pre><code> (no error).

Tip

Where to get grammars. Almost every VS Code language extension ships a TextMate grammar file. Grab them from the language’s official VS Code extension repository — for example, scala-lang/vscode-scala-syntax for Scala. Drop the .tmLanguage.json file directly into your grammars/ directory.

Note

Sites with no grammars/ directory pay zero cost. The build doesn’t try to load any highlighter at all. Code blocks render as plain <pre><code>. The theme’s CSS for the .hl-* classes is inert in that case (no .hl-* spans get emitted), so there’s no wasted bytes either.

The classes the theme styles, with their default light-mode colors:

ClassColor (light)Color (dark)What kinds of tokens
.hl-keyword#9a3412#fb923cclass, def, if, for, match, import, etc.
.hl-string#166534#86efacString literals (single, double, triple)
.hl-comment#78716c#a8a29eLine + block comments (italic)
.hl-number#b45309#fdba74Numeric literals
.hl-type#1d4ed8#93c5fdType names (capitalized identifiers in most grammars)
.hl-function#6d28d9#c4b5fdFunction / method names — when the grammar tags them
.hl-variableinheritinheritVariable references
.hl-operator#44403c#d6d3d1=>, ::, etc. — when the grammar tags them
.hl-punctuation#78716c#a8a29eBrackets, commas, semicolons, dots

Override any of these from your site’s static/site.css (loaded after the theme’s stylesheet) if the defaults don’t match your site’s palette.

Going beyond config

Drop a file with the same path under your own site to override anything in the theme — this is the ordinary theme overlay pattern:

You want to change…Override at…
The topbar nav<src>/partials/topbar.html
The footer markup<src>/partials/footer.html
The byline<src>/partials/post-byline.html
The series sidebar<src>/partials/series-nav.html
The 404 page<src>/layouts/_default/404.html
Custom CSS<src>/static/site.css (and link it from your overridden head.html)

Site files always win over theme files, so overrides are a one-file-at-a-time operation — no forking the theme.

SEO

Every juicer theme ships a shared SEO partial (partials/seo.html) that emits the standard meta block: description (with site-level fallback), canonical link, author meta, robots noindex, OpenGraph + Twitter cards (via the ogTags builtin), Atom feed discovery, and theme-specific JSON-LD structured data. The engine separately writes sitemap.xml and robots.txt. This section documents the knobs.

Site-wide keys

description = "A monthly journal of small tools and small wins."   # used as default meta description and JSON-LD site description# used as default meta description and JSON-LD site description
ogImage     = "/og/default.png"                                    # fallback OpenGraph image (absolute URL or site-relative path)# fallback OpenGraph image (absolute URL or site-relative path)

# robots.txt# robots.txt
robots   = true                                                    # default; set false to suppress robots.txt entirely (ship your own in static/)# default; set false to suppress robots.txt entirely (ship your own in static/)
noindex  = false                                                   # default; set true for staging/preview domains — emits Disallow: / for the whole site# default; set true for staging/preview domains — emits Disallow: / for the whole site
disallow = ["/drafts/", "/staging/"]                               # additional Disallow lines (string or array of strings)# additional Disallow lines (string or array of strings)

Per-page frontmatter

---
title: A short post about something
summary: A one-sentence dek for search results and OG cards.
image: /img/post-hero.jpg          # used by ogTags + JSON-LD BlogPosting
ogImage: /img/post-social.jpg      # explicit OG image, wins over `image`
ogTitle: A snappier social headline # explicit OG title, wins over `title`
ogDescription: Tightened for socials # explicit OG description, wins over `summary`
noindex: true                       # excluded from sitemap.xml, JSON-LD suppressed, <meta robots> emitted
---

Structured data emitted

juicerblog emits these JSON-LD @types:

PageSchema
Root section (content/_index.md)WebSite
Non-section page with date:BlogPosting (with author / image / dateISO)
Any page with ancestorsBreadcrumbList (built from .page.ancestors)

noindex: true suppresses ALL JSON-LD on that page — a deliberately-hidden page should not advertise itself to crawlers in any form.

Overriding the SEO partial

To customize, drop a file at <src>/partials/seo.html (or seo-jsonld.html for just the structured-data part). The site override wins over the theme’s copy with no forking.

Search

Esc
to navigate to open Esc to close