This repository holds the Automerge website, including all the docs, blog posts, and other site content, the templates that give pages their structure and styling, and a custom build system that weaves it all together.
We welcome all contributions. If you see any mistakes or glitches on the website, or have any trouble building the site locally, please file an issue or open a pull request. Thank you.
First:
- Clone the repo and run
npm install
(oryarn
, orpnpm
… whatever you use, it'll probably work).
Henceforth:
- Run
site
to build the website and spin up a live-reloading dev server. - Run
site help
to see other available commands.
If
site
doesn't work, use./site
instead (or add.
to your $PATH)
Here's the folder structure:
content
— html and markdown files for every page, plus a few lightweight assets; see pagesdemo
— source files for the interactive demo on the home pagefonts
— uncompressed source files for fonts used on the site; see fontspublic
— automatically generated when building the sitesystem
— the build system source code; see systemtemplate
— html files that define different types of page on the site; see templatesRedirects.txt
— Cool URIs don't change; see redirects
This site is powered by our own custom build system, which we can adapt and extend as our needs change.
The build system takes HTML and Markdown files in the content
folder, parses and transforms them with rules defined in system
, combines the result with various templates in template
, and finally saves the fully-assembled page to public
.
In short, the content
folder should feel like an FTP server. You put stuff in there, and it shows up on the website. The file path becomes the URL. There's more, of course — macros and templates and other behaviour that we'll get to in a second. But all that fanciness is opt-in. If you stick to plain HTML and simple assets, /content
feels just like FTP.
We're going to talk a lot about pages — so what's a page? It's an HTML or Markdown file that lives in the content
folder, with some frontmatter explaining what the page is and how it should be processed.
If you make an HTML file /content/pens/index.html
, the build script copies it over to /public/pens/index.html
, and it becomes a web page with the URL /pens/index.html
.
If you make a Markdown file /content/rams/index.md
, the build system will convert it to HTML, copy it to public
, and it'll end up with the URL /rams/index.html
.
Many web servers have a nice feature: if a page is named index.html
, you can omit that part of the URL. So the above pages can also be accessed at /pens
and /rams
. Those are clean URLs.
To make the most of these clean URLs, the build system has one trick: pages are renamed from name.html
to /name/index.html
, so the URL can be /name
.
Thus, a page with the URL /eraser/head
will be generated by exactly one of these files:
/content/eraser/head.md
/content/eraser/head.html
/content/eraser/head/index.md
/content/eraser/head/index.html
Use whichever file naming you like. It's fine to mix and match. Though for pages with static assets, it's recommended to use one of the latter two forms so you have a natural place to put those assets. And if there's ever a conflict (where two files would have the same URL), you'll see a helpful error message.
At the top of every page is the frontmatter section — properties that control how the page is processed by the build system. The frontmatter section begins and ends with ---
, and uses a key: value # comment
syntax that looks like YAML but is much simpler.
Here's an example of what frontmatter looks like:
---
title: Automerge
styles: /index/index.css, /index/demo.css
scripts: /index/dithering.js
template: landing
---
All the fanciness of the build system is opt-in. Frontmatter is the primary way for a page to opt-in to special processing.
- For an HTML file with no frontmatter, no processing is applied (including the aforementioned clean-URL renaming).
- All markdown files in
/content
get processing special whether they have frontmatter or not.
The most crucial piece of frontmatter is template
, which specifies what kind of page this is. The build system will inject the page's content into the matching template HTML file from /template
. See the Templates section for more.
Another important piece of frontmatter is publish
. It's true
by default, which means the page will be included when building the website. You can set it to draft
, which means the page won't be included on automerge.org, but will be included in local dev builds. Or you can set it to false
to completely exclude the page from all builds.
If a non-draft page links to a draft, you'll see a helpful warning.
For most CLI commands, you can specify --no-draft
if you don't want drafts to be included. But, for site build
, drafts are excluded by default, and you must use --draft
to include them.
If a page is given a date
, it'll be included in RSS feeds. The date must be in YYYY-MM-DD
format.
title
will be used for <title> and for RSS.description
will be used for OG and RSS. Can include inline markdown.image
for the rich preview whenever someone shares a link to the page.styles
andscripts
, which accept comma-separated lists of relative or absolute paths.clean: false
to skipname.html -> name/index.html
rewriting.index: false
— Page will not be included in RSS feeds or the sitemap.
The /template
folder contains a handful of HTML files that give pages their structure and styling.
Pages use the template
frontmatter to specify which of these template HTML files to use. If omitted, default
is used.
Templates can have frontmatter, too, though this is a more advanced topic that depends on the inner workings of the build system.
Pages & templates can contain macros, which look like: {{ rain-dogs }}
.
Macros mark locations that the build system should do something.
The most basic macro is {{ content }}
, which marks where pages will be injected into templates.
If there's no special behaviour defined for a macro, the default behaviour is to copy a value from the frontmatter. For instance, if the page frontmatter includes band: Zs
and the template has {{band}} are so damn good!
, the final page will have the text Zs are so damn good!
. Accurate.
If you see a macro and you aren't sure what it does, take a look at /system/macros.ts
— that's where all the macro expansion logic lives.
There's a folder of reusable snippets at /template/includes/
. You can add HTML or MD files here, and then include them in your pages with the {{include:___}}
macro.
For instance, the Automerge logo lives in /template/includes/logo.html
, and can be included like so: {{ include: logo }}
.
Git is fine for version control of text. It's awful for images and videos — it remembers ever version of every asset that's ever been committed, and they slow down clones, diff, and other actions. Before adding images, videos, and other non-text assets to the repo, please do the following:
- Resize — if an image is only going to display at 1000px wide (at most), it should be no more than 2000px wide, and ideally closer to 1200px. (A tiny bit of extra detail is often enough to give the illusion of a crisp 2x retina image).
- Format — line art and screenshots should be PNG or WebP, photos should be WebP or JPG.
- Optimize — Use ImageOptim or Squoosh or whatever other tools you have available. Try to keep big photos down around 100-300kb, and screenshots around 10kb-100kb.
In typical web development, you'd put a woff2
font file alongside your other assets, and then load it in your stylesheet. But fonts often contain a ton of wasted data — ligatures and alternates and glyphs for characters that are never used.
Instead, to add fonts to our website, you place ttf
or otf
files in the /fonts
folder. The build system can then:
- Scan every page of the site, and collect every unique character of text.
- Create a subset of data in the
ttf
/otf
files with just the characters we use. - Create optimized
woff2
files of those subset fonts, and save them to/content/static/fonts/
.
The build system will redo step 1 every time it runs. It will only redo steps 2 & 3 if it notices that the set of unique characters has changed.
So, when you add, remove, or change any of the fonts in the /fonts
folder, you need to force the build system to redo steps 2 & 3. To do that, just delete the /content/static/fonts/chars.txt
file.
This automatic font subsetting requires two non-NPM binary dependencies: harfbuzz and woff2.
- On Mac, you can
brew install harfbuzz woff2
- Not sure about Linux / Win — it is a goal to fully support these platforms, but that work hasn't been done yet.
- It's fine if these deps are missing. The build system will still run, but when the set of characters changes it will log a single warning and skip regenerating the fonts.
- Any characters that are missing will still be displayed on the site, they'll just appear in a fallback font.
- TODO / Wishlist: These deps can be added to the Github Actions flow, ensuring we always have the correct font subsets when the site is deployed.
In the Redirects.txt
file, you'll find a list of redirect rules. One rule per line, space-separated.
/source /destination
/old/busted /new/hotness
Bare text is a comment 👋
The redirect source MUST start with a slash. The destination can be any URL.
/secret https://feelingisreality.com
You can redirect other kinds of files, too, not just HTML/Markdown pages.
We don't control the server, so we can't do a 301 or 302. Instead, the build system
will hardlink the file at the new path to the old path, so that both paths will work.
/old/path.pdf /new/path.pdf
- Add a redirect whenever you move or rename an existing page or asset (ie: PDF) that someone might have bookmarked.
- You can also use a redirect to create an alias so that multiple URLs will point to the same thing — but don't go overboard.
- These redirects are excluded from the Sitemap.
The build system will automatically check internal links for validity. If you try to link to a page that doesn't exist, you'll see a warning in the terminal.
In the future, we'd like to add automatic checking for external links too, with some sort of assistance in adding a Wayback Machine fallback if links go dead. How best to do this is an open question — suggestions welcome!
The website includes the site
CLI tool.
You can run site help
to list all the available commands.
If
site
doesn't work, use./site
instead (or better yet, add.
to your $PATH)
site
is a little shell script at the root of the repo. It executes system/app.ts
using the fastest available Node-compatible runtime (bun, deno, or node).
app.ts
is the main entrypoint to the build system. If you want to learn how it works, start reading from there. It's all extensively commented, and a lot of care was taken to make it easy to understand and hack on.