← Home

Microformats2: Marking Up Posts for the IndieWeb

Microformats2 (MF2) is a vocabulary of CSS class names that embeds semantic meaning into existing HTML. No new elements, no JSON-LD in the head, no separate metadata file — the structure lives in the markup itself.

The key unit is h-entry: the class that marks a post. Everything else hangs off it.

The anatomy of an h-entry

A minimal article looks like this:

<article class="h-entry">
  <a href="https://adrian-altner.de/articles/..." class="u-url" hidden>Permalink</a>
  <h1 class="p-name">Article Title</h1>
  <time class="dt-published" datetime="2026-03-23T00:00:00.000Z">March 23, 2026</time>
  <div class="e-content">
    <!-- article body -->
  </div>
</article>

Each class has a specific meaning:

  • h-entry — the root; marks this element as a post
  • u-url — the canonical URL of the post
  • p-name — the post title (plain text)
  • dt-published — the publication date, read from datetime
  • e-content — the full post body, parsed as HTML

Authorship

The author is embedded as a nested h-card inside the h-entry. Since author information is the same on every page, it’s hidden from visual display but present for parsers:

<span class="p-author h-card" style="display:none">
  <img class="u-photo" src="https://adrian-altner.de/avatar.jpg" alt="Adrian Altner" />
  <span class="p-name">Adrian Altner</span>
  <a class="u-url" href="https://adrian-altner.de">adrian-altner.de</a>
</span>

Parsers read this as: the author of this entry is the person described by the nested h-card.

Notes and photos follow the same pattern

Notes use the same h-entry structure. Because notes don’t have long titles, p-name is the note’s first sentence and e-content contains the full body.

Photos wrap each image in an h-entry with u-photo on the <img> element itself:

<article class="h-entry">
  <img class="u-photo" src="..." alt="..." />
  <h1 class="p-name">Photo Title</h1>
  <time class="dt-published" datetime="2025-10-06T00:00:00.000Z">October 6, 2025</time>
</article>

A grid of photos becomes an h-feed — a collection of h-entries — by adding the class to the container:

<div class="h-feed">
  <div class="h-entry">...</div>
  <div class="h-entry">...</div>
</div>

When a post has been copied to another platform, u-syndication marks the external copy:

<a class="u-syndication" href="https://bsky.app/profile/...">Also on Bluesky</a>

This is stored in frontmatter and rendered conditionally:

syndication: z.array(z.string().url()).optional(),
{post.data.syndication?.map((url) => (
  <a class="u-syndication" href={url}>{new URL(url).hostname}</a>
))}

No visible change

None of this markup changes how the page looks. The classes are invisible to CSS unless you target them explicitly. The only additions are the hidden u-url permalink anchor and the hidden p-author h-card — both carry zero visual weight.

indiewebify.me validated the h-entry on the first pass. The parser found the author, title, date, and content without any issues.

← Home