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 postu-url— the canonical URL of the postp-name— the post title (plain text)dt-published— the publication date, read fromdatetimee-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>
Syndication links
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.