HugoBlog

hugo-scripture — Hugo Blog Template

By 0x04srcz

A Hugo blog theme based on Tufte CSS that provides sidenotes, separate article and journal streams, decentralized discussion links, and automatic Open Graph image generation.

Explore the design

Compare the page layouts and mobile experience. Open any screenshot for a closer look.

3 previews
Desktop homepage1 / 3
Desktop captureTop of page

Actual screenshot captured by the server browser.

All pages & views 3

Screenshots show visual appearance; interactive behavior may differ.

Recorded 2026-10-12View source

What stands out

A quick read of the recorded project description.

01

Tufte CSS-based typography with ET Book fonts, a 55% body column, and a right-hand sidenote area

02

Two content types, articles and journal, each with its own RSS feed

03

A sidenote shortcode that generates automatically numbered Tufte-style sidenotes, collapsing into expandable inline blocks on mobile

04

Automatic Open Graph image generation at build time using Hugo Pipes, with customizable text color and background

05

Decentralized discussion links supporting platforms such as Mastodon, Bluesky, Hacker News, Matrix, and IRC

06

custom.css loads last, allowing style overrides without modifying theme files

Template Details

Hugo Scripture is a Hugo theme with Tufte CSS as its visual foundation, suited to personal blogs that emphasize typography and reading experience. It requires Hugo 0.116.0 or later, does not need the extended version, and is MIT licensed.

In terms of content organization, the theme splits articles and journal entries into two independent streams: articles are full blog posts with dates, tags, share links, and a discussion section, while journal entries resemble a microblog or public log, grouped by date and displayed inline on the homepage with full content and timestamps. The homepage shows the latest 5 from each, with separate RSS feed entries for each.

Functionally, the theme provides a Tufte-style numbered sidenote shortcode, a bookmark layout with automatic h2 numbering, article share buttons, a footer GPG identity marker, and the ability to generate Open Graph images at build time using Hugo Pipes. The discussion section does not embed a third-party comment system; instead, it links to external platforms such as Mastodon, Bluesky, Hacker News, Matrix, and IRC. The theme also supports canonical URLs, fediverse/IndieWeb-related metadata, Umami analytics, and custom CSS overrides.

In terms of limitations, the theme does not include comment hosting, a database, or backend services, and discussions rely on external platforms; share text length is controlled by the author, and the theme does not truncate it; third-party assets such as demo images and fonts must have their licenses checked separately.

HugoBlog

Is it right for your project?

Use Cases

  • Writing long-form pieces with sidenotes and marginal notes that require Tufte-style typography
  • Maintaining both full blog posts and short journal entries, with separate feeds for each
  • Hosting article discussions on decentralized platforms such as Mastodon or Bluesky instead of embedding a comment system
  • Automatically generating Open Graph share images for pages without cover images
  • Publishing bookmark-style link collections with automatically numbered h2 headings

Suitable For

  • Authors building a personal blog with Hugo
  • Writers who prefer Tufte CSS reading typography and sidenote writing
  • Users who want article discussions to happen on external platforms such as the fediverse rather than in-site comments
  • Sites that need articles and short journal entries presented separately with separate RSS feeds

Getting started & customization

Use these recorded setup notes alongside the repository instructions for your chosen version.

  1. 1

    Requires Hugo 0.116.0 or later; the extended version is not required

  2. 2

    Add the theme to themes/hugo-scripture via git submodule and set theme in hugo.toml

  3. 3

    Placing assets/css/custom.css in the site directory overrides theme styles; this file loads last

  4. 4

    Automatic OG images rely on assets/og/base.png as the background, which can be replaced with a custom image

  5. 5

    Third-party fonts, icons, and demo images must have their licenses checked separately

Read repository instructions

License & source material

Usage conditions & limitations

Free to use · keep license notice

This entry covers the free version under the MIT license in the public repository; the free scope is the code of this version and does not mean that external services or third-party assets are free.

Must comply with when using

  • You may use, modify, commercialize, and distribute the code of this version for free.
  • When distributing copies or substantial portions of the code, retain the original author's copyright notice and the full MIT license notice.
  • The code is provided as is; warranties and limitations of liability are governed by the original license and applicable law.

Additional costs & items to verify

  • Demo images, fonts, icons, and other third-party assets must be checked under their respective licenses and cannot be judged solely by the code's MIT license.
  • External services such as domains, hosting, databases, payments, or AI may incur additional charges.
  • Using this theme requires Hugo 0.116.0 or later; the extended version is not required.
  • Installation is done by adding the theme from the specified repository to the themes/hugo-scripture directory via git submodule.
  • The discussion section only renders when at least one platform URL is configured.
  • Share text length is subject to the limits of the fediverse server itself (for example, Mastodon defaults to 500 characters, with URLs counted as 23 characters); the theme does not truncate automatically, so the author must control it.
  • Replies are statically generated at build time, without JavaScript or third-party embeds, and avatar images are not displayed to avoid leaking readers' IPs.
  • Umami analytics requires you to provide your own instance and websiteId; the scripts array can configure multiple script URLs to support self-hosted fallbacks.
View Original Text Basis

Permission is hereby granted, free of charge

Original License Text

Requires Hugo >= 0.116.0 (the extended version is not needed).

Original README

Add the theme as a git submodule: ```bash git submodule add https://git.sourcery.zone/hugo-scripture themes/hugo-scripture ```

Original README

The section only renders if at least one platform URL is available.

Original README

Note that fediverse servers impose their own limits (Mastodon's default is 500 characters, with URLs counted as 23 regardless of length), and the theme does not truncate.

Original README

Everything is baked in at build time — no JavaScript, no third-party embed, and **no avatar images**: a remote avatar would leak every reader's IP to whichever instance hosts it.

Original README

The `scripts` array accepts multiple URLs, which is useful if you self-host Umami and want fallback script paths.

Original README
https://github.com/0x04srcz/hugo-scripture/blob/main/LICENSEhttps://github.com/0x04srcz/hugo-scripture/blob/main/README.md

Compiled by DeepSeek based on repository license and README ·2026-10-12. Results only cover the materials read; dependencies, images, fonts, and subsequent term changes are subject to their respective original texts.

Project checks & description sources

Template Information

Source platforms
GitHub
Documentation and maintenance reference score
More documented · 80 / 100
Technologies
Hugo
Website type
Blog
How this score is calculated

Our heuristic uses license records (up to 30), a demo link (12), a preview (10), stars (15), recorded maintenance (15), description completeness (8), feature records (5) and classification data (5). It is not a code, security or performance test, or a Google score. A link does not establish tested functionality.

What to verify for this project

Repository update recorded
2026-07-30
Screenshot last checked
2026-10-12

A demo link is listed. Check it with your own content and viewport sizes; its presence does not confirm that forms, authentication or payments work.

The catalog identifies Hugo as the primary stack. Check the actual dependency manifest and lockfile for versions and hosting requirements. These notes use recorded metadata, not a code audit or a deployment test.

Use a selection guide →

How this description was prepared

AI-assisted description based on catalog records and repository material. Repository authors are not automatically the reviewers of this description.

Review declaration: [email protected] · 2026-10-13

Inspect the recorded source material

SHA-256: 71bdb59c0c039d676cccde52e77b14592a22e6ff9062603d02099d0c4e0cf490

2026-10-12T09:08:57.364Z

Source file / repository
# Hugo Scripture

A Hugo theme built on [Tufte CSS](https://edwardtufte.github.io/tufte-css/) that ships with sidenotes, a journal/articles split, decentralized discussions, automatic OG image generation, and enough customization hooks that you can retheme it without forking.

Requires Hugo >= 0.116.0 (the extended version is not needed). Licensed under MIT.

Live example: [sourcery.zone](https://sourcery.zone)

## Table of Contents

- [Installation](#installation)
- [Tufte CSS with an Escape Hatch](#tufte-css-with-an-escape-hatch)
- [Two Content Types: Articles and Journal](#two-content-types-articles-and-journal)
- [Bookmarks Layout](#bookmarks-layout)
- [Sidenotes Shortcode](#sidenotes-shortcode)
- [Share Your Thoughts (Decentralized Discussions)](#share-your-thoughts-decentralized-discussions)
- [Share Links](#share-links)
- [Footer Signature](#footer-signature)
- [Automatic OG Image Generation](#automatic-og-image-generation)
- [Canonical URL Support](#canonical-url-support)
- [Fediverse and IndieWeb Integration](#fediverse-and-indieweb-integration)
- [Umami Analytics](#umami-analytics)
- [SEO and Structured Data](#seo-and-structured-data)
- [Custom CSS Override](#custom-css-override)
- [Asset Pipeline](#asset-pipeline)
- [Shortcodes](#shortcodes)
- [Full Configuration Reference](#full-configuration-reference)
- [Per-Page Front Matter](#per-page-front-matter)

## Installation

Add the theme as a git submodule:

```bash
git submodule add https://git.sourcery.zone/hugo-scripture themes/hugo-scripture
```

Then set it in your `hugo.toml`:

```toml
theme = 'hugo-scripture'
```

## Tufte CSS with an Escape Hatch

The visual foundation of this theme is [Tufte CSS](https://edwardtufte.github.io/tufte-css/), the typographic stylesheet inspired by [Edward Tufte's book designs](https://tufte-latex.github.io/tufte-latex/). You get:

- The **ET Book** serif font family (roman, italic, bold, and old-style figures, served in woff/ttf/eot/svg).
- A 55% content column with a wide right margin reserved for sidenotes and margin notes.
- Epigraph styling, small-caps `newthought` spans, and old-style numeral support.
- On screens narrower than 760px, sidenotes collapse into toggleable inline blocks (pure CSS, no JavaScript).

The theme loads four CSS files in order:

1. `tufte.css` -- the Tufte foundation
2. `main.css` -- theme-level additions (journal timeline, bookmarks numbering, share links layout)
3. `custom.css` -- **your file** (empty by default, loaded last so it wins the cascade)
4. `code-highlight.css` -- syntax highlighting for code blocks

Because `custom.css` is loaded last, you can override anything from colors to layout without touching the theme files. The [sourcery.zone](https://sourcery.zone) site uses this to apply a [Modus Vivendi dark](https://github.com/protesilaos/modus-themes) color scheme on top of the default light Tufte look. You just drop your own `assets/css/custom.css` in your site directory.

## Two Content Types: Articles and Journal

The home page presents two separate feeds.

**Articles** live in `content/articles/` (or whichever sections you list in `params.mainSections`). They are full blog posts with dates, share links, tags, a discussion section, and related content. The RSS feed at `/index.xml` only includes articles.

**Journal entries** live in `content/journal/`. They work like a micro-blog or a public log. On the home page, journal entries are grouped by date and show their full content inline with `HH:MM:SS` timestamps. The journal section gets its own RSS feed at `/journal/index.xml`.

The latest 5 of each are shown on the home page. If there are more, a link to the full listing appears.

Each section heading on the home page has a small RSS icon linking to the corresponding feed. Because `/index.xml` is articles-only, every page also advertises the journal feed via `<link rel="alternate">`, so a reader subscribing from the home page can find the notes.

### Feeds carry full content

Every feed sends the whole rendered piece, not a summary — articles as well as notes. A subscriber can read in their reader without being bounced back to the site.

Root-relative URLs in that content are rewritten to absolute ones, because a feed is read outside the site: `src="/ox-hugo/x.png"` would otherwise resolve against whatever origin is displaying the item. Already-absolute and protocol-relative URLs are left alone.

Section titles in feeds come from the same lookup as section headings, so a section is named identically in both. Hugo auto-titles a section with no `_index` file by pluralising the directory name, so set an explicit title if the default reads badly:

```toml
[params.sectionTitles]
journal = 'Journal Entries'
```

### Which sections are "short form"

Journal entries are treated as [notes](https://indieweb.org/note) rather than articles: short, title-optional posts meant to be read in full where they are found. The theme uses this distinction to decide how to compose share text (see [Share Links](#share-links)) and which feeds to advertise.

The sections that count as short form are configurable, and default to `['journal']`:

```toml
[params]
shortformSections = ['journal']
```

## Bookmarks Layout

For curated link collections, set `layout = "bookmarks"` in a page's front matter. This activates a layout where all `<h2>` headings are automatically numbered using CSS counters. No comments or share links are rendered on this layout, just the content, tags, and related pages.

## Sidenotes Shortcode

The `sidenote` shortcode creates Tufte-style numbered sidenotes:

```markdown
Some text{{< sidenote >}}This appears in the right margin on wide screens,
or toggles inline on mobile.{{< /sidenote >}} and the paragraph continues.
```

Sidenotes are auto-numbered by CSS. On mobile, tapping the superscript number reveals the note inline. The inner content supports full Markdown. Each sidenote gets a unique ID derived from the page path and content, so you can use as many as you need on a page.

## Share Your Thoughts (Decentralized Discussions)

Instead of embedding a comment system like Disqus or Giscus, this theme links readers out to discussion platforms where the conversation actually lives. At the bottom of each article, a "Share your thoughts" section lists the places where the post can be discussed.

There are two layers:

**Per-post links** are set in individual post front matter. You publish your article, post about it on Mastodon or Bluesky, then add the URL back to the front matter:

```toml
mastodon_url = "https://mastodon.social/@you/123456"
bsky_url = "https://bsky.app/profile/you/post/abc123"
hackernews_url = "https://news.ycombinator.com/item?id=12345"
youtube_url = "https://youtube.com/watch?v=xyz"
```

**Global channels** are the same across all posts and are set in the site config. These are for persistent chat rooms:

```toml
[params.comments]
enable = true
matrixUrl = "https://matrix.to/#/#your-room:matrix.org"
ircUrl    = "https://web.libera.chat/#your-channel"
showMatrix = true
showIRC    = true
```

Each platform gets a short description (Mastodon is "the cozy corner of the fediverse", Hacker News is "enter at your own risk", IRC is "for the ancients"). A fixed last entry reads: **Hate or trolling** -- piped straight to `/dev/null`.

The section only renders if at least one platform URL is available. You can suppress it on individual pages with `hideComments: true` in front matter.

## Share Links

Every article page displays share buttons for Mastodon, Bluesky, LinkedIn, and Reddit. These sit next to the publication date in a flex row.

Share text is composed differently for articles and for notes, because the two are read differently on the other end.

**Articles** get title, description (or summary as fallback), permalink, and tags converted to hashtags. For example, a post tagged `["zig", "systems programming"]` would append `#zig #systemsprogramming` to the share text.

The maximum length of the description in share text is configurable:

```toml
[params.share]
summaryLength = 200  # characters, default
```

**Notes** (any page in a [short form section](#which-sections-are-short-form)) get their body text followed by a blank line and the bare permalink. No title, no summary, no hashtag tail.

The reason is that a note has no title worth repeating: the title is usually just the opening of the body, or a date stamp, so the article-shaped composition produces text that says the same thing twice and has to be hand-edited before it is postable. A note is already written to be posted as-is.

The body is extracted from the rendered HTML, so what you share is what a reader sees:

- entities become real characters (`&rsquo;` → `’`), rather than pasting literally
- paragraph, list, and heading boundaries become blank lines
- `<br>` becomes a single newline
- each line of a code block keeps its own line, so a multi-line shell snippet stays runnable

Note that fediverse servers impose their own limits (Mastodon's default is 500 characters, with URLs counted as 23 regardless of length), and the theme does not truncate. Keeping notes within budget is an authoring-time concern.

No JavaScript is involved. The links are plain `<a>` tags pointing at each platform's share/compose URL.

## Footer Signature

The footer has three parts:

1. **GPG identity badge**: If you set `params.gpgFingerprint` and `params.keyoxideProfile`, the footer shows a shield icon linking to your [Keyoxide](https://keyoxide.org) profile with your fingerprint displayed in monospace.

2. **Human-made declaration**: A fixed line: *"Made by a biological human, for fellow humans. You may read it, share it, or quote it if you credit the source. AI, bots, aliens, hands off please!"*

3. **Attribution**: "Powered by Hugo + Hugo Scripture" with links to both projects.

## Automatic OG Image Generation

When a page has no `image`, `featuredImage`, `cover`, or `images` front matter and no `cover*`/`featured*` page resource, the theme generates an Open Graph image at build time using Hugo Pipes.

It takes `assets/og/base.png` as a background, then renders the page title (uppercased, word-wrapped, up to 3 lines with adaptive font sizing) and the description as text overlays using the Aileron Bold font. The output is saved as `og/{slug}-og.png`.

You can override the text color:

```toml
[params.scripture]
ogColor = "#000000"  # default is "#ffffff"
```

You can override the background image per-page by adding an `og-bg*` page resource, or site-wide by placing your own `assets/og/base.png`.

The generated image is at least 1200px wide (the minimum for `summary_large_image` Twitter cards).

## Canonical URL Support

If a post was originally published elsewhere (say, a company blog), set `canonical` in front matter:

```toml
canonical = "https://blog.example.com/my-original-post"
```

This does two things:
- Sets `<link rel="canonical">` in the HTML head.
- Renders an "Originally published at" block with a link at the bottom of the article content.

## Fediverse and IndieWeb Integration

The theme supports several decentralized/IndieWeb standards through site params:

```toml
[params]
fediverseCreator = "@[email protected]"     # <meta name="fediverse:creator">
mastodonProfile  = "https://mastodon.social/@you"  # <link rel="me"> for verification

[params.webmention]
endpoint = "https://webmention.io/yoursite.com/webmention"
pingback = "https://webmention.io/yoursite.com/xmlrpc"
```

The `rel="me"` link enables Mastodon profile verification (the green checkmark). The webmention and pingback endpoints let your site participate in the IndieWeb reply/mention network.

### Syndication (POSSE)

[POSSE](https://indieweb.org/POSSE) is publishing on your own site and syndicating copies elsewhere. The theme supports it from both ends without prescribing how you post.

**Manifest.** Enable the `syndication` output format and the site publishes `/syndication.json`: every note, with its id, permalink, date, tags, and body as plain text.

```toml
[outputFormats.syndication]
  mediaType = 'application/json'
  baseName = 'syndication'
  isPlainText = true
  notAlternative = true

[outputs]
  home = ['html', 'rss', 'syndication']
```

The body is produced by the same partial as the site's own share links, so text posted by hand and text posted by a script are byte-identical. The `id` is the leading Denote timestamp of the filename (`20260416T114051`) when there is one, otherwise the permalink — it survives retitling, which a permalink does not, so it is safe as an idempotency key.

**Backlink.** Drop a `data/syndication.json` keyed by that same id, and each note renders its "also on" link in the discussion section:

```json
{
  "20260416T114051": {
    "mastodon_url": "https://mastodon.social/@you/115610844258140241",
    "bsky_url": "https://bsky.app/profile/you/post/3m6hkxbzqc72m"
  }
}
```

Per-page front matter (`mastodon_url`, `bsky_url`) still wins where present. The data file matters for content generated by a tool such as ox-hugo, where writing back into front matter would be overwritten on the next export.

## Umami Analytics

The theme has built-in support for [Umami](https://umami.is), a privacy-focused, cookie-free analytics tool. No Google Analytics, no tracking scripts from third parties.

```toml
[params.umami]
enabled   = true
websiteId = "your-website-id"
scripts   = [
  "https://your-umami-instance.com/script.js",
]
```

The `scripts` array accepts multiple URLs, which is useful if you self-host Umami and want fallback script paths. All scripts are loaded with `defer`.

## SEO and Structured Data

The `meta/standard.html` partial generates a full set of meta tags on every page:

- Open Graph (`og:title`, `og:description`, `og:image`, `og:locale`, `og:url`)
- Twitter Card (`twitter:title`, `twitter:description`, `twitter:image`, `summary_large_image`)
- Schema.org `itemprop` attributes
- `<link rel="alternate">` for translations and output formats (RSS, etc.)
- `robots: noindex,follow` for taxonomy and term listing pages (keeps tag indexes out of search engines)

For content pages in a `mainSections` or `shortformSections` section, `meta/post.html` adds:

- `og:type = article`, `og:url`, `article:published_time`, `article:modified_time`, `article:author`, `article:section`, and one `article:tag` per tag
- JSON-LD with headline, author, publisher, word count, dates, and image — typed `Article` for longform and `BlogPosting` for notes

## Custom CSS Override

The theme ships an empty `assets/css/custom.css`. To restyle the theme, create your own `assets/css/custom.css` in your Hugo site root. Since Hugo's asset lookup checks the site directory first, your file takes precedence.

For example, the sourcery.zone site turns the default warm off-white Tufte look into a dark theme with a few dozen lines of CSS custom properties:

```css
:root {
  --black: #000000;
  --yellow: #d0bc00;
  --mint: #6ae4b9;
  /* ... */
}

body {
  background-color: var(--black);
  color: var(--white);
}

a {
  color: var(--mint) !important;
}
```

Same goes for `assets/css/code-highlight.css` if you want different syntax highlighting colors, and `assets/og/base.png` if you want a different OG image background.

## Asset Pipeline

All CSS and JS go through Hugo Pipes:

- **Development** (`hugo server`): files are served unprocessed for fast reloads.
- **Production** (`hugo`): files are minified, fingerprinted, and served with [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) hashes.

JavaScript is built with `js.Build`, so if you extend `assets/js/main.js`, it goes through the same pipeline.

## Shortcodes

### `sidenote`

Tufte-style numbered margin note. See [Sidenotes Shortcode](#sidenotes-shortcode) above.

### `mark`

Wraps content in an HTML `<mark>` element (yellow highlight by default):

```markdown
{{< mark >}}This text is highlighted.{{< /mark >}}
```

Supports Markdown inside.

### `ytvideo`

Responsive YouTube embed:

```markdown
{{< ytvideo src="https://www.youtube.com/embed/VIDEO_ID" >}}
```

Renders a full-width iframe at 315px height inside a `<figure>` wrapper.

## Full Configuration Reference

Below is a complete `hugo.toml` showing all theme-specific params. Only `params.comments.enable` is required for the discussion feature; everything else has sensible defaults or is optional.

```toml
baseURL = 'https://example.com/'
languageCode = 'en-us'
title = 'My Site'
theme = 'hugo-scripture'

[params]
  description = "Site description for meta tags"
  mainSections = ['articles']          # which sections count as "articles" on the home page
  shortformSections = ['journal']       # which sections are notes (share text, feed autodiscovery)
  dateFormat = "2 Jan 2006"            # date display format (Go time format)
  gpgFingerprint = "YOUR_FINGERPRINT"
  keyoxideProfile = "https://keyoxide.org/..."
  fediverseCreator = "@[email protected]"
  mastodonProfile = "https://mastodon.social/@you"
  sitename = "My Site"                 # og:site_name value (defaults to the site title)

[params.author]
  name = "Your Name"                   # shown on home page subtitle, RSS, and meta tags
  email = "[email protected]"            # used in RSS managingEditor/webMaster

[params.webmention]
  endpoint = "https://webmention.io/example.com/webmention"
  pingback = "https://webmention.io/example.com/xmlrpc"

[params.scripture]
  ogColor = "#ffffff"                  # text color for auto-generated OG images

[params.share]
  summaryLength = 200                  # max chars in share text description

[params.comments]
  enable = true
  matrixUrl = "https://matrix.to/#/#your-room:matrix.org"
  ircUrl    = "https://web.libera.chat/#your-channel"
  showMatrix = true
  showIRC    = true

[params.umami]
  enabled   = true
  websiteId = "your-id"
  scripts   = ["https://analytics.example.com/script.js"]

[[menus.main]]
  name = 'Home'
  pageRef = '/'
  weight = 1

[[menus.main]]
  name = 'Articles'
  pageRef = '/articles'
  weight = 2
```

## Per-Page Front Matter

These front matter keys are consumed by the theme:

| Key | Type | Used by |
|-----|------|---------|
| `title` | string | Page title everywhere |
| `date` | datetime | Publication date |
| `tags` | list | Tag links, share link hashtags |
| `description` | string | Meta description, share text |
| `language` | string | `lang` attribute on content section |
| `canonical` | string | Canonical URL override + "Originally published at" block |
| `image` | string | OG/social image (also `featuredImage`, `cover`, `images`) |
| `imageAlt` | string | Alt text for OG image |
| `hideComments` | bool | Suppress discussion section on this page |
| `mastodon_url` | string | Per-post Mastodon discussion link |
| `bsky_url` | string | Per-post Bluesky discussion link |
| `hackernews_url` | string | Per-post Hacker News discussion link |
| `youtube_url` | string | Per-post YouTube discussion link |
| `category` | string | Used in `article:section` and `news_keywords` meta |
| `layout` | string | Set to `"bookmarks"` for the bookmarks layout |

### Backfeed: replies rendered statically

Drop a `data/replies.json` keyed by note id and each post renders the replies to its syndicated copy:

```json
{
  "20260416T114051": {
    "replies": [
      {
        "id": "117007900414073714",
        "url": "https://hachyderm.io/@someone/117007900371714227",
        "created_at": "2026-07-30T08:07:50.000Z",
        "author": {
          "name": "Someone",
          "acct": "[email protected]",
          "url": "https://hachyderm.io/@someone"
        },
        "text": "Plain text. Blank lines become paragraphs."
      }
    ]
  }
}
```

Everything is baked in at build time — no JavaScript, no third-party embed, and **no avatar images**: a remote avatar would leak every reader's IP to whichever instance hosts it. Reply text is printed through Go's escaper rather than `safeHTML`, so a stranger's markup cannot reach the page.

Each reply is marked up as an `h-cite` inside the post's `h-entry`, so the page is machine-readable as a conversation.

## Microformats2

Posts carry [microformats2](https://microformats.org/wiki/microformats2) markup, which is what lets webmention senders, feed readers, and fediverse bridges understand the page:

- `h-entry` on each post (and on each journal entry in a listing), with `e-content`, `dt-published`, `u-url`, and an inline `p-author h-card`
- A representative `h-card` on the home page, with `rel="me"` to the fediverse profile
- **No `p-name` on short-form entries.** [Post Type Discovery](https://indieweb.org/post-type-discovery) classifies an entry whose name is not a prefix of its content as an *article*, and consumers render articles as a title plus a link stub instead of showing the text. Notes are written to be read where they are found, so the title is displayed but not marked up. Articles do carry `p-name`.

The markup is emitted on hidden elements where it has no visible counterpart, rather than being hidden with CSS: parsers read the DOM, so a `hidden` attribute is still parsed while `display: none` is a rendering concern they never see.
Report a correction

Explore similar templates

Compare previews and descriptions within the same stack and category.