<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Kiln example site</title>
  <id>https://bekto.github.io/kiln/</id>
  <updated>2026-07-25T00:00:00.000Z</updated>
  <link rel="self" href="https://bekto.github.io/kiln/feed.xml"/>
  <entry>
    <title>Search Tips</title>
    <id>https://bekto.github.io/kiln/posts/search-tips/</id>
    <updated>2026-07-25T00:00:00.000Z</updated>
    <link rel="alternate" href="https://bekto.github.io/kiln/posts/search-tips/"/>
    <content type="html">&lt;p&gt;The search box in the site header is client-side: Kiln builds a JSON
index of every published page at &lt;code&gt;dist/search-index.json&lt;/code&gt;, ships a tiny
script alongside it, and the browser does the filtering. No server, no
service, no tracking.&lt;/p&gt;
&lt;h2&gt;How the index is built&lt;/h2&gt;
&lt;p&gt;Each entry carries a title, the page URL, a plain-text excerpt, and the
post&#39;s tags. Because the index is generated after the publish filter,
drafts and future posts are absent from it — you cannot find what was
never shipped.&lt;/p&gt;
&lt;h2&gt;Getting good results&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Search for whole words: &lt;code&gt;frontmatter&lt;/code&gt;, &lt;code&gt;sitemap&lt;/code&gt;, &lt;code&gt;highlighting&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tags are part of the index, so &lt;code&gt;showcase&lt;/code&gt; surfaces the showcase posts.&lt;/li&gt;
&lt;li&gt;Results update as you type; the empty state tells you when nothing
matched.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What it will not do&lt;/h2&gt;
&lt;p&gt;The demo script has no fuzzy matching and no stemming — it favors
predictability over cleverness. For a site of this size, that is plenty.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Feeds and Sitemaps</title>
    <id>https://bekto.github.io/kiln/posts/feeds-and-sitemaps/</id>
    <updated>2026-06-18T00:00:00.000Z</updated>
    <link rel="alternate" href="https://bekto.github.io/kiln/posts/feeds-and-sitemaps/"/>
    <content type="html">&lt;p&gt;Two machine-readable outputs ship with every build: an Atom feed at
&lt;code&gt;/feed.xml&lt;/code&gt; and a sitemap at &lt;code&gt;/sitemap.xml&lt;/code&gt;. Both are generated from the
published posts, so a draft or a future-dated post can never leak into
either.&lt;/p&gt;
&lt;h2&gt;The Atom feed&lt;/h2&gt;
&lt;p&gt;The feed lists the newest posts — capped by &lt;code&gt;features.feed.limit&lt;/code&gt;, which
this site sets to 10 — with absolute URLs derived from &lt;code&gt;site.url&lt;/code&gt;. Every
page also advertises it from the document head:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;link rel=&amp;quot;alternate&amp;quot; type=&amp;quot;application/atom+xml&amp;quot; href=&amp;quot;/feed.xml&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Point a reader at the feed URL, or let them discover it automatically;
both work because the link tag is in the template, not hand-written per
page.&lt;/p&gt;
&lt;h2&gt;The sitemap&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;sitemap.xml&lt;/code&gt; enumerates every emitted URL: the home list and its
overflow pages, each post, each static page, and every tag archive. The
crawler that reads it maps each &lt;code&gt;&amp;lt;loc&amp;gt;&lt;/code&gt; straight back to a file inside
&lt;code&gt;dist/&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Why absolute URLs matter&lt;/h2&gt;
&lt;p&gt;Feeds and sitemaps are consumed off-site, so relative URLs are
meaningless there. Kiln rejects a build whose &lt;code&gt;site.url&lt;/code&gt; is not an
absolute &lt;code&gt;http(s)&lt;/code&gt; origin — the example site sets
&lt;code&gt;https://example.com&lt;/code&gt; in &lt;code&gt;kiln.config.ts&lt;/code&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Syntax Highlighting Tour</title>
    <id>https://bekto.github.io/kiln/posts/syntax-highlighting-tour/</id>
    <updated>2026-05-12T00:00:00.000Z</updated>
    <link rel="alternate" href="https://bekto.github.io/kiln/posts/syntax-highlighting-tour/"/>
    <content type="html">&lt;p&gt;A long-form tour of Kiln&#39;s build-time syntax highlighting. Everything on
this page was highlighted &lt;strong&gt;while the site was built&lt;/strong&gt; — there is no
client-side highlighting library, just spans baked into the HTML. The
tour doubles as the demo page for the table of contents above the
article and for the related-posts list at the bottom.&lt;/p&gt;
&lt;h2&gt;Why highlight at build time?&lt;/h2&gt;
&lt;p&gt;Shipping a highlighting library to the browser means every visitor pays
for it: more JavaScript, more CSS, and a flash of unstyled code before
the library runs. Kiln resolves the fence&#39;s language at build time and
emits &lt;code&gt;&amp;lt;span class=&amp;quot;hljs-…&amp;quot;&amp;gt;&lt;/code&gt; token markup once, so readers get colored
code from the first byte.&lt;/p&gt;
&lt;p&gt;The trade-off is honest: highlighting is frozen at build time, and
changing the theme means rebuilding the site. For documentation and
blogs that is exactly the right trade.&lt;/p&gt;
&lt;h2&gt;TypeScript fences&lt;/h2&gt;
&lt;p&gt;The info string&#39;s first word picks the language. A &lt;code&gt;ts&lt;/code&gt; fence becomes a
labelled, highlighted block:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;/** One build report line — what the CLI prints after emit. */
export interface EmittedPage {
  url: string;
  file: string;
}

export function summarize(pages: EmittedPage[]): string {
  const total = pages.length;
  const newest = pages.at(-1)?.url ?? &amp;quot;(none)&amp;quot;;
  return `${total} pages, newest ${newest}`;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unknown languages fall back to an escaped plain block, so a fence is
never blank and never crashes the render.&lt;/p&gt;
&lt;h2&gt;CSS fences&lt;/h2&gt;
&lt;p&gt;Stylesheets get the same treatment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;:root {
  --accent: #0969da;
}

.post-card h2 a {
  color: var(--text);
  text-decoration: none;
}

.post-card h2 a:hover {
  color: var(--accent);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Token markup under the hood&lt;/h2&gt;
&lt;p&gt;Highlighted output is plain HTML with &lt;code&gt;hljs-&lt;/code&gt; prefixed spans — the theme
in &lt;code&gt;dist/assets/hljs.css&lt;/code&gt; styles the classes, and nothing else needs to
run. Grep the built page for &lt;code&gt;hljs-&lt;/code&gt; and you will find dozens of hits.&lt;/p&gt;
&lt;h3&gt;What gets a label&lt;/h3&gt;
&lt;p&gt;Any recognized language renders a small label above the block, taken
from the fence&#39;s info string.&lt;/p&gt;
&lt;h3&gt;What falls back&lt;/h3&gt;
&lt;p&gt;A fence with no info string, or one naming a language Kiln does not
know, renders as escaped monospace text — still readable, just not
colored.&lt;/p&gt;
&lt;h2&gt;Themes&lt;/h2&gt;
&lt;p&gt;The example site picks &lt;code&gt;github-dark&lt;/code&gt; in &lt;code&gt;kiln.config.ts&lt;/code&gt;. Swapping the
key to any bundled highlight.js style changes the emitted stylesheet
without touching a single template.&lt;/p&gt;
&lt;h2&gt;Where this shows up elsewhere&lt;/h2&gt;
&lt;p&gt;Highlighted code appears across the example content — the build snippet
in &lt;em&gt;Hello, Kiln&lt;/em&gt;, the shell sessions in &lt;em&gt;Writing Markdown&lt;/em&gt;, and the
YAML block in &lt;em&gt;Frontmatter Fields&lt;/em&gt; — so the tour is also a reminder
that good examples should be runnable.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Café Münster 日本語</title>
    <id>https://bekto.github.io/kiln/posts/café-münster-日本語/</id>
    <updated>2026-03-30T00:00:00.000Z</updated>
    <link rel="alternate" href="https://bekto.github.io/kiln/posts/café-münster-日本語/"/>
    <content type="html">&lt;p&gt;This post exists to prove that Kiln handles Unicode everywhere it
counts: the filename &lt;code&gt;café-münster-日本語.md&lt;/code&gt; becomes the URL
&lt;code&gt;/posts/café-münster-日本語/&lt;/code&gt;, the title keeps its accents and its
Japanese script, and the slug stays readable in both.&lt;/p&gt;
&lt;h2&gt;A trip through three scripts&lt;/h2&gt;
&lt;p&gt;We started in &lt;strong&gt;Café Münster&lt;/strong&gt; — flat whites and filter coffee — before
the rails carried us east. Somewhere between the second interchange and
the last stop, the announcements switched to 日本語, and the platform
signs began to blur together in the best possible way.&lt;/p&gt;
&lt;h2&gt;What the pipeline does with it&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The filename is NFC-normalized and lowercased; letters from any script
survive into the slug, punctuation is dropped.&lt;/li&gt;
&lt;li&gt;Emitted paths are percent-encoded only where HTTP requires it — the
file on disk keeps its real name.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Unicode is not an edge case. Half the web&#39;s authors write in scripts
that are not ASCII, and a generator that mangles their filenames is
simply broken.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
  </entry>
  <entry>
    <title>Template Layouts</title>
    <id>https://bekto.github.io/kiln/posts/template-layouts/</id>
    <updated>2026-02-14T00:00:00.000Z</updated>
    <link rel="alternate" href="https://bekto.github.io/kiln/posts/template-layouts/"/>
    <content type="html">&lt;p&gt;Layouts are HTML templates with a few template-language tags. The example
site ships three of them — &lt;code&gt;base.html&lt;/code&gt;, &lt;code&gt;index.html&lt;/code&gt;, and &lt;code&gt;post.html&lt;/code&gt; —
plus a &lt;code&gt;partials/&lt;/code&gt; folder for reusable fragments.&lt;/p&gt;
&lt;h2&gt;How a document picks a layout&lt;/h2&gt;
&lt;p&gt;A document renders through the template its frontmatter names, falling
back to the post layout when none is set. &lt;code&gt;index.html&lt;/code&gt; declares its own:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;---
layout: index
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the template itself, the chain is ordinary template inheritance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;{% extends &amp;quot;base.html&amp;quot; %}
{% block content %}
&amp;lt;h1&amp;gt;{{ page.title }}&amp;lt;/h1&amp;gt;
{{ content | safe }}
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;base.html&lt;/code&gt; owns the document head — the Atom feed link, the stylesheets,
the navigation and search box — while every other layout fills the
content block.&lt;/p&gt;
&lt;h2&gt;Partials&lt;/h2&gt;
&lt;p&gt;Small fragments live in &lt;code&gt;templates/partials/&lt;/code&gt; and are pulled in by bare
name. The navigation embeds the search widget with a single include, and
the table of contents on this very page comes from a partial too.&lt;/p&gt;
&lt;h2&gt;Strict by default&lt;/h2&gt;
&lt;p&gt;A typo like &lt;code&gt;{{ titel }}&lt;/code&gt; fails the build with the template name and line
number instead of shipping an empty heading.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Frontmatter Fields</title>
    <id>https://bekto.github.io/kiln/posts/frontmatter-fields/</id>
    <updated>2026-01-20T00:00:00.000Z</updated>
    <link rel="alternate" href="https://bekto.github.io/kiln/posts/frontmatter-fields/"/>
    <content type="html">&lt;p&gt;Frontmatter is the YAML block between &lt;code&gt;---&lt;/code&gt; fences at the top of a
Markdown file. It is where a document declares the metadata that the rest
of the pipeline consumes — and every feature reads only the fields it
owns.&lt;/p&gt;
&lt;h2&gt;The fields this site uses&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;---
title: Frontmatter Fields   # shown in &amp;lt;title&amp;gt;, lists, and the feed
date: 2026-01-20            # ordering, archives, scheduling
tags: [reference]           # tag archives and related-post scoring
draft: true                 # hides a post unless --drafts is passed
layout: index               # which template renders the document
---
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;What happens to it&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The parser validates that the block is a YAML mapping and that &lt;code&gt;date&lt;/code&gt;
is a real date; anything else is left alone for features to check.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tags&lt;/code&gt; must be a list of non-empty strings — an empty tag fails the
build with a message naming the file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;layout&lt;/code&gt; chooses the template; when it is absent, posts render through
the default post layout.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Defaults you rarely touch&lt;/h2&gt;
&lt;p&gt;Directory names (&lt;code&gt;content/&lt;/code&gt;, &lt;code&gt;templates/&lt;/code&gt;, &lt;code&gt;public/&lt;/code&gt;, &lt;code&gt;dist/&lt;/code&gt;) live in
&lt;code&gt;kiln.config.ts&lt;/code&gt; and stay at their defaults here, so the whole site
resolves relative to its own folder.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Writing Markdown</title>
    <id>https://bekto.github.io/kiln/posts/writing-markdown/</id>
    <updated>2025-12-15T00:00:00.000Z</updated>
    <link rel="alternate" href="https://bekto.github.io/kiln/posts/writing-markdown/"/>
    <content type="html">&lt;p&gt;Markdown is the input format for every page on this site. This post walks
through the constructs the example content actually uses — headings,
lists, tables, strikethrough, and fenced code — so you can see how each
one renders before you write your own pages.&lt;/p&gt;
&lt;h2&gt;Tables, strikethrough, and friends&lt;/h2&gt;
&lt;p&gt;Kiln&#39;s renderer understands GitHub-flavored Markdown. You can &lt;del&gt;cross out
old ideas&lt;/del&gt; while keeping them readable in the source, and lay data out
in a table:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Construct&lt;/th&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Renders as&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bold&lt;/td&gt;
&lt;td&gt;&lt;code&gt;**text**&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;text&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strikethrough&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~~text~~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;del&gt;text&lt;/del&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code span&lt;/td&gt;
&lt;td&gt;&lt;code&gt;`code`&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;code&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Shell sessions in fences&lt;/h2&gt;
&lt;p&gt;Bash commands belong in a fenced block with the &lt;code&gt;bash&lt;/code&gt; info string, which
the highlighter picks up:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd site &amp;amp;&amp;amp; node ../src/cli.ts build   # bake the example site
cd site &amp;amp;&amp;amp; node ../src/cli.ts serve   # browse it locally
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A checklist for new posts&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Give every post a distinct, past &lt;code&gt;date&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tag it so it lands in a tag archive.&lt;/li&gt;
&lt;li&gt;Open with a prose paragraph — it becomes the home-page excerpt.&lt;/li&gt;
&lt;/ol&gt;
</content>
  </entry>
  <entry>
    <title>Hello, Kiln</title>
    <id>https://bekto.github.io/kiln/posts/hello-kiln/</id>
    <updated>2025-11-02T00:00:00.000Z</updated>
    <link rel="alternate" href="https://bekto.github.io/kiln/posts/hello-kiln/"/>
    <content type="html">&lt;p&gt;Kiln turns a folder of Markdown into a static website. This post is the
first of the example content: it carries a TypeScript code sample, links
to the little image asset the site ships with, and shows up on the home
list with an excerpt, a date, and a reading time.&lt;/p&gt;
&lt;h2&gt;Why a static site?&lt;/h2&gt;
&lt;p&gt;Static output is easy to host, cheap to scale, and has no database to
break at 3 a.m. You write Markdown, run one command, and upload the
resulting &lt;code&gt;dist/&lt;/code&gt; folder anywhere that can serve files.&lt;/p&gt;
&lt;h2&gt;Your first build&lt;/h2&gt;
&lt;p&gt;From the repository root, the canonical command is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// One process: load config, discover content, run features, emit dist/.
const { exitCode } = await build({ cwd: &amp;quot;site&amp;quot; });
console.log(`kiln build finished with ${exitCode}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run it and Kiln prints a report of every page it emitted. The image below
is a one-pixel PNG served straight from &lt;code&gt;public/&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/dot.png&quot; alt=&quot;dot&quot;&gt;&lt;/p&gt;
&lt;h2&gt;What to read next&lt;/h2&gt;
&lt;p&gt;If you are new to Markdown itself, start with &lt;em&gt;Writing Markdown&lt;/em&gt;; if you
are curious about metadata, read &lt;em&gt;Frontmatter Fields&lt;/em&gt;.&lt;/p&gt;
</content>
  </entry>
</feed>
