<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Articles on Liftoff</title><link>https://hugo-liftoff.kevinhorst.de/en/articles/</link><description>Recent content in Articles on Liftoff</description><generator>Hugo</generator><language>en</language><lastBuildDate>Fri, 10 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://hugo-liftoff.kevinhorst.de/en/articles/index.xml" rel="self" type="application/rss+xml"/><item><title>Every markdown element, on one page</title><link>https://hugo-liftoff.kevinhorst.de/en/articles/markdown-reference/</link><pubDate>Fri, 10 Jul 2026 00:00:00 +0000</pubDate><guid>https://hugo-liftoff.kevinhorst.de/en/articles/markdown-reference/</guid><description>&lt;!-- markdownlint-configure-file {
 "MD010": false,
 "MD014": false,
 "MD028": false,
 "MD029": false,
 "MD040": false
} --&gt;
&lt;p&gt;This article exists to be ugly on purpose. It runs through every
markdown element the theme styles, so you can check typography,
spacing, and dark-mode contrast in one place. If something looks wrong
here, it will look wrong in your content too.&lt;/p&gt;
&lt;p&gt;Every section shows the source first, then the result.&lt;/p&gt;</description></item><item><title>Shipping a docs site in an afternoon</title><link>https://hugo-liftoff.kevinhorst.de/en/articles/shipping-a-docs-site/</link><pubDate>Sun, 28 Jun 2026 00:00:00 +0000</pubDate><guid>https://hugo-liftoff.kevinhorst.de/en/articles/shipping-a-docs-site/</guid><description>&lt;p&gt;Most documentation sites die of structure, not of prose. You spend the
first afternoon on the generator, the second on navigation, and by the
third you have written four pages and lost interest. This walkthrough
skips to the part where you are writing.&lt;/p&gt;
&lt;h2 id="start-from-the-module-not-from-a-fork"&gt;Start from the module, not from a fork&lt;/h2&gt;
&lt;p&gt;Add Liftoff as a Hugo module rather than copying it into &lt;code&gt;themes/&lt;/code&gt;. You
keep the ability to update, and your repository stays small enough to
read.&lt;/p&gt;</description></item><item><title>Icons without a build step</title><link>https://hugo-liftoff.kevinhorst.de/en/articles/icons-without-a-build-step/</link><pubDate>Fri, 12 Jun 2026 00:00:00 +0000</pubDate><guid>https://hugo-liftoff.kevinhorst.de/en/articles/icons-without-a-build-step/</guid><description>&lt;p&gt;Icon fonts ship every glyph to every visitor. Sprite sheets need a
build step and a naming convention nobody remembers. Liftoff takes a
third route: it fetches each icon from the Iconify API while Hugo
builds, and inlines the SVG.&lt;/p&gt;
&lt;div class="codeblock"&gt;
 &lt;div class="codeblock__head"&gt;
 &lt;span class="codeblock__lang"&gt;go-html-template&lt;/span&gt;
 &lt;button type="button" class="codeblock__copy" data-action="copy-code" aria-label="Copy"&gt;Copy&lt;/button&gt;
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;partial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;icon.html&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;simple-icons:codeberg&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Any set on Iconify works, addressed as &lt;code&gt;set:name&lt;/code&gt;. A bare name falls
back to Material Design Icons, so &lt;code&gt;check&lt;/code&gt; and &lt;code&gt;mdi:check&lt;/code&gt; are the same
icon.&lt;/p&gt;</description></item><item><title>Multilingual from day one</title><link>https://hugo-liftoff.kevinhorst.de/en/articles/multilingual-from-day-one/</link><pubDate>Sun, 24 May 2026 00:00:00 +0000</pubDate><guid>https://hugo-liftoff.kevinhorst.de/en/articles/multilingual-from-day-one/</guid><description>&lt;p&gt;Retrofitting a second language is the kind of task that looks like an
afternoon and turns into a weekend. Every content file needs a
counterpart, every menu entry needs a translation, every data file
needs a variant, and every hardcoded date format turns out to be
English.&lt;/p&gt;
&lt;p&gt;Configuring it up front costs almost nothing, even if you only ever
ship one language.&lt;/p&gt;
&lt;div class="codeblock"&gt;
 &lt;div class="codeblock__head"&gt;
 &lt;span class="codeblock__lang"&gt;toml&lt;/span&gt;
 &lt;button type="button" class="codeblock__copy" data-action="copy-code" aria-label="Copy"&gt;Copy&lt;/button&gt;
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;defaultContentLanguage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;defaultContentLanguageInSubdir&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;English&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;weight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Content files then carry their language in the filename:
&lt;code&gt;about.md&lt;/code&gt; and &lt;code&gt;about.de.md&lt;/code&gt; sit side by side and Hugo pairs them
automatically.&lt;/p&gt;</description></item><item><title>A performance budget you can actually keep</title><link>https://hugo-liftoff.kevinhorst.de/en/articles/a-performance-budget/</link><pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate><guid>https://hugo-liftoff.kevinhorst.de/en/articles/a-performance-budget/</guid><description>&lt;p&gt;A performance budget only works if it is short enough to remember.
Three numbers are enough for a content site.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Budget&lt;/th&gt;
					&lt;th&gt;Target&lt;/th&gt;
					&lt;th&gt;What breaks it&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Requests per page&lt;/td&gt;
					&lt;td&gt;Under 10&lt;/td&gt;
					&lt;td&gt;Web fonts, analytics&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CSS&lt;/td&gt;
					&lt;td&gt;Under 30 kB&lt;/td&gt;
					&lt;td&gt;Utility frameworks&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;JavaScript&lt;/td&gt;
					&lt;td&gt;Under 10 kB&lt;/td&gt;
					&lt;td&gt;Anything with a build&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Liftoff spends its JavaScript on three things: the theme toggle, the
copy buttons on code blocks, and the blueprint filter. All three
degrade to something usable when scripting is off, which is the only
reason they are allowed to exist at all.&lt;/p&gt;</description></item><item><title>Dark mode that respects the system</title><link>https://hugo-liftoff.kevinhorst.de/en/articles/dark-mode-defaults/</link><pubDate>Fri, 08 May 2026 00:00:00 +0000</pubDate><guid>https://hugo-liftoff.kevinhorst.de/en/articles/dark-mode-defaults/</guid><description>&lt;p&gt;A theme toggle that defaults to dark for everyone is a preference
disguised as a feature. Liftoff reads &lt;code&gt;prefers-color-scheme&lt;/code&gt; first, and
only writes to &lt;code&gt;localStorage&lt;/code&gt; once someone actually picks a scheme.&lt;/p&gt;
&lt;div class="codeblock"&gt;
 &lt;div class="codeblock__head"&gt;
 &lt;span class="codeblock__lang"&gt;js&lt;/span&gt;
 &lt;button type="button" class="codeblock__copy" data-action="copy-code" aria-label="Copy"&gt;Copy&lt;/button&gt;
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;liftoff.theme&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;(prefers-color-scheme: light)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That ordering matters. A visitor whose system is light gets a light
page on first load, without a flash of dark. A visitor who then picks
dark keeps dark, even if their system changes later, because an
explicit choice outranks an inferred one.&lt;/p&gt;</description></item><item><title>On design tokens</title><link>https://hugo-liftoff.kevinhorst.de/en/articles/design-tokens/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://hugo-liftoff.kevinhorst.de/en/articles/design-tokens/</guid><description>&lt;p&gt;Liftoff exposes its palette, spacing scale, and typography as CSS
custom properties on &lt;code&gt;:root&lt;/code&gt;. Consumers can theme the site without
forking the theme.&lt;/p&gt;
&lt;h2 id="override-pattern"&gt;Override pattern&lt;/h2&gt;
&lt;p&gt;Drop a small CSS file into your site&amp;rsquo;s &lt;code&gt;assets/css/&lt;/code&gt; and import it
after the theme&amp;rsquo;s bundle.&lt;/p&gt;
&lt;div class="codeblock"&gt;
 &lt;div class="codeblock__head"&gt;
 &lt;span class="codeblock__lang"&gt;css&lt;/span&gt;
 &lt;button type="button" class="codeblock__copy" data-action="copy-code" aria-label="Copy"&gt;Copy&lt;/button&gt;
 &lt;/div&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ff7a59&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--color-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#0d0a0a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Light mode is opt-in by setting &lt;code&gt;data-theme=&amp;quot;light&amp;quot;&lt;/code&gt; on &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;. The
built-in toggle remembers the user&amp;rsquo;s choice in &lt;code&gt;localStorage&lt;/code&gt; with
&lt;code&gt;prefers-color-scheme&lt;/code&gt; as the fallback.&lt;/p&gt;</description></item><item><title>Hello, Liftoff</title><link>https://hugo-liftoff.kevinhorst.de/en/articles/hello-liftoff/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://hugo-liftoff.kevinhorst.de/en/articles/hello-liftoff/</guid><description>&lt;p&gt;I kept rebuilding the same scaffold for every small product page: a
hero with a code block, OS-specific install tabs, a feature grid, a
docs section, and a closing CTA. Liftoff bundles those pieces into one
Hugo theme with sensible dark-first defaults.&lt;/p&gt;
&lt;h2 id="whats-in-the-first-cut"&gt;What&amp;rsquo;s in the first cut&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Hero partial driven by front matter&lt;/li&gt;
&lt;li&gt;OS tabs as a CSS-light shortcode&lt;/li&gt;
&lt;li&gt;Code blocks with copy buttons&lt;/li&gt;
&lt;li&gt;Feature, integrations, pricing, testimonial, and CTA shortcodes&lt;/li&gt;
&lt;li&gt;Docs layout with card index, on-this-page TOC, and prev/next navigation&lt;/li&gt;
&lt;li&gt;Blog and showcase sections&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="where-its-going"&gt;Where it&amp;rsquo;s going&lt;/h2&gt;
&lt;p&gt;Internationalisation strings ship for German and English. More
languages are a &lt;code&gt;i18n/&amp;lt;lang&amp;gt;.yaml&lt;/code&gt; file away.&lt;/p&gt;</description></item></channel></rss>