Sistine, the static site engine
Sistine is a simple, flexible, productive static site generator written entirely in Ink and built on Merlot’s Markdown engine. This demo site is, of course, generated by Sistine itself.
Features
Like all my side projects, Sistine is ultimately built for me to use and hack on for building my static websites. If there are idiosyncratic features, those appeal to my idiosyncrasies, and if there are missing features, they're probably features I don't need. Sistine is open source for the curious, but not necessarily open-roadmap. With that in mind...
Sistine tries to cover a lot of creative, expressive ground with a few well-chosen primitives. Among these are simple templating based on a single page type, rich control over page customization with page variables, and an extended Markdown syntax. It's not written in Ink for any particularly good reason, other than that I enjoy writing Ink programs, because I designed the language.
Simple templating
Unlike other static site generators that work with different types of pages like lists, index pages, and article pages, Sistine knows about exactly one type of page: the ... page.
A page has access to the site configuration and its own variables, as well as all the pages below it in the content hierarchy. Using these and the templating language, a page can render itself as any appropriate type of layout, from lists of posts by date to a multi-level hierarchy of topics.
Here's an abridged version of the Sistine template for the docs page on this site.
{{ -- head -- }}
<body>
{{ -- header -- }}
<article>
{{ if page.title }}<h1>{{ page.title }}</h1>{{ end }}
{{ page.content }}
</article>
{{ if page.pages }}
<div>
{{ each page.pages by order asc }}
<h2><a href="{{ path }}">{{ title }}</a></h2>
<p>{{ description }}</p>
{{ end }}
</div>
{{ end }}
{{ -- footer -- }}
{{ -- scripts -- }}
</body>
Here, you can see some of the features of Sistine templates:
{{ -- header -- }}
embeds a partial template atparts/header.html
into this place in the template.{{ if page.title }}...{{ end }}
lets us include the page title only if it's defined for the page in the content file.{{ each page.pages by order asc }}
loops through all posts in thepage.pages
variable (a list of posts under this page), in ascending order of theorder
page variable.
You can find the full list and documentation of Sistine's templating features in the templating documentation page.
Simple, transparent build process
Over time, all static site generators accumulate features that make the build process difficult to understand and "see through". By that, I mean that for many static site generators, we can't hold in our minds all the steps that happen conceptually when we run a build.
Since I was focused on simplicity and hackability (and because Ink is ... slow), I wanted to keep the build process conceptually light with a few, clear steps. This results in a static site generator that gets a lot done with very little complexity. When you run sistine build
, only five things happen in order.
- Copy over all the static files from
./static
- Read and parse the site configuration defined in
config.json
- Read and parse the "content pages" for the site under
./content
- For each content page...
- Use the page's path to find a template and render that page according to the template
- Write that page into a file in
./public
- Render the RSS feed from the
rss.xml
template
This makes Sistine-generated sites easy to debug, and templates easier to write.
Rich page customization with custom parameters
Each Sistine page template gets access to a rich set of default variables to render a page, including access to all of its children and parent pages. In addition, each page can easily define (through the Markdown front matter) its own set of variables to further customize a page.
For example, documentation pages on this site have fully customized breadcrumbs, implemented in a simple partial template rather than a separate plugin:
{{ if page.roots.1 }}
<div class="breadcrumbs">
{{ each page.roots }}
{{ if i is 0 }}
{{ else }}
<span class="breadcrumb-item">
<a href="{{ path }}">{{ title }}</a>
</span>
{{ end }}
{{ end }}
</div>
{{ end }}
Out of the box RSS feed support
I built Sistine primarily to replace other static site generators in my blogging. That means it needed good first-class support for generating site-wide RSS feeds. The rss.xml
template in ./tpl
gets handed a pages
list with all pages on the site, and this makes RSS feeds a first class citizen in Sistine projects.
Current progress
Sistine, like most of my side projects, is a work in progress. It's currently quite stable and featureful enough to build some of my blogs, but not my main website (which uses some custom Hugo features like date formatting and custom functions). Sistine is also currently not very fast, because performance was not a goal of the first release. In addition to performance work, some focuses of upcoming releases include
- Support for blog-specific data formats like reading time, word count, and date/time formatting
- Table of contents (and perhaps sitemap?) support
- Better error messages for mis-parsed and invalid templates
- Syntax highlighting on code blocks
- Support for more Markdown features, blocked on their support in the Merlot project
Given that it's currently quite slow and written in Ink, you probably shouldn't use it for anything important. But if you are interested, and want to ask questions about how it works or what's coming next, feel free to reach out on Twitter or file a GitHub issue on the repository.