Cookbook

A Blogin site is customized in six places. This page is the index: what each one reaches, and where the detail lives.

Where What it changes
blogin.json Site-wide behaviour. Feeds, pagination, taxonomies, assets, languages.
Front matter One post. Its URL, its ordering, its summary, its redirects.
layouts/ The markup of every page, per section if you want.
assets/ and static/ Stylesheets, scripts, images, and files served verbatim.
data/ and shortcodes/ Values a layout reads, and markup a post can call.
themes/ A whole layout and asset set, reused across sites.

Change what a page looks like#

Edit layouts/. base.haml is the shell, show.haml a post, and index.haml a listing. A missing one of those three is a build error naming it.

Give one section its own markup by putting a layout in a matching directory. A post in content/essays/ renders through layouts/essays/show.haml when that file exists, falling back to layouts/show.haml:

layouts/
  show.haml            every post
  essays/show.haml     posts under content/essays/

Or name a layout in the config instead of using a directory:

"sections": {
  "essays": { "layout": "essay" }
}

See Layouts for the full set of files and how they resolve, and Template Data for every value a layout can read.

Change what a post does#

Front matter. title is the only required key:

---
title: My Post
slug: a-different-url
order: 2
toc: true
draft: true
summary: What listings and feeds show instead of the opening paragraph.
aliases: [/the-old-url]
---

slug picks the URL, order places a post ahead of date ordering, toc turns on the table of contents, draft holds it back from a normal build, and aliases writes a redirecting page at each old URL. Any key Blogin does not recognise is still available to the layout under its own name, so series: Advanced in front matter reads as series in show.haml.

See Writing Posts.

Change the site's behaviour#

blogin.json. Every key is optional, an unknown key is a warning naming the closest real one, and a key with the wrong type stops the build.

Common ones:

Want Key
Extensionless URLs clean-urls
Posts per listing page page-size, or per section
More than tags taxonomies
RSS or JSON alongside Atom feed-formats
Syntax highlighting highlight
Smaller CSS and JS minify
Cache-safe asset names fingerprint
Responsive images image-widths
More than one language languages

See Configuration for all of them with types and defaults.

Change the navigation#

The nav is built from the content tree, so a new directory under content/ becomes a new nav entry. Adjust it per section:

"sections": {
  "notes": { "label": "Field Notes", "order": 1 },
  "drafts": { "nav": false }
}

label renames it, order places it ahead of alphabetical, and nav: false keeps it out. A layout renders the tree itself from nav-nodes, with nav-current(node) marking the current section, so the markup is yours. See Template Data.

Nothing links /tags automatically. Add it to a layout when you want it.

Change the styling#

Three separate levers, and they combine.

A CSS framework. Set css-framework to none, bootstrap5, pico, or bulma and Blogin classes the HTML it writes to match. See CSS Frameworks.

Your own CSS. Anything under assets/css is copied to public/assets/css, minified and fingerprinted if you asked for it. Link it from base.haml.

The generated stylesheet. public/assets/css/blogin.css carries the syntax highlighting colors, code block styling, and the light and dark toggle. Override any rule in your own stylesheet rather than editing it, since a build rewrites it.

For a light and dark toggle, put theme-script in the <head> and theme-toggle in the navbar. See Layouts.

Change the pagination#

page-size sets how many posts a listing page holds, and a section can override it. pagination-html renders a ready-made bar, and pagination-links gives the pages as data for a bar you write yourself. See Pagination.

Add values a layout can read#

data/. Every .json, .yaml, and .yml file under it loads into data, keyed by filename:

data/
  authors.json    -> data<authors>
  site/links.yaml -> data<site><links>

A _data.json inside a content directory applies to the pages beneath it and overrides the site-wide tree key by key. See Data files and Data File YAML for what a YAML file may hold.

Add markup a post can call#

shortcodes/. Put an HTML template there named for the shortcode, and a post calls it with key="value" arguments:

<!-- shortcodes/note.html -->
<aside class="note">{{ text }}</aside>
{{< note text="Heads up" >}}

youtube and figure are built in, and a file of the same name overrides one. See Writing Posts.

Reuse a whole design#

themes/. A theme is a directory of layouts/, assets/, and static/ that fills in behind your own file by file, so you can override one layout and keep the rest:

"theme": "acme"

See Themes.

Speed up a big site#

Wrap chrome that renders the same on every page in cache-fragment:

!= cache-fragment('sidebar', { render(:partial<sidebar>) })

Reuse is decided by the values the fragment read, so it cannot serve one page's markup to another. See Layouts.

blogin build --counters prints the work a build did, which is what says whether a change made it do more. See Previewing and Building.

What cannot be customized#

Know these before you go looking for a setting.

  • No plugin system. Everything a site needs is in the binary.
  • No template logic beyond the expression language. It reads, compares, iterates, and calls what the view offers. No assignment, no user-defined functions, no calling into a host language. See Template Expressions.
  • No per-taxonomy page size. Term pages use the site's page-size.
  • The page/2 URL shape is fixed. See Pagination.
  • Feeds carry every post in scope, with no length key.