A simple static site generator based on nunjucks, LESS and markdown.
We wanted a static site generator that uses the same frontend stack as Apostrophe. We also wanted strong support for building accordion-style navigation, breadcrumb trails, "next" buttons and the like easily.
There is a sample site provided in the "test" folder:
cd test
node ../app.js
This creates a _site
folder and copies and converts your files like this:
.md
files are compiled with markdown and converted to.html
as described below. They must include metadata, at least enough to specify the title as shown below.main.less
files are compiled with LESS and converted to.css
as described below.- Files and folders starting with
.
or_
are ignored. - Everything else is simply copied.
By default each markdown file is fed to the nunjucks template at _layouts/default.html
. The HTML generated by markdown is passed as the content
variable.
Your markdown file must begin with a metadata section, like this:
---
title: "This is the title of my page"
---
Any metadata you provide is passed to the nunjucks layout template that renders your page.
Metadata is parsed as YAML, so you may pass as much data as you like. All of your metadata becomes available to Nunjucks templates, when rendering that page or any other that references it as a child, ancestor, etc. Note that Nunjucks auto-escaping is in effect, so use | safe
in your layouts if you are intentionally passing markup.
Just use metadata:
---
title: "This is the title of my page"
layout: alternate
---
This will use the nunjucks template _layouts/alternate.html
instead.
Yes, you may use the nunjucks extends
keyword to extend another layout, override blocks and all that cool stuff.
To make relative links between pages just use the standard Markdown syntax. Keep in mind each file will have a .html
extension.
If your site is going to be a standalone website living at root, do whatever you like with URLs that start with /
. The --server
convenience option pairs well with this.
If you need to build a site that can be moved around from folder to folder within a larger site, preface "absolute" links in your layouts with {{ root }}
. This ensures that you can copy your _site
folder into github pages or another environment where it will be in a subdirectory and not at the actual root of the site. If you know your content will be at the root you may ignore this.
If you wish, habit
can provide you with all the information you need to build navigation links such as breadcrumb trails, lists of child pages, and "previous" and "next" links.
To take advantage, you must:
- Ensure that every folder has an
index.md
page. This is considered the home page for that folder. - Include a
children
property in the metadata of that page. That property should be a list of the filenames of the child pages, without the file extension, in the order you want them displayed.
Confused? Here's a simple example of a page with children:
---
title: "Advanced"
children:
- flossing
- mortgaging
- dancing
---
## Advanced life skills
Here you'll learn how to really rock your life.
That page would be called index.md
, and the same folder would contain flossing.md
, mortgaging.md
, and dancing.md
.
Once you do this, the following additional variables are visible in your page templates:
children
is an array of objects with information about the child pages, with title
and url
properties, in addition to other metadata.
parent
is an object with information about the parent of the current page. For flossing.md
, this would be index.md
. For index.md
, it would be the index.md
file of the parent folder.
ancestors
is an array of objects with information about all of the ancestors of the current page plus itself. ancestors[0]
is the home page (index.md
in the root directory of your proejct). The last entry in ancestors
is the current page.
previous
is an object with information about the previous page with the same parent, if any.
next
is an object with information about the next page with the same parent, if any.
Note that all of these objects have complete information. You may loop over ancestors
in your nunjucks template and then loop over the children
of each ancestor to create accordion-style navigation.
There is a complete example in the test
folder. Just check out test/layouts/default.html
to see how all of these variables are used.
If there is a file called main.less
, it is compiled with LESS, and the result is stored as main.css
. Yes, you may @import
more .less
files from your main.less
file. You may have multiple main.less
files in separate folders, if you are really on fire to have separate LESS compilation for them for some reason.
You may also use plain old .css
files, which are simply copied, like anything else that doesn't start with _
.
Need static assets like fonts and images? No problem; they are automatically copied, because they are not .less
or .md
files.
Install habit
globally:
npm install -g habit
Now you can just cd
to a folder containing the appropriate content and type:
habit
To rebuild the _site
folder.
But for testing, this is more convenient:
habit --server
This will rebuild your _site
folder, then launch a little webserver ready for you to check out your pages at http://localhost:3000
by default. You may change that with the ADDRESS
and PORT
environment variables.
Up to you, but here's a little shell script we use:
#!/bin/bash
habit &&
rsync --delete -a _site/ user@example-server.com:/var/www/mysite &&
echo "Done!"
0.4.1: clarify that --server
does rebuild your site first. No code changes.
0.4.0:
- New
--server
convenience option, which listens athttp://localhost:3000
by default. You may change that with theADDRESS
andPORT
environment variables. - Updated dependencies, notably
less
andnunjucks
. - nunjucks now auto-escapes the data you pass in via YAML. If you are passing something that should be used directly as markup, use the
| safe
nunjucks filter in your layout. You do not need to use| escape
or| e
. - ignore a top-level
node_modules
folder. This makes sense because it allows a site to be built with habit as a dependency rather than using habit globally. Thanks to Alex Gleason.
0.3.4: added a process.exit(0)
call to address a situation where habit
does not otherwise terminate in node 0.12.7 on a Mac when installed globally. Despite chasing this with process._getActiveHandles
and process._getActiveRequests
I still don't know why.
¯\_(ツ)_/¯
0.3.3: filename is reported when a nunjucks error occurs.
0.3.2: filename is reported when a markdown error occurs.
0.3.1: linked to github repository. No code changes.
0.3.0: linking to headings within a page is much nicer. Headings now contain anchor elements with the anchor
class, as seen on github. The name
of each heading is hyphenated, like a CSS class name. This automatically does the right thing whether the original name was "a sentence with spaces", "aCamelCaseFunctionName", or "an_underscored_function_name". If a heading contains (
, that character and everything after it is ignored when constructing the name, allowing you to easily link to documentation for a function that includes the parameters in the header. However, if two headings would otherwise have the same name
, 2
is appended to the second one, 3
to the third one and so on.
0.2.2: just documentation fixes.
0.2.1: The "next" and "previous" properties now allow you to traverse the entire site depth-first. That is, you can read through the entire "book" by continuing to click "next."
0.2.0: metadata and navigation-building data.
0.1.6: fixed @import
in LESS.
0.1.3, 0.1.4, 0.1.5: renamed habit
.
0.1.2: global install works.
0.1.1: introduced the policy of ignoring dotfiles and _
files, filtering markdown and LESS, and just copying everything else. This makes much more sense than a long list of special cases. This change renames the layouts
folder to _layouts
so they won't be copied.