Bascule is a static website generator, inspired by tools such as jekyll, jbake and Griffin. Content is written as a series of markdown files, which are scanned, combined with HTML templates written using handlebars, and output as a series of simple HTML files. These files are uploaded to a web server. There are no databases, no server-side processing, no complex configuration, just plain, SEO-friendly HTML.

The project is defined using a simple yaml configuration file, and each web page or post markdown file should be prefaced with a small block of yaml text (though bascule will try to make sense of a file which is missing this block). The yaml configuration file contains a few key values (such as sitename, author and theme) but you can mix in your own values to customise your project.

Individual pages or posts can be tagged with multiple tags, and bascule will generate listing pages for each tag used - such as all posts tagged 'bascule'.

Bascule builds a cache of your project, so only those markdown files which have been changed since the last generation are regenerated, saving generation time. It is always possible to trigger a full regeneration by adding the clean (-c) flag.

Bascule is also extensible - it is possible to configure the processing pipeline to, for instance, skip the tag listing pages mentioned above, or to write new generators, such as a sitemap.xml generator or a PDF generator.

User Guide

Bascule is generally used to create blog-type websites, such as this one, but it can be used to create any static website. At its core, individual web pages are modelled as Post objects, but bascule makes little distinction between posts or pages. The structure of your website is determined purely by the structure of the markdown source files and folders. Different types of post can be rendered using different template types.

For instance, all posts may have a date, an author, some meta data tags, and contain a link to the previous and next post. A page might only have a date, no tags, and no next/previous links. It's up to you. This guide uses the terms post and page pretty much interchangeably. There are some downsides to this approach which I'm still working on.

Project configuration

Details to follow, sample below:

siteName: Liam John Davison
dateFormat: "dd/MM/yyyy"
dateTimeFormat: HH:mm:ss dd/MM/yyyy
author: Liam Davison
theme: liamjd-theme
tagskey: tags
postsPerPage: 10

  source: sources
  output: site
  assets: assets
  templates: liamjd-theme/templates
    posts: posts
    pdf: pdf
    pdfTemplates: liamjd-theme/templates/fop
generators: [IndexPageGenerator, PostNavigationGenerator, TaxonomyNavigationGenerator, org.liamjd.bascule.extra.generators.pdf.SitePDFGenerator, org.liamjd.bascule.extra.generators.sitemap.SitemapXMLGenerator, org.liamjd.bascule.extra.generators.lunr.LunrJSIndexGenerator]
$debug: false

Markdown sources

Pages and posts are written in the markdown formatting language as implemented by the flexmark-common library. All pages and posts should have a short frontispiece written using yaml, defining some of the key metadata for the page. Bascule will attempt to render any page which is missing this frontispiece, but for the most reliable behaviour it should always be included. The frontispiece must be wrapped in three dashes (---). Here is an example frontispiece, taken from the markdown for this very page:

title: Bascule Static Site Generator
author: Liam Davison
layout: page
date: 23/12/2018
slug: bascule

The yaml defines a number of key metadata elements which bascule expects: the title, the author, the layout handlebars template uses to render the page, the date and a slug, which is used in generating the URL for the page. Most posts may also include a tags element, a bracked comma-seperated list. Bascule is very flexible, and you can add your own custom elements here too - Bascule simply adds any elements it doesn't recognise to the page model, so they are available for use in your handlebars templates.

TagRequiredMultiple values?Default value if missing
authornonofrom project yaml configuration, or empty
datenonofrom the markdown file created date
slugnonofrom the markdown file filename
(custom elements)noyesempty

Here's another example yaml frontispiece, from the pdf generation in bascule post:

title: PDF Generation from Bascule
author: Liam Davison
layout: post
date: 08/12/2018
tags: [software development, kotlin, bascule]
slug: pdf-generation-from-bascule

Bascule uses the flexmark library to parse and render HTML from Markdown. It is configured with a few extensions enabled. I'm considering options to make this more configurable or extendable, but I want at the very least a sensible set of defaults. The default extensions are:

  • AttributesExtension
  • YamlFrontMatterExtension (a core requirement for bascule)
  • TablesExtension (to allow simple templates like the yaml table above)
  • HydeExtension, a custom extension allowing embedding of HTML files into the markdown output; I've used it to create image galleries

In addition, the following parameters are set:

  • HtmlRenderer.GENERATE_HEADER_ID,true to give headings unique IDs
  • HtmlRenderer.RENDER_HEADER_ID,true to give headings unique IDs
  • HtmlRenderer.INDENT_SIZE,2 to indent the HTML output for readability

Handlebars templates

HTML layouts for posts and pages are prepared using the handlebars templating language, as implemented by the library. For each page or post, bascule generates a page model which contains all the meta-data elements from the project configuration file, the yaml frontispiece from each markdown source file, and a few calculated values. It's quite a comprehensive list. Custom generators can add to this model, as some of the default provided extensions like the TaxonomyNavigationGenerator do.

model objectvalue
sourceFileNamefrom the source markdown file name
urlcalculated URL, based on the folder structure and slug
titlepage or post title
authorpage or post author, or project author
layouthandlebars layout template name, such as post or list
datedate of page or post
tagslist of tags
slugslug which forms the basis of the URL
attributesany custom elements added to the yaml frontispiece
newerlink to a newer post than this one
olderlink to a older post than this one
contentThe main body of the page or post, as formatted HTML
rawContentThe markdown source for the entire page

For listing pages, i.e. pages which contain links to one or more links to other pages, the following additional model objects are defined:

model objectvalue
currentPagenumber of the current page in the list
totalPagestotal number of pages/posts which make up this list
isFirsttrue if this is the first page in the list, null otherwise
isLasttrue if this is the last page in the list, null otherwise
previousPagenumber of the previous page in the list
nextPagenumber of the next page in the list
nextIsLasttrue if the next page is the last
prevIsFirsttrue if the previous page is the first
totalPoststotal number of pages across the entire list
postsall of the pages/posts (can get quite big)
paginationa special object representing the 1,2,...9,10 pagination markers
tagthe current tag generated for this list, if the list comes from a tagging taxonomy
titleA page title for this list

See the pagination examples for more details.


Pagination is quite a tricky subject for Bascule to handle. Each blog post contains a link to its successor and predecessor posts, and these are easily used in the Handlebars templates like this:

<a href="/{{older.url}}" title="Older post: {{older.title}}">{{older.title}}</a><br/>

But what we really need is a way of generating a listing of posts and pages.


Extending Bascule

Sources on Github:

Core libraries used:

Bascule is written in Kotlin.