Slab reference
This reference guide provides a comprehensive overview of Slab's features and syntax.
Note: Slab is still a young project. The features demonstrated here work as shown, but they may not always generalize as expected.
Before reading this reference guide, you may want to start with the tutorial first.
Introduction
Slab is an alternative syntax for writing HTML, plus some programming language features (often found in templating languages, such as conditionals and loops). The slab
command-line tool processes .slab
files and renders them into HTML.
Slab revolves around the concept of fragments. Fragments are pieces of HTML that act like functions. You can define a fragment and then call it:
frag page
doctype html
body
content
page
h1 A title
p A paragraph.
The above example defines a fragment named page
. It then calls page
, passing a block of HTML (containing a heading and a paragraph) as an argument (implicitly called content
).
The slab render --pretty
command can be used to transform the above code into the following HTML:
<!DOCTYPE HTML>
<body>
<h1>
A title
</h1>
<p>
A paragraph.
</p>
</body>
When it is not pretty-printed, it looks like this instead:
<!DOCTYPE HTML>
<body><h1>A title</h1><p>A paragraph.</p></body>
Basic syntax
Elements
At its core, Slab is a shorthand notation for representing HTML. An HTML element is written as its tag name, followed by text content, or by other elements. The tree structure is captured by indenting child elements instead of using closing tags. This syntax is identical to that used in Pug, which inspired Slab.
ul
li Item
li Item
li Item
<ul>
<li>
Item
</li>
<li>
Item
</li>
<li>
Item
</li>
</ul>
Note: We show the resulting HTML pretty-printed. In a production environment, we would generate the following instead:
<ul><li>Item</li><li>Item</li><li>Item</li></ul>
IDs and classes
To assign an ID to an element, the regular attribute notation can be used (see below). Slab also supports tacking the ID directly to the element name with a # character.
h1#title A title
<h1 id="title">
A title
</h1>
Classes work similarly, using the .
character for short notation instead.
h1.hero A title
p.lede.muted Catching summary.
<h1 class="hero">
A title
</h1>
<p class="lede muted">
Catching summary.
</p>
An ID or a class can also be used by itself. In this case, a div
element is assumed.
.playground
#app
<div class="playground">
<div id="app">
</div>
</div>
Attributes
Attributes (including IDs and classes) can be assigned to an element using a syntax similar to HTML itself, by placing them in parentheses, directly after the element name. To add multiple attributes, use a comma to separate them.
form(action='/login', method='POST')
<form action="/login" method="POST">
</form>
Comments
Comments are introduced with --
or ---
, either with the comment itself on the same line, or as an indented block of text on subsequent lines.
-- A regular comment.
--- A comment in the generated HTML.
---
Another comment
in the generated
HTML.
--
Another
regular
comment.
<!-- A comment in the generated HTML. -->
<!-- Another comment
in the generated
HTML. -->
Fragments
Fragments are blocks of HTML that can be named and reused.
Basics
To define a new fragment, the frag
keyword is used, followed by the name of the fragment, and optionally by its formal parameters (in curly braces). The body of the fragment consists of the following indented lines. To call a fragment, its name is used as a regular element and its actual arguments are introduced with the with
keyword.
frag card{para}
h1 Card
para
card
with para
p A paragraph.
<h1>
Card
</h1>
<p>
A paragraph.
</p>
In the example above, the fragment card
is defined. In its body, it itself calls another fragment, para
. This fragment is passed as an argument when card
is called. Arguments are indented below the call.
Implicit content
argument
To make a call to a fragment look more like a regular HTML elements, indented lines below the call are passed as an argument called content
. I.e. the following example is equivalent to the example above:
frag card
h1 Card
content
card
p A paragraph.
<h1>
Card
</h1>
<p>
A paragraph.
</p>
Default arguments
Fragments can define default contents for their arguments (making them optional) by using the default
keyword before calling an argument.
frag card
h1 Card
default content
p Default paragraph.
card
p Another paragraph.
card
<h1>
Card
</h1>
<p>
Another paragraph.
</p>
<h1>
Card
</h1>
<p>
Default paragraph.
</p>
Expression arguments
Fragments can also take non-fragment arguments. The expressions that can be used in Slab are documented in the Expression language section.
A fragment is defined to take (non-fragment) arguments by using parentheses directly after its name, mentioning parameter names. Similarly, when calling a fragment, (non-fragment) arguments are passed between parentheses.
frag hello(name)
.greeting
p Hello, #(name).
content
hello('Alice')
p Some content.
<div class="greeting">
<p>
Hello, Alice.
</p>
<p>
Some content.
</p>
</div>
Expression language
In addition to its lightweight syntax equivalent to HTML, Slab supports some constructs commonly found in programming languages.
Variable declarations
Variables are declared with the let
keyword. A variable (or a more general expression) can be used as the content of an element. This is done by immediately following the element name with a =
character.
let a = 1
b = "Alice"
p= a
p= b
<p>
1
</p>
<p>
Alice
</p>
Expressions
Simple values (booleans, integers and string literals) are supported, as well as simple operations: addition, subtraction, multiplication, and division for integers, and comparison for all types.
let a = 1
p= a + 2 * 3
<p>
7
</p>
Strings can be concatenated using the +
operator. Integers can be converted to strings with the show
function. Strings can be "converted" to booleans using the null
function.
let a = 1
p= show (a + 2) + '.'
<p>
3.
</p>
String interpolation
Expressions can be used within the text content of an element using the #(...)
syntax.
let name = "Bob"
p Hello, #(name).
<p>
Hello, Bob.
</p>
JSON support
Static JSON files can be loaded by using their relative path. This makes it easy to store and template data outside of a Slab file.
Assume a file with the following JSON content:
[
{
"username": "Alice",
"email": "alice@example.com"
},
{
"username": "Bob",
"email": "bob@example.com"
}
]
Here is an example of how to format the JSON data:
let values = ../../data/values.json
ul
for value in values
li= value['username']
<ul>
<li>
Alice
</li>
<li>
Bob
</li>
</ul>
Control flow
Slab supports conditionals and loops using if
and for
blocks.
Conditionals
The else
branch is optional.
if true
p A.
else
p B.
<p>
A.
</p>
Loops
It is possible to iterate on lists and objects. In the following example, the index and the key are optional.
ul
for val, index in [1, 2]
li val: #(val), index: #(index)
ul
for val, key in {1: 'one', 2: 'two'}
li val: #(val), key: #(key)
<ul>
<li>
val: 1, index: 0
</li>
<li>
val: 2, index: 1
</li>
</ul>
<ul>
<li>
val: one, key: 1
</li>
<li>
val: two, key: 2
</li>
</ul>
Modularity
Slab code can be split into multiple files by using include statements.
An include works by inlining the content of the included file in the including file. When the included file has a .slab
extension, it is interpreted as if its code was present in the including file. In particular, any toplevel code that is not let
expressions or fragment definitions will render as-if directly written in the including code, and definitions can be used in the including file.
The include syntax allows you to specify a filter. Currently, the only supported filter is :escape-html
. It can be used when you want to show HTML code and also to show Slab code (i.e. disabling its interpretation). This is how the code examples on this page are shown.
include basic.slab
<ul>
<li>
Item
</li>
<li>
Item
</li>
<li>
Item
</li>
</ul>
include:escape-html basic.slab
ul
li Item
li Item
li Item