Porchsite · All guides · Ask for something

Making a theme (for developers)

# Making a Porchsite theme

A theme is the look of a Porchsite site and nothing else: logic-less
Mustache templates, a stylesheet, and a manifest that tells an assistant
what the theme needs. The person's assistant writes every word and picture;
the theme only arranges them. The full contract is
[TEMPLATES.md](../spec/TEMPLATES.md).

## Name it for what the site is for

`product-page`, `personal-homepage`, `small-business`, `event`. Not
`midnight` or `swiss`: an assistant asks its person "what's the site for?"
and picks from the answer. The same look in other colours is a palette,
not a new theme.

## The folder

```
themes/<name>/
├── template.json       # manifest: name, title, type, version, description, slots, art
├── sample.json         # generic sample content (no real person) for previews
├── home.mustache       # required pages
├── entry.mustache
├── archive.mustache
├── tag.mustache
├── not_found.mustache
├── partials/*.mustache # optional, used as {{> name}}
└── static/site.css     # required; other scripts and styles go here too
```

Start by copying the theme closest to yours.

## template.json

- `type`: what the site is for (`personal`, `product`, `business`, …).
- `description`: one or two sentences a person can choose by.
- `version`: bump it on every change. A published version never changes.
- `slots.images`: each picture the theme uses, as `{field, alt, size,
  need, purpose, alt_hint, prompt}`. `size` sets the shape and the minimum.
  `prompt` may use `{subject}`.
- `slots.content`: each field the theme reads and what it does with it
  ("`site.hero_caption` prints as the dateline").
- `art`: the brief a designer would give, so an assistant can make the
  pictures on its own: `ask` (what to settle with the person), `style`,
  `prompt_prefix`, and `steps`, including the checks to run before using
  any picture.

## The rules (theme:check enforces them)

- **No fixed words.** Every visible word comes from the view (`site.*`,
  `page.*`, `strings.*`, content). Words in a template are an error.
- **Triple braces only for `*_html`** values (Markdown the site rendered).
- **No inline scripts or event handlers.** Scripts load from `static/`.
- **No outside addresses** except Google Fonts.
- **Every `<img>` takes its `alt` from the view.**
- **Works at 320 px** wide, with text contrast of at least 4.5:1.

## Work on it

```bash
npm install
npm run theme:preview <name>   # render sample.json and open it in your browser
npm run theme:check <name>     # check the rules and render every page
npm test                       # the site's own tests, including the built-in themes
```

## Send it

Open a pull request with the theme folder. A maintainer runs the same
checks locally (every release runs them again), reviews it, and the next release publishes it to the catalog at
`https://get.porchsite.dev/themes.json`, with phone and desktop previews
made from your sample.json. Sites install themes only from the catalog.

## Credit

A theme is credited to its maker's X handle, and nothing else: no name,
email or link, anywhere (theme:check refuses `author`, `email`, `url` and
other author fields in template.json). Credit lives in
`themes/credits.json` (look name to handle), not in the theme, so it can
be added or removed without a new version.

To be credited, say your X handle in the pull request. The reviewer gives
you a one-time claim code; post `Porchsite look <name>: <code>` from that
account. Once the reviewer has seen the post, they add your line to
`themes/credits.json`, and the catalog entry and the theme's demo site say
"by @<handle>", linking to your X profile. Without the post, the theme is
published without credit.

To have credit removed, ask the curator through a theme request or at
https://porchsite.dev/ask, or contact the Porchsite author on X. The next
catalog build drops it.

To ask for a feature, a look or a fix without making it yourself, use the
form at https://porchsite.dev/ask. Porchsite's curator reads it; nothing sent
there is published, and the only contact it takes is an optional X handle.

The plain file