Uptoolkit
GitHub

Post Card

Renders an ActivityPub Note or Article with Schema.org SocialMediaPosting markup.

Bergen, Norway

Spent the week redrawing a 1:25000 coastline by hand. The shoreline has moved 40 metres inland since the 1984 survey.

Every map is a snapshot of a place that has already changed. #cartography #openstreetmap

Hand-drawn coastline contour map with annotated survey markers
Comparison overlay of the 1984 and current shoreline
#cartography#openstreetmap

Rahel Kebede boosted

What we learned running a mapping co-op for five years

Five years in, the hardest part was never the software. It was agreeing on what counts as done.

Full write-up on our site, including the governance docs we wish we had started with.

#cooperatives
Long typography rant, mild swearing

Installation

npx shadcn@latest add https://ui.uptoolkit.com/r/post-card.json

Usage

Renders any ActivityPub object as a social post: author identity, visibility, content warning, attachment grid, hashtags, and the reaction bar.

import { PostCard } from "@/components/ui/post-card";

<PostCard object={note} viewer={viewer} now={renderedAt} />

Boosts

Pass the wrapping activity. An Announce renders the "boosted" attribution above the post while the body stays attributed to the original author.

<PostCard object={getActivityObject(activity)} activity={activity} viewer={viewer} />

Structured data

Schema.org microdata is always in the markup — SocialMediaPosting on the root, with author, datePublished, articleBody, image, and keywords on the nodes inside. Add includeJsonLd to also emit a JSON-LD script, which is the form Google prefers:

<PostCard object={note} includeJsonLd />

Content rendering

Federated content is HTML from a server you do not control, so the default renderer converts it to plain text and linkifies hashtags and mentions from the object's tag array. To render HTML, pass your own renderer and sanitize it yourself:

<PostCard
  object={note}
  renderContent={(object) => (
    <div dangerouslySetInnerHTML={{ __html: sanitize(object.content ?? "") }} />
  )}
/>

Content warnings

When sensitive is set and summary is present, the body and attachments stay collapsed behind the warning until the reader expands it.

Composition

children render inside the footer under the reaction bar — the place for a comment thread. Pass hideActions for a read-only card, and a fixed now when rendering on the server so relative timestamps do not shift at hydration. Dates and counts format in en by default; pass locale — the same value on server and client — for anything else.