Jank

Jank is the Bash script that powers this website. It requires Bash because it uses the “here string” (<<<) operator extensively. It uses jq and JSON for page metadata, and cmark to render Markdown files. Pandoc and other converters will probably also work.

Getting started

  1. Download the latest release: Jank v2.
  2. Extract the archive.
  3. Edit the website information in the $SITE variable in jank.bash.
  4. Use the example pages in src/ as a guide to add content to your new website.

A sample local server script (test-server.sh) is included. If you don’t have PHP installed, the script must be modified to use another local web server. Check out this page on MDN for other options.

Customization

  1. (optional) Edit the CSS file src/assets/main.css.
  2. (optional) Modify the templates in templates/ to suit your needs

Assets

By default, the contents of $src/assets/ are copied to the root of $dest/. So any CSS, JavaScript, or other files you put there will be copied verbatim. This means you can use any framework you want.

However, since you’re choosing to use a static site generator in the first place, you may want to use one of these CSS-only options:

You can find a lot more options (including CSS resets) at troxler/awesome-css-frameworks (no affiliation, just a good page of links).

Templates

The templates Jank uses are just plain Bash scripts located in templates/. They all take the same arguments in the same order: page data, content, and site data. Both page data and site data are JSON objects generated by Jank itself. The templates use jq to pull out whatever bits of information a given page requires. Content is either rendered Markdown or the output of a sub-template.

Every file in templates/ except main.bash is a sub-template. That means Jank will use it to render a page first, and then render main.bash around it. Let’s take a look at the default main.bash that ships with Jank. It starts off by pulling out information with jq

# main.bash
# This a template script is meant to be run by Jank.

# Need page JSON, rendered page content, and site JSON in that order.
page_data="$1"
page_content="$2"
site_data="$3"

page_title=$(jq -r ".title" <<< "$page_data")
page_description=$(jq -r ".description" <<< "$page_data")
page_keywords=$(jq -r ".keywords" <<< "$page_data")
page_url=$(jq ".url" <<< "$page_data")

site_name=$(jq -r ".name" <<< "$site_data")
site_menu=$(jq ".menu" <<< "$site_data")
site_description=$(jq ".description" <<< "$site_data")
site_keywords=$(jq ".keywords" <<< "$site_data")
site_footer=$(jq -r ".footer" <<< "$site_data")

Then it uses a bit of logic to decide what goes in the keywords and description meta tags.

# Use site description and keywords if they are not present.
if [ "$page_description" == "null" ]; then
  page_description="$site_description"
fi

if [ "$page_keywords" == "null" ]; then
  page_keywords="$site_keywords"
fi

Next comes a multi-line string with a ton of escaped double-quotes. I suppose you could clean this up a bit by changing all the internal double-quotes to single quotes.

# Build page head and metadata.
template="<!doctype html>
<html lang=\"en\">
  <head>
    <meta charset=\"UTF-8\">
    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
    <meta name=\"description\" content=$page_description>
    <meta name=\"keywords\" content=$page_keywords>
    <title>$site_name == $page_title</title>
    <link rel=\"stylesheet\" href=\"/main.css\">
  </head>
  <body>
    <div id=\"wrapper\">
      <header>
        <nav>
          <ul>
"

After that we have a loop that generates the main menu links.

# Generate page menu
site_menu_length=$(jq '. | length' <<< "$site_menu")
cur_index=0
while [ "$cur_index" -lt "$site_menu_length" ]; do
  current_page=$(jq ".[$cur_index]" <<< "$site_menu")

  url=$(jq ".url" <<< "$current_page")
  title=$(jq -r ".title" <<< "$current_page")

  template="$template
  <li><a href=$url>$title</a></li>"
  cur_index=$((cur_index + 1))
done

Finally, the template closes off the HTML document and uses echo to send the output back to Jank.

template="$template
          </ul>
        </nav>
      </header>
      <main>
        <h1>$page_title</h1>
        $page_content
      </main>
      <footer>
        <p>$site_footer</p>
      </footer>
    </div>
  </body>
</html>
"

echo "$template"

Because the templates are shell scripts, you can run any commands you want. Feel free to go nuts with Awk, Sed, Python, Ruby, whatever. As long as the final output is valid HTML, it should work.

Pages and the main menu

Pages in Jank are Markdown files that start with a chunk of JSON metadata like this:

 {
   "title": "Home",
   "menu": true,
   "date": "2026-08-23",
   "template": "home.bash"
 }
 +++

The line that starts with +++ marks the spot where the metadata stops and your content begins. The title and date key are required. Everything else is optional.

If the menu key is present and set to true, then the page is included in the $SITE.menu JSON array (use jq to access it). All pages are sorted from newest to oldest, so the value of the date key determines a page’s position in the main menu.

If the template key is present, its value must be set to the name of a file in templates. For example, if you put "template": "superpage.bash" in your page metadata, the file templates/superpage.bash must exist. Otherwise, the program will terminate with a message like this:

Found cmark
Found jq
Found src
Found public
Found main.bash
Template 'yay.bash' specified in 'src/your/page/here.md' DOES NOT EXIST!

The keywords and description keys are use to set the corresponding HTML meta tags for a page.

Blog posts

Jank was originally written with blogging in mind, so any page with blogPost set to true will be available in $SITE.blogPosts JSON array.

Jank does not yet have the ability to auto-generate individual tag or category pages, but that is next on the list of features to implement. Or you can fork and modify it yourself.