A Reference to Fabricate's Page Templates

This page has an informal explanation of the parts that make up a Fabricate page, using inputs and outputs paired together for demonstration purposes.

A formal definition of the grammar is included in the documentation for the site.fabricate.prototype.read.grammar namespace.

Basic Expressions

An expression starts and ends with the asterisk and END emojis: ✳🔚. You can then use some optional control characters to change how that expression will be used by Fabricate. All expressions in those emoji will be evaluated after Fabricate reads the page template and parses it.

One thing that makes Fabricate different is the fact that templates can appear anywhere in the text. So if you wanted to write a sentence generated by a Clojure program in the middle of an ordinary paragraph, you can do that. If you want to define a variable naming all the sections and write a sentence like "this page has 3 sections," you can do that. Fabricate, by default, uses inline expressions instead of Markdown.

✳ - Run expressions

If you don't specify any additional characters, Fabricate evaluates the expression without putting the result in the page.

Useful for: namespace declarations, variable definitions, and other side effects - anything where you don't need to see the result of what you run.

Examples
✳(ns my-demo-ns (:require [site.fabricate.prototype.page :as page]))🔚
✳(def metadata {:title "My page"})🔚


✳= - Yield expressions

If you add the "=" character after the asterisk - ✳=, the expression's results get added into the page. Hiccup expressions, like [:code "example inline code"] are included in the document tree and will be transformed into HTML all at once after the document is evaluated.

Useful for: anything you'd use HTML for.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
No such var: page/expr->hiccup
Error phase
:compile-syntax-check
Location
Lines 54-64, Columns 1-13
Source expression
 (list [:h5 {:class "l-text"} "Examples"]
         [:div
          (apply list
                 (for [expr yield-exprs]
                   [:div {:class "example-rows"}
                    [:div {:class "example-row"}
                     [:pre (first grammar/delimiters) "="
                      [:code (page/expr->hiccup expr)] (last grammar/delimiters)]]
                    [:div {:class "example-row"} expr]]))]

         )


✳+Insert expressions

Use this for when you want to evaluate some code and insert the expression into the result, but not its output.

Useful for: when you want to demonstrate some code that contributes to the page but its output isn't important. You could show the code used to define a variable, but use the the contents of that variable later on in the page. It's also useful for instructive function definitions, where the return value of a function definition is just a var.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
No such var: page/expr->hiccup
Error phase
:compile-syntax-check
Location
Lines 88-100, Columns 1-17
Source expression
(list [:h5 "Examples"]
(apply conj [:div {:class "small"}]
       (for [expr insert-exprs]
         [:div {:class "example-rows"}

          [:pre
           {:class "example-row"}
           [:code (first grammar/delimiters) "+" (page/expr->hiccup expr)
            (last grammar/delimiters)]]
          [:pre
           {:class "example-row"}
           [:code (page/expr->hiccup expr)]]
          ])))


✳+=Composing control characters

You can use both the "+" and "=" characters together to display a form along with its output.

Useful for: showing off your art alongside the code that produced it.

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
No such var: page/expr->hiccup
Error phase
:compile-syntax-check
Location
Lines 138-150, Columns 1-17
Source expression
(list [:h5 "Examples"]
(apply conj [:div]
       (for [expr insert-display-exprs]
         [:div {:class "example-rows"}

          [:pre
           {:class "example-row"}
[:code (first grammar/delimiters) "+="
(page/expr->hiccup expr) (last grammar/delimiters)]]
          [:div
           {:class "example-row"}
           (eval expr)]
          ])))


Interlude: (code) documentation as data

Not only do these examples illustrate how Fabricate's control characters work, they also show how you can build up components of a page by defining them as variables in Clojure and then using them to produce page elements. I was able to use Flexbox attributes to tailor the layout to my examples without tying the whole page to a single way of displaying things. The example expressions above were defined not as ossified strings in a fenced code block, but as quoted Clojure expressions, some of which were evaluated to produce this page. If they encountered an error on evaluation, an error would have surfaced in the page as I was drafting it. Can you be assured of that when putting example code in Markdown documents?

Extended form expressions

Sometimes it's preferable to group multiple forms together along with paragraphs of plain text into a single section. In order to do this without embedding many paragraphs in a single large Fabricate expression, Fabricate also supports extended forms. They allow you to mark the start and end points of an element in a document and continue working within that element normally using Fabricate's mix of plain text and expressions.

Form Definition

✳// [:section {:class "chapter"}

Text in a "chapter" section. Embedded expressions can be included as well, like ✳=[:em "emphasized text."] 🔚

]//🔚


Results


Text in a "chapter" section. Embedded expressions can be included as well, like emphasized text.

A longer example document: this page

This page itself uses many of the "useful for" tips I mentioned in the first section. You might find the source code of the page instructive, so it is included below:

Error
Error type
class clojure.lang.Compiler$CompilerException
Error message
No such var: page/expr->hiccup
Error phase
:compile-syntax-check
Location
Lines 181-200, Columns 1-28
Source expression
[:pre {:class "small"}
 
 [:code (->> 
         "pages/reference/template-structure.html.fab"
         slurp
         (#(read/parse %
                       {:filename "pages/reference/template-structure.html.fab"}))
         (clojure.walk/postwalk
          (fn [i] (if (read/fabricate-expr? i)
                    (let [[start end] grammar/delimiters
                          ctrl-char
                          (cond (and (contains? i :expr) (:display i)) "+="
                                (:display i) "+"
                                (contains? i :expr) "="
                                (contains? i :exec) "")]
                      [:span start ctrl-char
                       (page/expr->hiccup (or (:expr i) (:exec i)))
                       end])
                    i)))
         (apply list)) ]]