A template for small teams to deploy landing pages and small websites cheap and fast, engage non-technical contributors, and architect information over time.
The developer experience is most of the time terrible with content management systems that ship WYSIWYG editors, and learning HTML and managing metadata is too much for the vast majority of non-technical users. I aim for the sweet spot where efficient contribution is possible both for developers and non-technical users.
This repository aims at making it easier to use Jekyll to:
It regroups the setup and best practices I have identified. While reusable as such, it is perhaps better to think of it as a library of examples than as a template.
Jekyll is a static website generator.
This means it transforms pieces of content in Markdown or HTML into full HTML files that can be displayed in web browsers.
It is flexible and easy to use. Other static website generators exist. However, one of the main benefits of Jekyll is that it can be run and hosted for free directly by code hosting platforms such as GitHub or GitLab through their Pages feature, and you can always switch to your own server at any time.
This means your website can be made available on the internet at no cost.
Jekyll claims to be “blog-aware”. It is indeed, supporting notions such as posts, publishing dates and authors out of the box. More than “aware”, though, the default boilerplate and some of the documentation will guide you towards blog posts.
Jekyll can do much more than that and, over the years, I have come to build and reuse a set of practices and presets that support my main use case: building and deploying landing pages cheap and fast (under half a day) that enable non-technical users to contribute content through online text editors, and maintain an ability to scale to an entire website with full quality control.
This approach has enabled me to build products such as:
It is standard practice to ensure that development and production environments are in sync, in order to ensure bugs can be caught before production, or at least reproduced in development. Most of the time, this is achieved by locking down versions in development so that production updates accordingly.
However, when relying on hosting providers such as GitHub Pages, the host manages production versions. In this case, the only way to ensure both environments are in sync is to… enforce production versions in dev!
The Gemfile
in this repository is dynamic and fetches GitHub’s production versions. A fallback mechanism supports offline work and assumes local versions are correct —you won’t be pushing work if you’re offline anyway 🙂
Credit goes to @MaukoQuiroga for most of the dynamic Gemfile code.
The Ruby version itself is locked with the .ruby-version
file, and bundler
will complain if it does not match the production version. I suggest to install rbenv
to manage and automatically switch the Ruby version.
The alternative to rbenv is RVM. If you have it already installed on your system, or you prefer to use it for any reason, there is no need to switch to rbenv.
In CI, the .circleci/config.yml
in this repository builds using the same versions and options as GitHub Pages does, such as “safe mode” which disables most plugins, and enforces additional checks such as HTML spec compliance.
The Jekyll documentation provides a template for CircleCI, but it assumes a deployment to Amazon S3 where you control production versions.
If you need to keep building the website without technical interventions, such as for example if some collaborators edit content without having the ability to change the code and there is no more technical support available long term, a failing CI can become an issue.
In that case, you could disable CI entirely. However, this means you would lose content validation, which would still bring value to non-tech collaborators. Hence, the recommended step is to disable version check in CI, leaving it to technical collaborators to ensure synchronisation when they intervene on the code.
In order to lock down dependencies and maximise the time without which a technical intervention is necessary, you should lock down all dependencies versions, remove runtime version checks in CI, and switch to building in CI rather than relying on GitHub Pages updates, as these updates can be incompatible with your specific developments. The specific way of locking down dependencies is all documented in comments starting with “Maintenance mode instructions”.
Some of the lesser-known yet very powerful features of Jekyll are collections and includes. The default Jekyll template, by staying minimal, does not help with making those discoverable.
This is why this template showcases and prefills the _includes
folder and defines a collection in _config.yml
with both an explicit output and default values that demonstrate the power of collections.
Jekyll is most often used in a way where the information architecture (i.e. content hierarchy) reflects the folder hierarchy of content files. While a definitely easier way to get started, this has two major drawbacks:
Gemfile
. This makes it very confusing for non-technical users.This is why this template suggests to only store content in collections. This will help you think about content hierarchy, make sure all your content is in scoped folders, enable the use of default values for layout and metadata for each folder, and let you teach non-technical contributors that they can change anything they want as long as they work in some given folder (which they can, for example, bookmark in their browser).
The only folder that is exposed directly is assets
, in which files that should be processed as a pass-through are to be stored.
Jekyll does support themes, so it is tempting to think you can create content and later on make it beautiful. While this can work for well-known forms of content such as blog posts, this is usually not a satisfying approach when you are building custom websites.
If you manage your own styles with Jekyll, you will manipulate layouts and CSS. Since the content is static, rather than including stylesheets on a page-by-page basis, many websites end up including all their stylesheets on all pages (or creating a single one with Sass), decreasing web performance.
This is why this template supports three ways to ease category-by-category styling:
body
of each page with the name of the current layout as soon as it is not default
. For example, if you define layout: tree
on a page, then its body will have class .layout-tree
so that you can easily target elements within.layout: tree
on a page, then it will load the stylesheet at assets/css/tree.css
. If you don’t need this feature, the best is to simply remove the corresponding lines in the default layout.additional_css
front-matter property loads all stylesheets listed there, so that any page or collection can load arbitrary files.I personally prefer to use standard CSS over Sass, but that choice is obviously up to you and Jekyll does support Sass / SCSS as preprocessors.
Most Jekyll themes don’t provide the meta tags for “social cards” that will enable rich content preview on major social media.
The _includes/metadata.html
in this repository ships the minimal amount of code to enable rich preview on Facebook, Twitter, LinkedIn and any other OpenGraph-compatible scraper.
GitHub Pages provides free and fast deployment, but relying on GitHub-provided Jekyll compilation adds limitations to how much you can customise the process.
The _scripts/deploy.sh
script in this repository, combined with the pre-filled CI config file, enables CI compilation and deployment of your Jekyll website as a pure HTML dump on GitHub Pages, allowing you to benefit from GitHub’s hosting without relying on their more constrained Jekyll engine.
Since your files are stored in Git, and they will be exposed directly, it is a very good idea for the performance of both your public website and production workflow to compress assets before they are committed to the repository. In particular, I strongly recommend using ImageOptim or an equivalent service to compress images before committing them.
Creating a new file is significantly harder than editing existing ones in the online editor interface. In order to make it easier for non-technical users to create new content, we can create special links that embed a template file, by using query string variables.
For example, the GitHub online editor will use filename
and value
as URL-encoded values for the file name and file content, so we can craft URLs that will pre-fill a new file, complete with YAML front-matter:
https://github.com/${username}/${repo_name}/new/${branch}/${containing_folder_path}?filename=${new_file_name_with_extension}&value=${url_encoded_template}
I usually store these links in the CONTRIBUTING
file.
This will ease the contribution of both non-technical users, who can navigate the published website rather than the file hierarchy, and of external contributors.
There, the link is much easier to craft by relying on Jekyll-provided variables:
https://github.com/{{ site.repository }}/edit/${target_branch}/{{ page.path }}
_config.yml
to ensure the metadata matches your setup.logo.png
with your own.bundle exec jekyll serve --watch --safe --strict_front_matter
watch
improves your developer experience: the server will reload automatically when it detects changes to files (except the config file, you’ll get bitten by that at some point).safe
mirrors GitHub Pages' setup.strict_front_matter
allows to catch errors early.While the server will systematically suggest you start it with the
--incremental
option, I recommend to only use this option as an opt-in, when you are iterating quickly over a specific set of pages in a large website. When designing, this is a recipe for getting inconsistent pages where some have updated their links and included assets and others have not, leading to lots of frustration.
In order to avoid mixing content files with config & dev files, pages are never created at the root: they all get written in collections. In order to add a page to the menu, you can simply add it to _toplevel
, as long as you set its URL with permalink
. Prefix their filename with the index at which you'd like them to appear in the menu. The first one (by convention, index 0
) will appear on the left handside of the menu, along with your website logo.
If you use CircleCI, just log in with GitHub and activate builds for your repository. They offer a free plan for open-source.
MIT: do whatever you want as long as you don’t sue me.