Template

We call template to almost any file in the input folder. Templates are transformed, and combined, to create the website.

View Helpers

In templates, you can usually write Elixir, which allows you to extend your site with abstractions to generate CSS or make API calls. Still comes with some functions out of the box to help you build your website. We call these view helpers.

Including other files or templates

Use the include/3 function to import the contents of another file. For instance, the following template includes the file _includes/head.slime:

html
  head
    = include "_includes/head.slime"
  body
    = children

Behind the scenes, Still will render head.slime to HTML and replace the function call with its contents.

Linking to other files

The link/2 function creates HTML hyperlinks. It takes care of specifying the rel and target when necessary, and it supports both full URLs or paths relative to the input folder. You should always use the link function to create hyperlinks between files, otherwise deploys to subpaths will not work. For instance:

= link "Home", to: "/"
= link "Blog", to: "https://example.org"

Including images

Image support is still (get it?) rudimentary: it can only generate images of different sizes. By default, it relies on ImageMagick, but if you install still_imageflow as a dependency it will use imageflow. The reason we do not use imageflow by default is that it requires Rust. If you want to use ImageMagick, you also need to have it installed in your system.

To insert an image use the function responsive_image/2. The first argument is the image's path and the second a keyword list. To transform the image we only care about the :sizes key, everything else will be rendered as an attribute on the generated img tag.

The :sizes key is optional and by default we take the image's width to generate 3 smaller images. If you specify an array of integers, it will use that instead.

A responsive image can be generated like this:

<%= responsive_image("example.jpg") %>
= responsive_image("cover.jpg", class: "cover")

and it will include the proper src and srcset attributes to use the different images at the right time.

Custom helpers

You can call any module from the templates (Enum.reverse(list) or MyHelpers.func()). However, if you want to write custom view helpers, you can add them to the configuration and their functions will be imported to the templates using import Your.Module.

config :still,
  view_helpers: [Your.Module]

Configuration

Most template files allow for a YAML Front Matter block to change their configuration.

If you add your own template language, you can support this by ensuring the file's preprocessor pipeline includes Still.Preprocessor.Frontmatter.

Setting the permalink option allows you to override the output path of a file. For instance, by default blog/post_1/index.md would generate blog/post_1/index.html, but you can change it to blog/announcment.html like this:

---
permalink: `blog/announcment.html`
---

# Post 1 - Announcement

Layouts

Layouts are templates that wrap other content. To use a layout, set the layout key in front matter. For instance:

---
layout: _layout.slime
---

h1 About Page

This will look for a _layout.slime in your input folder.

The layout file must render the @children variable:

doctype html
html
  body
    = @children

Notice that _layout.slime starts with an underscore. This is necessary since we don't want to compile the layout file by itself. Any file starting with an underscore isn't compiled to the output, but can still be imported by other files.

More Examples

You can use any templating language in your layout - it doesn't need to match the language of the content being wrapped. For instance, we can have a markdown file with a JavaScript wrapper:

---
permalink: index.js
layout: _console_layout.js
---

# This is an h1

And in _console_layout.js we have to render the children:

console.log("<%= @chidlren %>");

Layout Chaining

Any file can include a layout, even files that work as layouts. For instance, you can have a blog post that uses a layout:

---
layout: _post_layout.slime
title: My blog post
---

This is a blog post.

And that layout can use another layout:

---
layout: _layout.slime
---

h1 = @title
main = @children

Which may look something like this:

html
  body
    div = @children

This allows you to cascade down and combine any templating language.

Collections

Collections allow you to group file using a tags property:

---
tags:
  - post
title: A blog post
---

This file will be assigned to the collection post. Every file that specifies the post tag will be listed in the post collection. You can then iterate over this list using the get_collections function:

ul
  = Enum.map get_collections("post"), fn x ->
    li
      = link x[:title], to: x[:permalink]

Collections are automatically updated when the files change. Files that use a collection will also be compiled whenever a file in a collection changes.

Files that do not generate an output file, such as file that start with an underscore like _layout.slime, are never added to collections. We are still considering whether we want to keep this behavior or not.