> One renderer for static Papyrus sites: settings in projection.json, a theme, brand assets, and a few named hooks. What it produces and how a site configures it.

Canonical: https://p.apyr.us/standard-front-end.html

# The standard Papyrus front end

A Papyrus static site is a `web/` folder, and Papyrus renders it. The renderer writes the pages, listings, tag pages, sitemap, `robots.txt`, a 404 page, RSS and Atom feeds, and every head tag that search engines and social cards read. A site supplies settings, a theme, brand assets, navigation and redirect data, and, only when settings are not enough, a few named hooks. It does not write its own projector, sitemap writer, feed generator or edge function.

We moved our own sites onto it so that a fix or a feature lands once and every site gets it. [Pilobol.us](https://pilobol.us) is the reference case, a publication with no sections at all.

## The smallest site

```json
{ "site": { "name": "Example" } }
```

That one setting gives a home page, one page for each published item, a paginated archive, tag pages when items have tags, `sitemap.xml`, `robots.txt`, `404.html`, `feed.xml` and `atom.xml`. Sections are something you add, not something you must have.

## projection.json

Every block is optional except the site's name, and unknown keys are an error, so a typo does not pass silently. The blocks are:

- **site**: name, tagline, description, language, production URL, author.
- **urls**: the page address style (`/x.html`, `/x/` or `/x`) and the sections, if the site has any.
- **nav and footer**: links, dropdown groups, columns.
- **seo**: share image, Twitter handles, the JSON-LD type for each kind of page, breadcrumbs.
- **sitemap and robots**: sitemap style, `lastmod`, extra routes, extra lines.
- **feeds**: RSS and Atom, how many entries, the paths.
- **listings and collections**: page size, which types are listed, tag pages, listings chosen by tag or section.
- **redirects**: exact redirects and anchored patterns, compiled into the edge function at deploy time.
- **forms**: public forms described as data.
- **showcase**: a logo wall.
- **agents and pageActions**: the agent-readable files and the page actions.
- **hooks**: the path of the site's hooks module.

## Theme and brand assets

The look is a stylesheet and a set of `--papyrus-*` tokens, plus the site's fonts, icons and images. Component CSS ships as a baseline and a site can turn it off.

## Hooks

When settings are not enough, a site names a hooks module. A hook is a small function with a documented name: it can replace the masthead or footer, change a card, add raw markup to the head, set the body class, or supply a page's body. Whatever a site does not hook, the standard renderer does. This site uses a hook for its page layout and nothing else.

## Feeds, sitemap and SEO

RSS and Atom are generated from the same entries as the listings, so a feed never disagrees with the site. The sitemap carries `lastmod`. Each page's head is written by one builder, in one order: icons, feed autodiscovery, sitemap link, robots, description, canonical URL, Open Graph, Twitter, article times and tags, and JSON-LD with breadcrumbs. A site that is not the production site gets `noindex` on every page and a `robots.txt` that disallows everything, and never sends analytics.

## Listings, tags and collections

Items without a section live at the root. Sections add listings at `/<base>/` with pagination, a dated grouping by week or month, and optional archives by month or year. Tag pages and collections are listings chosen by a tag or section selector. Link-only sections list cards that point elsewhere and stay out of the sitemap and feeds.

## Forms

A public form is data: its fields, a honeypot field, the texts, and an endpoint. With an endpoint, the form posts JSON and reports success only when the answer is a 2xx. With no endpoint it renders as a disabled form under a notice that it is not open yet, so nothing can be submitted and success is never faked.

## The Built with Papyrus wall

A site can have a logo wall fed by published items. Each record is a name, a one-line description, a logo, a link and a kind. Adding a file adds a tile, with no code change. Ours is the [Built with Papyrus](/built-with/) page. The wall falls back to the site's name when it has no logo.

## Page blocks, islands and the family footer

Marketing pages need full-width sections, columns and calls to action, and some need interactive widgets. Three parts of the standard front end cover that, and blocks and islands are in use on [cyclotron.anth.us](https://cyclotron.anth.us), [chattic.us](https://chattic.us) and [tactus.anth.us](https://tactus.anth.us). The family footer is on Cyclotron and Tactus, and Tactus also has grouped navigation.

**Page blocks.** In a site whose content is a directory of Markdown files, a line on its own can start a block: `::band` is a full-width section (with a tone, a size and an eyebrow line), `::column` starts the next column, `::button` is a link styled as a button, and `::island` is a mount point. They are opt-in, since a file with no `::band` line is converted as before, and a bad attribute is a build error that names the file. The twin of a page with blocks is its Markus source: blocks stay as written, and an island carries a one-line summary of what it shows. Blocks do not work through the browser editor, which still rejects them. A `{{name}}` placeholder in a page is filled from a `values` object in `projection.json`, and a backslash before it prints the placeholder itself.

**Islands.** An island is a mount point for code the site owns, such as a chart or a carousel. Core finds the mount points and loads the site's script only on pages that have one. The script registers a function for each island name, and a missing or failing island logs a warning and never stops the page or the other islands. `islands.prerender` names a file of server-rendered HTML for named islands, so a page has its content before the script runs.

**Header and footer.** The brand can be a logo and a word instead of the site name, and navigation links can be grouped into a dropdown that works without a script. `footer.family` renders the Anthus family footer: the site's own columns, the "Made by Anthus AI Solutions" band listing our live sites, and the community links. `urls.style` can also be `clean`, which addresses a page as `/x` rather than `/x.html`; [Cyclotron](https://cyclotron.anth.us/use-cases/support-call-quality-review) uses it.

**Smaller options.** These are in Papyrus 5.19.0, the release this site is built with:

- `site.dateFormat` sets how dates print, as a `strftime` pattern; the default is ISO dates.
- `site.ledeField` names a front matter field whose text is printed as the article's lede under its title.
- `seo.isBasedOnField` names a field holding the URL of the original a page is based on, and it appears as `isBasedOn` in the page's JSON-LD. Cyclotron uses it.
- `seo.articleTypes` lists entry types that are not listed but still report `og:type` article and Article JSON-LD.
- `urls.sections.<name>.groupBy` accepts `field:<name>`, which groups a listing by a front matter field, with entries that lack it last under "Other". The other values are `week` and `month`.
- `pageActions.types` gives Copy Markdown and View as Markdown to further entry types, such as guides and standing pages. Cyclotron, Chattic and Tactus use it.

## Agent-readable output

The standard front end also publishes a Markdown twin of every page, `llms.txt`, `llms-full.txt`, Markdown by `Accept` header, and page actions on articles and guides. That has [its own page](/agent-accessible.html), including what does not work yet.

## Running it

Papyrus is open source and the front end is part of it, so the [documentation for building a static site](https://github.com/AnthusAI/Papyrus/blob/main/docs/building-a-papyrus-static-site.md) is in the repository with the code. This site, p.apyr.us, is built the same way: its content is Markdown files, its settings are a `projection.json`, and its layout is one hooks module.
