Style Guide
A complete reference for every markdown and HTML element used in this Saga theme.
This page is the canonical styling reference for this Saga theme. Every element that the theme styles appears at least once below. Use it to verify your theme after changes, and refer to the Special Cases section at the bottom for elements that require raw HTML or a CSS class.
Typography and text
Body text is set in Avenir Next at 18px / 1.7 line-height. On platforms without Avenir Next (Windows, Android) the system sans-serif renders cleanly as a fallback.
This paragraph demonstrates bold (**bold**), italic (*italic*), and bold italic (***bold italic***). You can also use inline code which renders with a warm parchment background.
Here is a link to the home page — links render in the muted-red accent colour with a hairline underline.
Heading levels
H2 above is 28 px / 600 weight. H3 here is 21 px / 600 weight. Both use Avenir Next with tight letter-spacing. H4 and below follow the same family at progressively smaller sizes.
H4 example
H5 example
H6 example
Blockquote
The model picks the screen. The schema picks the model’s vocabulary. The renderer is glass.
Use blockquotes for pull-quotes and attributed passages. They render with a 2 px muted-red left rule and italic Avenir Next at 20 px.
Lists
Unordered list
- Field kinds:
text,picker,toggle,date,list. - Validation hints, expressed as a tiny DSL the renderer evaluates locally.
- A small library of named presets the model can reference by name instead of describing.
- Nested item — indented one level.
- Another nested item.
Ordered list
- Hallucinated fields. The model invents a control that doesn’t map to anything the renderer can produce.
- Ugly-but-valid layouts. The spec is correct but the result looks like 1998.
- Excessive agreement. The model says yes to bad ideas. Fixed by a system prompt that asks it to push back.
Code
Inline code
Use backtick fences for short references: UISpec, compact-list, --color-accent. Inline code renders with a warm parchment background (#F0EBDC) and dark ink (#5A4A20).
Fenced code block
struct UISpec: Codable {
let title: String
let fields: [Field]
let primary: Action
let secondary: Action?
}
Code blocks use a dark (#020617) background with light ink — matching the dark-theme preference selected during setup.
/* CSS example */
.entry-body h2 {
font-family: var(--font-serif);
font-size: 1.75rem;
font-weight: 600;
letter-spacing: -0.015em;
}
{
"model": "claude-haiku-4-5",
"latency": "380ms",
"cost_per_1k": "$0.42"
}
Images
A full-bleed image with a caption:
Figure 1. Caption text renders in 13 px italic Avenir Next, in --color-text3.
Images inside .entry-body receive border-radius: 6px and are capped at 100% width.
Tables
Standard pipe tables. Column alignment is controlled with :---, ---:, and :---: in the separator row.
| Model | Median latency | Cost / 1k specs | Reject rate |
|---|---|---|---|
| Claude Haiku 4.5 | 380 ms | $0.42 | 2.1% |
| GPT-4o mini | 510 ms | $0.31 | 3.8% |
| Gemini 2.5 Flash | 340 ms | $0.18 | 5.4% |
| Local Llama 3.1 8B | 1,200 ms | $0.00 | 11.2% |
Table 1. Measured against a fixed schema, on 1,000 representative prompts. Your mileage will vary.
Table styling notes:
- Header row: 1 px solid ink bottom border, uppercase small-caps labels (12 px, 0.04 em tracking).
- Body rows: 0.5 px hairline bottom border in
--color-hair. - Right-aligned
Costcolumn uses---:in the separator. The theme respects bothstyle="text-align: right"(emitted by Saga’s markdown parser) and the[align="right"]attribute. - The italic line immediately after the table becomes a caption.
Wide table (use the scroll wrapper)
For tables wider than the reading column, wrap the raw HTML in a <div class="table-scroll"> (see Special Cases below).
Horizontal rule
A hairline rule above and below this paragraph.
Special Cases — elements that require raw HTML or a CSS class
These elements cannot be produced with standard Markdown alone. Use them by dropping raw HTML into your .md file. Saga passes raw HTML through untouched.
Subscribe aside
Drop this block between paragraphs in a post. It renders with an accent-coloured top and bottom rule and a warm tinted background.
<div class="subscribe-aside">
<p class="subscribe-aside-label">An aside</p>
<p class="subscribe-aside-heading">
If you're getting something from this,
<em>the newsletter is a way to get more.</em>
</p>
<p>One short essay most Sundays. Swift, AI tools, and the long arc
of shipping software. No teasers, no fluff.</p>
</div>
Live example:
An aside
If you're getting something from this, the newsletter is a way to get more.
One short essay most Sundays. Swift, AI tools, and the long arc of shipping software. No teasers, no fluff.
Social proof line
Use above any Subscribe CTA. The <strong> carries the reader count in full ink; the rest is muted.
<p class="social-proof">
<span class="social-proof-dot"></span>
<span><strong>Join 4,000 readers.</strong> Swift, AI tools, and shipping software.</span>
</p>
Live example:
Newsletter form
Standard placement: inside any page, centered. The form POSTs directly to Substack.
<div class="newsletter-form-wrap">
<form class="newsletter-form"
action="https://newsletter.deverman.org/api/v1/free"
method="post">
<input class="newsletter-form-input" type="email" name="email"
placeholder="[email protected]" required autocomplete="email" />
<input type="hidden" name="redirect"
value="https://deverman.org/subscribed/" />
<button class="newsletter-form-btn" type="submit">Subscribe</button>
</form>
<p class="newsletter-form-note">No spam. Unsubscribe whenever.</p>
</div>
Live example:
Article gate (gated posts)
To gate a post, add gated: true to its front matter:
---
title: My gated post
date: 2026-05-16
path: /writing/my-post/
gated: true
---
The Swift renderer automatically:
- Shows the first paragraph freely
- Blurs the rest with a subscribe card overlay
- Embeds a newsletter form with
?return=/writing/my-post/so the subscriber is redirected back after signing up
Unlock flow:
- Visitor subscribes → Substack redirects to
/subscribed/?return=/writing/my-post/ /subscribed/page setslocalStorage.setItem('deverman_subscribed', '1')- Visitor is auto-redirected back to the article after 4 seconds
- The inline
<script>in<head>adds.deverman-unlockedto<html>before first paint - CSS hides the gate card and shows the full content
On all future visits the article loads fully unlocked without any flash.
To reset the gate (for testing): open browser DevTools → Application → Local Storage → delete deverman_subscribed.
Wide table with scroll
<div class="table-scroll">
| Very long first column header | Second header | Third header | Fourth header | Fifth header |
|-------------------------------|---------------|--------------|---------------|--------------|
| Row one value | 123 | abc | yes | 99% |
| Row two value | 456 | def | no | 87% |
</div>
Live example:
| Very long first column header | Second header | Third header | Fourth header | Fifth header |
|---|---|---|---|---|
| Row one value | 123 | abc | yes | 99% |
| Row two value | 456 | def | no | 87% |
Small-caps label
Used as a section heading for archive pages, timeline sections, etc.
<p class="label">Recent Writing</p>
Recent Writing
Buttons
Two pill-style CTA buttons.
<a class="btn-primary" href="#">Subscribe to the newsletter</a>
<a class="btn-secondary" href="#">Get in touch</a>
Live example:
Project card
Used on the home and work pages. Each card has a full-width coloured .project-card-hero area with large italic initials. An optional .project-card-featured badge appears top-right of the hero. Wrap two in .grid-2col.
<div class="grid-2col">
<div class="project-card">
<div class="project-card-hero" style="background:#ECE6D5;color:#B5A98A;">SE</div>
<div class="project-card-body">
<div class="project-card-tags">
<span class="project-card-tag">Stream Deck</span>
<span class="project-card-tag">Shipped</span>
</div>
<h3 class="project-card-title">SafeEject</h3>
<p class="project-card-desc">One-press disk manager for macOS. Native Swift, sold on the Stream Deck marketplace.</p>
<a class="project-card-action" href="#">View on marketplace →</a>
</div>
</div>
<div class="project-card">
<div class="project-card-hero" style="background:#E0E7DF;color:#8AA89C;">
FR
<span class="project-card-featured">★ Featured</span>
</div>
<div class="project-card-body">
<div class="project-card-tags">
<span class="project-card-tag">Open Source</span>
<span class="project-card-tag">25 ★</span>
</div>
<h3 class="project-card-title">FocusRelayMCP</h3>
<p class="project-card-desc">Talk to your OmniFocus tasks. A Swift bridge connecting AI assistants to your task system.</p>
<a class="project-card-action" href="#">Star on GitHub →</a>
</div>
</div>
</div>
Live example:
SafeEject
One-press disk manager for macOS. Native Swift, sold on the Stream Deck marketplace.
View on marketplace →FocusRelayMCP
Talk to your OmniFocus tasks. A Swift bridge connecting AI assistants to your task system.
Star on GitHub →Case study row
Used on the work page. Outputs a div.case-row with year, title+detail, and action.
<div class="case-row">
<div class="case-row-year">2024</div>
<div class="case-row-body">
<div class="case-row-title">Amplify Health (AIA): Cloud migration</div>
<div class="case-row-detail">13 TB Oracle to PostgreSQL · 34-person team</div>
</div>
<div class="case-row-action">Read case study →</div>
</div>
Live example:
Timeline row
Used on the About page.
<div class="timeline-row">
<span class="timeline-row-years">2026–present</span>
<span class="timeline-row-role">Apple Developer Academy</span>
<span class="timeline-row-place">Bali, Indonesia</span>
</div>
Live example:
Now-page bullet list
Accent-dot bullet list used on the Now page.
<ul class="now-list">
<li>Working through the Apple Developer Academy curriculum in Bali.</li>
<li>A Miro plugin compiled with SwiftWasm.</li>
</ul>
Live example:
- Working through the Apple Developer Academy curriculum in Bali.
- A Miro plugin compiled with SwiftWasm.
Page hero
Used on section pages (Writing, Work, About, Now). The .page-hero-label slot takes a .label element. em inside .page-hero-title renders in italic 500 weight.
<div class="page-hero">
<p class="page-hero-label"><span class="label">Writing</span></p>
<h1 class="page-hero-title">Essays on <em>building software</em></h1>
<p class="page-hero-dek">Swift, AI tools, and the long arc of shipping things that matter.</p>
</div>
Live example:
Writing
Essays on building software
Swift, AI tools, and the long arc of shipping things that matter.
Article header
Used on single post views. Emitted by the Swift renderer — shown here for reference when building custom pages.
<header class="article-header">
<div class="article-meta">
May 16, 2026
<span class="article-meta-dot"></span>
<a class="tax-pill" href="/tag/swift/">Swift</a>
<a class="tax-pill tax-pill--accent" href="/tag/featured/">Featured</a>
</div>
<h1 class="article-title">How I rebuilt my site with Saga</h1>
<p class="article-dek">A quiet rewrite, a weekend of yak-shaving, and lessons about when to stop tweaking.</p>
<div class="article-byline-row">
<img class="article-byline-avatar" src="/static/avatar.jpg" alt="Brent Deverman" />
Brent Deverman
</div>
</header>
Live example:
How I rebuilt my site with Saga
A quiet rewrite, a weekend of yak-shaving, and lessons about when to stop tweaking.
List rows (archive pattern)
The writing archive and tag pages use .list-rows > .list-row. Each row is a three-column grid: date / title / action. Use .list-section-year to group rows by year. The .list-row--compact modifier reduces vertical padding for dense lists.
<div class="list-section-year">
<span class="label">2026</span>
<span class="list-section-year-rule"></span>
</div>
<ul class="list-rows">
<li class="list-row">
<span class="list-row-date">May 16</span>
<a class="list-row-title" href="#">How I rebuilt my site with Saga</a>
<a class="list-row-action" href="#">Read →</a>
</li>
<li class="list-row">
<span class="list-row-date">Apr 3</span>
<a class="list-row-title" href="#">Advice to a young entrepreneur</a>
<a class="list-row-action" href="#">Read →</a>
</li>
</ul>
Live example:
Taxonomy pills
Used for category and tag chips. .tax-pill--accent renders in the muted-red accent tint — used for high-signal tags like “Featured”.
<a class="tax-pill" href="#">Swift</a>
<a class="tax-pill" href="#">AI Tools</a>
<a class="tax-pill tax-pill--accent" href="#">Featured</a>
Live example:
Writing filter tabs
Used on the Writing page to filter by tag. The .active state fills the pill with ink.
<div class="writing-filters">
<button class="writing-filter-tab active">All</button>
<button class="writing-filter-tab">Swift</button>
<button class="writing-filter-tab">AI Tools</button>
<button class="writing-filter-tab">Entrepreneurship</button>
</div>
Live example:
RSS link chip
Small inline chip used on archive pages to link to a feed.
<a class="rss-link" href="/posts/feed.xml">
<span class="rss-link-icon"></span>
RSS feed
</a>
Live example:
Now page pulse dot
Live indicator shown beside “Last updated” on the Now page. The dot pulses via CSS animation.
<span class="now-pulse">
<span class="now-pulse-dot"></span>
Last updated May 16, 2026 · Bali, Indonesia
</span>
Live example:
Case study components
A full set of named classes for case study pages. Use these instead of inline styles for consistent typography and spacing.
Meta row
<div class="cs-meta">
Amplify Health
<span class="cs-meta-dot"></span>
2024
<span class="cs-meta-dot"></span>
Cloud Migration
</div>
Metric strip
Three-column card showing headline numbers. Stacks to single column on mobile.
<div class="metric-strip">
<div class="metric-strip-item">
<div class="metric-strip-num">13 TB</div>
<div class="metric-strip-label">Data migrated from Oracle to PostgreSQL</div>
</div>
<div class="metric-strip-item">
<div class="metric-strip-num">34</div>
<div class="metric-strip-label">Person cross-functional team</div>
</div>
<div class="metric-strip-item">
<div class="metric-strip-num">6 mo</div>
<div class="metric-strip-label">From kickoff to production cutover</div>
</div>
</div>
Stat grid
Four key/value pairs in a compact grid, separated from content above by a hairline rule.
<div class="case-study-stat-grid">
<div><div class="case-study-stat-key">Client</div><div class="case-study-stat-val">Amplify Health</div></div>
<div><div class="case-study-stat-key">Year</div><div class="case-study-stat-val">2024</div></div>
<div><div class="case-study-stat-key">Role</div><div class="case-study-stat-val">Lead Architect</div></div>
<div><div class="case-study-stat-key">Stack</div><div class="case-study-stat-val">PostgreSQL, AWS</div></div>
</div>
Body typography (cs-h2, cs-p, cs-blockquote)
<h2 class="cs-h2">The challenge</h2>
<p class="cs-p">Thirteen terabytes of Oracle data, a 34-person team, and six months to production. The migration had to be zero-downtime.</p>
<div class="cs-blockquote">
The database was the heart of everything. We couldn't just lift and shift.
<div class="cs-blockquote-attr">— Head of Engineering, Amplify Health</div>
</div>
The challenge
Thirteen terabytes of Oracle data, a 34-person team, and six months to production. The migration had to be zero-downtime.
Figure placeholder
Use while waiting for a real image. The hatched background and monospace caption label the slot.
<div class="cs-figure">
<span class="cs-figure-caption">FIG 1 — ARCHITECTURE DIAGRAM</span>
</div>
Tag strip + CTA card
Footer of a case study. Tags on the left, a sharing note on the right. CTA card below.
<div class="cs-tag-strip">
<div class="cs-tags">
<span class="cs-tag">PostgreSQL</span>
<span class="cs-tag">Cloud Migration</span>
<span class="cs-tag">Oracle</span>
</div>
<span class="cs-shared-note">Share this case study</span>
</div>
<div class="case-cta-card">
<div>
<strong>Need a similar migration?</strong>
<p style="font-size:13px;color:var(--color-text2);margin:4px 0 0;">I consult on large-scale database and cloud migrations.</p>
</div>
<a class="btn-secondary" href="/work/">See my work →</a>
</div>
I consult on large-scale database and cloud migrations.
What is not yet styled
- Page transition animations — Hover animations from the design’s motion artboards are not implemented; they require a JS runtime. CSS view transitions (cross-fade between pages) are implemented.