Slab is a programmable markup language to generate HTML.
This project is still very young. Features demonstrated on this page work as shown but they don't always generalize as expected.
Introduction
Slab is an alternative syntax for writing HTML, plus some programming language features (often found in templating languages, like conditionals and loops). slab
is a command-line tool to process .slab
files and render them into HTML.
Slab revolves around the concept of fragments. Fragments are pieces of HTML that work 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 example above 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 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 short-hand notation to represent 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 the one 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 on the resulting HTML pretty-printed. In a production setting, we would instead generate the following:
<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 and use and the .
character for the 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 on their own. In that 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 writing them in parenthesis, directly after the element name.
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 text block on the floowing 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. The body of the fragment is made of the following indented lines. To call a fragment, its name is used like a regular element.
frag card
h1 Card
content
card
frag content
p A paragraph.
<h1>
Card
</h1>
<p>
A paragraph.
</p>
In the above example, the fragment card
is defined. In its body, it itself calls another fragment, content
. That fragment is passed as an argument when card
called. Arguments are given by defining fragments with the corresponding name, indented below the call.
Implicit content
argument
To make a call to a fragment look more similar to regular HTML elements, indented lines below the call are passed as an argument called content
when an expicit fragment with the same name is not defined. 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-fragments) arguments using curly braces directly after its name, mentioning parameters names. Similarly, when calling a fragments, (non-fragments) arguments are passed between curly braces.
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 of its lightweight syntax equivalent to HTML, Slab supports some constructs usually found in programming languages.
Variable declarations
Variables are declared using the let
keyword. A variable (or a more general expression) can be used as the content of an element by following the element name directly with a =
character.
let a = 1
let b = "Alice"
p= a
p= b
<p>
1
</p>
<p>
Alice
</p>
A current limitation is that each declaration requires its own let
line.
Expressions
Simple values (booleans, integers and string literals) are supported alongside simple operations: addition, substraction, multiplication, and division for integers, and comparison for all types.
let a = 1
p= a + 2 * 3
<p>
7
</p>
Strings can be concatenated with the +
operator. Integers can be converted to strings using 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 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.
Assuming a file with the following content:
[
{
"username": "Alice",
"email": "alice@example.com"
},
{
"username": "Bob",
"email": "bob@example.com"
}
]
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 in multiple files in two ways: include statements and imports.
Includes
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 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 example code on this page is 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
Imports
An import makes it possible to call a Slab file as if it was a fragment. Its body is the content of the file. As with fragments, it is possible to pass fragments as arguments.
Assuming a file called aux-imported.slab
with the following content:
div
default first
div
default second
It can be called as a fragment:
import aux-imported.slab
frag first
h1 A title
frag second
p A paragraph.
<div>
<h1>
A title
</h1>
</div>
<div>
<p>
A paragraph.
</p>
</div>
Open source
Slab is an open source project released under the terms of the 2-Clause BSD license. The source code and issue tracker are available on GitHub.
The Haskell package is available on Hackage.