Skip to content

Customizing

The core template puts the engine in your project under .pandoc/. You change it by editing those files and rebuilding the image:

make image      # rebuild the engine with your changes
make publish    # build your book against it

This is the harder end of Keystone, and the principle here is hard is hard, not impossible. Keystone makes the easy things easy; shipping the whole engine in your project is how it keeps the hard things possible. The work is real — but nothing is hidden or off-limits.

Before changing the engine, check whether you need to. Most new constructs are shortcuts, not new code — a shortcut composes existing handlers in shortcuts.yaml and needs no rebuild. Reach into the engine only for genuinely new behavior. See Writing your own shortcuts.

Adding a handler

When a behavior can't be composed from existing handlers, add one:

  1. Create a directory under .pandoc/filters/divs/<class-name>/. The directory name becomes the class — divs/sidenote/ handles ::: sidenote.
  2. Add handler.lua returning a per-format hook table (see Handlers for the contract). Implement the formats you need; unhandled formats pass through.
  3. Add optional style files — macros.tex for PDF, style.css for EPUB.
  4. make image, then build your book to confirm it works.

The new class is automatically available as a shortcut target — you can reference it from shortcuts.yaml immediately, no registration.

The hook table reaches further than div/span: a handler can also route Header elements by their class, or intercept whole element types (like Figure) through global slots. The authoritative contract is the dispatcher itself — read .pandoc/filters/divs.lua and the existing handlers alongside it for the complete surface.

Keep each handler to a single concern and let it pass content through in formats it can't serve — the format integration and handler pages explain why this is what keeps output correct everywhere. If you edit filter code, construct and inspect the document through the engine's AST library (KAST) rather than calling Pandoc's constructors directly; the existing handlers show the pattern.

DOCX/ODT styling is limited here

A handler's DOCX/ODT output relies on styles baked into a reference document. The tooling that regenerates those reference docs isn't part of the template, so a custom handler realistically styles PDF and EPUB; in DOCX/ODT its content passes through in the default style. If you need custom word-processor styling, you're into forking territory (below).

Fonts

You don't need to touch the engine to add a font — drop the .otf files in fonts/ and register them in fonts/fonts-registry.yaml, which works in both templates. See Typography & fonts. Editing the engine's built-in font set is a deeper change that belongs to a fork.

The fork boundary

There's a line between using Keystone and forking it. Adding shortcuts, adding a handler, adjusting a filter for your own book — that's customization, and core is built for it. Rebuilding the reference documents, changing the built-in font registry, or reworking the pipeline means you've taken ownership of the engine itself. That's a legitimate path and core is where it starts — just know that a fork no longer receives Keystone updates automatically.