1. Overview
  2. Usage
  3. Do I have to use layouts?
  4. How do I customize the styling of my layouts?
  5. The Render function
    1. Render an entire file
    2. Rendering specific headlines and content
    3. Render a Logbook SVG Polyline
    4. Rendering a Table of Contents
  6. Data and Functions available to layouts
  7. Styling Layouts


When you create a new Firn site for the first time, it will create a folder:_firn/layouts. The layout folder stores Clojure files (.clj) that are responsible for how your org-file is rendered to-html; The act of "applying" a template means to pass the content of the org-file through a specific clojure hiccup template that organizes how the content is displayed.

When an org-mode file is processed by Firn it looks for the in-buffer-setting#+FIRN_LAYOUT: to see if it uses a Layout. If none are specified, the default template is used.


Let's walk through an example of how to use layouts.

Create a sample org file; we will call it Edit it to include the following:

NOTE: Because the snippet below is org-mode code, you will need to remove the \ preceding the * Headlines.

#+TITLE: Layout
#+DATE_CREATED: <2020-03-24 Tue>
#+DATE_UPDATED: <2020-03-24 14:20>
#+FILE_UNDER: docs

\* My project

 This is my project.

\* Notes

 - [ ] Figure out how layouts work.

Now we have a file that is going to look for a layout called "docs" in the layouts folder. Return to the terminal.

cd layouts
touch docs.clj

Inside docs.clj place the following clojure code.

(defn project
  [{:keys [render title partials]}]
  (let [{:keys [head nav]} partials]
        [:div (render "Notes")]]]]))) ;; Renders The notes section.

NOTE: Layouts can have multiple Clojure functions in them, however only the last function of the file will be applied as the layout.

Do I have to use layouts?FAQ

Not exactly. When you create a new Firn site, it will have a default.clj file in the layouts directory. This provides a very basic out of the box formatting and will render the entirety of your org mode file. If you want to add any kinds of customization - even css, you'll need to work with layouts.

How do I customize the styling of my layouts?FAQ

As with normal HTML, you'll need to add a head tag with a link that references to a css file. It might make sense for you to create a partial for defining your html head tag, allowing it to be shared across layouts:

(defn head
    [:link {:rel "stylesheet" :href "/static/css/firn_base.css"}]]

The Render function

Render is used to render content from your org file. It's an important one - if you don't use it, you won't see any org-mode content.

Quite a bit of data and functions are made available in your layouts. The render function you to decide what parts of your org-file you would like to display. Firn makes some "pre-made" content available for rendering - logbook graphs, table of contents, etc. It can render multiple kinds of content"

Render typeParamatersExample
Entire filekeyword(render :file)
Specific headlinestring(render "Notes")
Specific contentstring, keyword(render "Notes" :content)
Table of contentskeyword, map(render :toc {:depth int :exclusive? bool :headline "Notes"})
Logbook Polylinekeyword, map(render :logbook-polyline {:stroke "#45698A" :height 60 :width 365})

Render an entire file

(defn default
  [{:keys [render partials]}]
  (let [{:keys [head]} partials]

       [:div (render :file)]])))

Rendering specific headlines and content

(defn default
  [{:keys [render partials]}]
  (let [{:keys [head]} partials]
       [:div (render "Notes")] ; render everything in "Notes" (including the heading "Notes")
       [:div (render "Notes")] ; render the content in "Notes", excluding the heading.

Render a Logbook SVG Polyline

Graphs all logbook entries for the current file in a polyline, generating a chart for each year.

(defn default
  [{:keys [render partials]}]
  (let [{:keys [head]} partials]
       [:div (render :logbook-polyline {:stroke "#45698A" :height 60 :width 365})]])))

Rendering a Table of Contents

When a file is processed, Firn collects all of it's headlines, whether you're choosing to render the entire file, or just one headline.

There are several ways you can create table of contents in your files, from simple to more complex use cases.

  1. Render a table of contents for an entire file.

      (defn default
        [{:keys [render partials]}]
        (let [{:keys [head]} partials]
             [:div (render :toc)]
             [:div (render :file)]])))
  2. Render a table of contents for everything within a specific headline.

      (defn default
        [{:keys [render partials]}]
        (let [{:keys [head]} partials]
             ;; only renders a table of contents for a single headline's children.
             ;; `:eclusive?` means we don't render "Notes"; just headlines that fall under it dflakdjflksadjf lksadjf lkasdjf lkasdjf .
             [:div (render :toc {:headline "Notes"
                                 :depth 4
                                 :exclusive? true})]
             [:div (render "Notes")]])))
  3. From within a file, you can set the keyword #+FIRN_TOC and pass it a map with the properties above to enable a table of contents specific to a single file:

      #+FIRN_TOC: {:depth 4}

Data and Functions available to layoutsreference

The following keys are made available in your layouts.

configThe site wide
date-createdThe #+DATE_CREATED value of the filestring
date-updatedThe #+DATE_UPDATED value of the filestring
fileThe file as a data
file-linksA list of links per filelist
firn-underThe #+FIRN_UNDER value of the filestring
logbookA list of logbooks entries per file.list
logbook-totalThe sum of all the logbook entries per filestring
metaA map of metadata per file (logbook, links, etc)map
partialsa list of invokable partials /partials dirlist
renderEnables rendering parts or entirety of an org file.function
site-linksA list of all links across all documentsvector
site-logsA list of aLL logbook entries.vector
site-mapA list of all files on the wikivector
titleThe #+TITLE value of the file.string

This may seem like a lot of information to make available to a layout template. And that's because it is. But thanks to destructuring in Clojure, you can make your templates only ask for what they need:

(defn project
  [{:keys [ render partials logbook] :as data}] ; < destructuring to make available only what you need.
  (let [{:keys [head nav]} partials]
       (page-header data)
        (main-section render)
        (sidebar render logbook)]]])))

The above template only needs access to render, partials and the logbook. Then functions, such as page-header can simply take the data map and destructure what it needs again:

(defn page-header
  [{:keys [title logbook-total date-updated date-created firn-under]}]
  (let [rndr (fn [i s]
               (when i [:span.flex.pr2
                        [:h4.italic.bold.pr1 s " "]
                        [:h4.italic.thin i]]))]
     [:h1.page_meta_title title]
      (rndr date-created "Published: ")
      (rndr date-updated "Last Updated: ")
      (rndr firn-under "File Under: ")
      (when-not (= logbook-total "0:00")
        (rndr logbook-total "Time Logged: "))]]))

Styling Layoutsstyling

You can write css as you normally would by placing css files in the_firn/static/css folder and then having firn move them into your _site folder when run. Styling is applied through hiccup.

There are some internal styles that are applied when org-mode text is transformed into data. These styles are all prefaced with the keyword firn-, (ie.firn-headline-text or firn-tag).

Inspect the file _firn/static/css/firn_base.css to see the configurable elements.