Fairpage

Build in your editor. Hand over a site the owner can run.

fairpage-cli keeps a Fairpage site’s draft in a folder on your machine. Build with Claude Code or any editor, preview against the real renderer, push, and publish. The owner keeps editing the same draft in the browser.

Early access: for designers building sites with us

# log in once, in the browser
$ fairpage login

# the draft, as files; pick from your sites
$ fairpage clone
$ cd acme

# preview on localhost, reloads on save
$ fairpage dev --open

# send your changes to the draft
$ fairpage push

# review what goes live, then publish
$ fairpage publish

Install

fairpage-cli is one binary, fairpage, for macOS and Linux on Intel and ARM, and for Windows on Intel. There is nothing else to install: no Node, no build step.

# macOS and Linux
curl -fsSL https://fairpage.co/install.sh | sh

# Windows, in PowerShell
powershell -c "irm https://fairpage.co/install.ps1 | iex"
  • The script checks the download against its published checksum and puts fairpage in /usr/local/bin when it can write there, else ~/.local/bin; on Windows, in %LOCALAPPDATA%\Programs\fairpage. FAIRPAGE_PREFIX picks another folder.
  • fairpage update installs a newer release once its signature checks out, and fairpage dev says once a day when there is one. FAIRPAGE_VERSION=v0.1.0 with the script installs a given version.

Log in

You log in once per computer, to your Fairpage account. login shows a code and opens the approval page in your browser. Log in there if you have to, check that the code matches your terminal and choose Approve.

fairpage login
# Open https://fairpage.co/cli/approve and check the code: BCDF-GHJK
  • The login reaches every site you are a member of, with your role on each. fairpage sites lists them; clone without a name offers them, and takes the only one when you have one. fairpage new "Nord Bakery" starts a site you own, as “New site” on your sites page does.
  • fairpage invite <email>, in a site’s folder, mails an invitation to it. --role designer for someone who builds, --owner to hand the site to the client.
  • Codes last ten minutes. --url logs in to another Fairpage, such as --url=localhost:3000 for one on your machine.
  • Tokens are stored in ~/.config/fairpage/credentials.json, readable only by you. FAIRPAGE_TOKEN overrides it, for CI.
  • fairpage logout forgets the token on this machine. To cut it off everywhere, revoke it on your account page.

Day to day

You and the owner edit one shared draft. The live site changes only when someone publishes.

  1. Pull before you start. fairpage pull brings in what the owner changed in the editor.
  2. Work locally. fairpage dev serves the site on localhost:4321, rendered by Fairpage with your unpushed files laid over the draft. The page reloads when you save, and styleguide problems show as warnings.
  3. Push when a piece of work is done. A push is all or nothing, and it becomes one step in the site’s history, so the owner can undo it like their own changes.
  4. Publish when it’s ready. fairpage publish lists what differs from the live site and asks before it goes. --restore puts the previous version back.

Commands

Every command takes --json for scripts.

login

Log in to your account through the browser. --token stores a token you already have; --no-browser only prints the address.

logout

Forget this machine’s token for the app. The token works until you revoke it on your account page.

whoami

The account you are logged in as.

sites

Your sites, with your role on each and the editor’s address.

new <name>

Start a site you own and build as its designer. --slug chooses its address.

clone [site] [folder]

Download a site’s draft into a new folder, named after the site by default. Without a site, your only one, or one you pick from a list.

invite <email>

Mail an invitation to this folder’s site. --role is editor or designer; --owner hands the site over.

status

Local changes, draft changes, files changed on both sides, and conflicts waiting to be resolved.

pull

Take the draft’s changes. A file changed on both sides is kept aside in .fairpage/theirs/ and the command exits with 2.

resolve [path…]

Settle conflicts, keeping your file. --theirs keeps the draft’s version instead.

check

Check your changes as a push would and render every page, without writing anything. Exits with 1 on any problem.

push

Send local changes to the draft, all or nothing. --dry-run shows what would go.

publish

Show draft against live, ask, publish. -y skips the question; --restore swaps the previous publish back.

rows push <collection> <file>

Import rows into a collection from JSON or CSV, up to 5,000 at a time. Rows without a status are published.

dev

Preview on localhost with live reload. --port picks the port (4321 by default); --open opens the browser.

docs [words…]

The reference for building sites, from the app. --search finds paragraphs, --list names the sections. Needs no login.

A site’s files

A site is plain files. Paths that start with _, and components/, never answer a URL.

index.x.htmlThe home page, at /. about.x.html is /about; services/index.x.html is /services.
_layout.x.htmlWraps every page in its folder and renders <slot />.
components/card.x.htmlUsed as <card />; attributes become props.
_content/schema.yamlThe collections and their fields. A page reads one as content.posts.
blog/[posts].x.htmlOne page per row of the collection in the brackets, at /blog/<slug>, which it reads as item. The folder is any path, so addresses need not follow the collection's name.
_styleguide/theme.css with the tokens, guide.md with the rules in words, and example sections/.
_pages.yamlDescriptions, share images and redirects.
public/Served as is: favicon.svg, fonts, files.
404.x.htmlThe not-found page.

Markup and styles

Pages are HTML with expressions in braces and control flow as attributes. There is no <script> in a site’s pages.

<title>Journal</title>
<h1 class="text-4xl font-display text-ink">Journal</h1>

<article @each={content.posts as post} class="py-8 border-b border-line">
  <a href={"/posts/" + post.slug}>{post.title}</a>
  <p @if={post.summary}>{post.summary}</p>
</article>

Classes are Tailwind 4 utilities over the site’s own tokens, compiled by Fairpage. There is no default palette and no arbitrary values: p-[13px] and inline style properties are refused, which is what keeps the owner’s edits and the AI’s on brand. dev shows a refused class as a warning; push and publish stop on it.

With Claude Code

clone writes an AGENTS.md into the folder, unless one is there already. It covers the commands, the workflow, the file layout and how a site differs from the markup reference, so an agent can build and push without a briefing. For the rest, fairpage docs markup, docs utilities or docs --search <term> prints the reference from the server, current with it. Neither AGENTS.md nor CLAUDE.md is synced to the site.

cd acme
claude "Add a Journal: a posts collection, a list page and a page per post, in the styleguide's type scale."

Drafts, conflicts, limits

Conflicts

If the draft changed under you, a push is refused with the files in question: pull, resolve, push again. Nothing pushes while conflicts are waiting.

What only a designer or developer can change

The collection schema, the styleguide and, on a styleguide site, any CSS; Lua files only a developer. Editors change content, pages and sections within them. A schema change can add fields; to retire one mark it removed: true, to rename use was:. The content is migrated on push.

Publishing

Publish is refused while any page in the draft has errors, or when there is no index.x.html. Local changes you haven’t pushed are not published.

Limits

10 MBper file
100 MBper push
500 MBand 5,000 files per site
255characters per path, 16 folders deep
600requests a minute per token
5,000rows per import

Coming later

  • domain, to connect a custom domain.

Until then, connect one on the site’s Domains page.