Layouts and Partials

Last updated:
2020-11-04 20:12


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 (or what might be called "Front Matter")#+FIRN_LAYOUT: to see if it uses a Layout. If none are specified, the default template is used.

A simple layout file

Let's consider a simple layout file:

(defn default                   ; define a clojure func
  [{:keys [build-url title render partials]}] ; destructured commonly used functions and data
  (let [{:keys [head]} partials]              ; Ignore for now, partials will be explained later
    [:html                      ; => opens a `html` tag
     (head build-url)           ; => Renders the `head` partial
     [:body                     ; => opens a `body` tag
      [:main                    ; => Ditto
       [:article.def-wrapper    ; => opens a `article` html tag with a class of `def-wrapper`
        [:div.def-content       ; => see above
         [:h1 title]            ; => renders an `h1` tag with the file title.
         (render :file) ;       ; => `render` is where the magic happens!
         ]]]]]))                ; => all these closing brackets can be thought of as closing html tags (for now)

The above may look a little strange if you are not familiar with Clojure. The next document will expand on what the render function does.

Your layout files can have several functions in them, but the last function in the file is the one that is loaded as your layout.


In the example above you may have noticed the partials binding appear. Partials are similar to layouts: they are clojure functions that render html. Partials are used for rendering particularly common pieces of html that might be shared across several layouts (such as the head of your HTML document as in the example above).

Using partials requires the following:

  1. Create a partial in the partials directory.

    • A partial must be a clojure function, just the same as a layout file.

    • The last function in your partial file is the partial that is applied.

  2. partials are a map of all the partial functions and is passed into layouts when they are rendered:

        (defn default
          [{:keys [partials]}] ; `partials` is a a map.
  3. You can optionally bind the partials you need in a let block so that they are easily callable as functions. The name of your partial is the name of the partial file. For example, the docs you are reading has a partials/head.clj file, and is accessible like so:

        (defn default                   ; define a clojure func
          [{:keys [partials]}]          ; destructure the `partials` key.
          (let [{:keys [head]} partials]
              [:html                      ; => opens a `html` tag
              (head build-url)            ; => Renders the `head` partial
              ; ....

The user layout mapadvanced

The following is a description of the map that gets passed to all user layouts. As such, all of these keys may be destructured as mentioned above. Some values may be removed / added.

{;; Layout stuff --
 :render        (partial render {:file file :config config})
 :partials      (config :partials)
 ;; Site-side stuff --
 :site-map      (config :site-map)
 :site-links    (config :site-links)
 :site-logs     (config :site-logs)
 :site-title    (cfg/prop config :site-title)
 :site-author   (cfg/prop config :site-author)
 :site-desc     (cfg/prop config :site-desc)
 :site-url      site-url
 :org-tags      (config :org-tags)
 :firn-tags     (config :firn-tags)
 :build-url     (build-url site-url)
 :config        config
 ;; File wide meta --
 :file          file
 :meta          (file :meta)
 :logbook       (-> file :meta :logbook)
 :file-links    (-> file :meta :links)      ;; TODO - possible re/move this too?
 :title         (-> file :meta :title)
 :firn-under    (-> file :meta :firn-under) ;; TODO this should be removed; should be handled by the render function...?
 :logbook-total (-> file :meta :logbook-total)
 :date-updated  (-> file :meta :date-updated)
 :date-created  (-> file :meta :date-created)}