slweb(1) - Simple static website generator
Version 0.9.8, 28 Apr 2024
slweb
[-h | --help]
slweb [-v | --version]
slweb [-b | --body-only] [-d | --basedir directory]
[-p | --global-link-prefix URL] [filename]
slweb Copyright © 2020-2024 Strahinya Radich.
This program is licensed under GNU GPL v3 or later. See the file
LICENSE
in the slweb repository for details.
slweb is a static website generator which aims at being simplistic. It transforms custom Markdown-like syntax into HTML.
-b
--body-only
Only add the contents of the <body>
tag, skipping <html>
and
<head>
.
-d directory
--basedir directory
Set the base directory for includes ({include}
and {incdir}
) and
favicons. Defaults to the current directory (“.”).
-h
--help
Print the usage information screen.
-p URL
--global-link-prefix URL
Set URL as a global prefix for relative links.
-v
--version
Print program version and exit.
Files processed by slweb are using a minimal subset of Markdown with added directives.
Backticks.
Text inside `backticks` will be put inside <code></code>
. Text
surrounded by triple backticks (```) on the lines by themselves will be
put inside <pre></pre>
. Any “less-than” characters inside
backticks will be converted to <
and “ampersand” characters
to &
. Any text following the first triple backticks
until the end of the line will be ignored. This is to account for language
highlighting specification, which isn't yet supported.
Blockquotes.
Starting the line with >
will surround it with a <blockquote>
tag.
Multiple lines are supported.
Bold/italic.
Text inside *asterisks* or _underlines_ will be put inside
<em></em>
. Text inside **double asterisks** or __double
underlines__ will be put inside <strong></strong>
. ***More than
two*** of the ___same symbol___ should be avoided.
Break marks. To address combinations of lists and “raw” tags, which are problematic to mix together, slweb supports explicitly marking a raw tag with a “break mark”. This currently means that when such a tag is introduced, any running list item and list will be closed prior to outputting the custom tag. Tags with break marks are introduced by prepending the exclamation mark to the tag name. For example, without a break mark, the input:
- Some list
{break-here}
produces the following output:
<ul><li><p>Some list
<break-here></p></li></ul>
When changed to:
- Some list
{!break-here}
slweb will output:
<ul><li><p>Some list
</p></li></ul>
<break-here>
Same for end tags:
{dl}{dt}{p}Introduction{/dt}
{dd}
- One
- Two
{/dd}{/dl}
produces:
<dl><dt><p>Introduction</dt>
<dd>
<ul><li><p>One
</p></li>
<li><p>Two
</dd></dl></p></li></ul>
while
{dl}{dt}{p}Introduction{/dt}
{dd}
- One
- Two
{/!dd}{/dl}
produces:
<dl><dt><p>Introduction</dt>
<dd>
<ul><li><p>One
</p></li>
<li><p>Two
</p></li></ul>
</dd></dl>
Footnotes (regular and inline). Two-part regular footnotes can be added in a manner similar to links (see Links). Inline footnotes are also supported.
Regular footnotes
have two mandatory parts: first, the footnote mark is represented by
[^
footnoteid
]
, where
footnoteid
is the footnote identifier. Second, the text of the footnote is
represented by
[^
footnoteid
]
: sometext
,
with
sometext
being the text of the footnote. Footnote text construct needs to begin at
the start of a line, and it is the most practical to have it placed near
the bottom of the file, similar to normal links.
Combined, the input:
This is a text[^first].
[^first]: With a footnote!
gives (some lines are wrapped for this manual):
<p>This is a text<a href="#footnote-1" id="footnote-text-1">
<sup>1</sup></a>.</p>
<hr>
<p id="footnote-1"><a href="#footnote-text-1">1.</a>
With a footnote!</p>
Inline footnotes
can be added by using the construct ^[footnotetext]
. For example, input
Inline footnote^[Footnote text] in a paragraph.
will produce
Inline footnote<a href="#inline-footnote-1"
id="inline-footnote-text-1"><sup>1</sup></a> in a
paragraph.
<hr>
<p id="inline-footnote-1"><a href="#inline-footnote-text-1">
1.</a> Footnote text</p>
The horizontal rule and the list of footnotes is output only once, before the
end of
HTML
document body. Rule and the list of footnotes can be surrounded by a div with
the “footnotes
” id by setting the
YAML
variable
add-footnote-div
to “1” (see
add-footnote-div).
Inline and regular footnotes can be used at the same time, but they don't share the numbering. This doesn't affect functionality, but it does affect presentation, as some footnote numbers will overlap. As a result, whenever slweb encounters both footnote types in the same document, a warning will be issued to stderr.
Headings.
A line starting with #
followed by space will be put inside
<h?></h?>
, where ?
stands for 1-4, depending on the number of
hash signs. Tags will have id
attributes set to heading-?
, with
?
set to the number representing its position within the list of all
headings.
Horizontal rules.
Three dashes at the start of the line will produce a <hr>
in the output.
As this Markdown feature clashes with
YAML
block boundaries, it will work only if
YAML
block is present in the input and before the three dashes for the horizontal
rule. Any text following the three dashes on the same line will be ignored.
Alternatively, three asterisks (***
) can be used instead of dashes,
avoiding the necessity for a
YAML
block preceding the asterisks.
Images. Similar to links (see Links), images can be added by using:

Which will produce:
<figure>
<a href="/path/to/image.png" title="Some title" class="image"
target="_blank"><img src="/path/to/image.png" alt="Some title"
width="100" height="50" ></a><figcaption>Some title</figcaption>
</figure>
The attributes width
and height
will be determined automatically
using the command
identify(1)
(if present), provided the src
attribute contains a valid file path. (Also
see
image-file-prefix).
As with links, a form similar to the “regular form” of links can also be used, using the image id instead of the direct URL.
If the additional link or <figure>
/<figcaption>
tags are not
desirable, they can be turned off by setting
add-image-links
and
add-figcaption
to “0”.
Keyboard tags.
Text inside ||double bars||
will be put inside <kbd></kbd>
tags. As
with backticks, any “less-than” character will be converted to
<
.
Line breaks.
Two spaces followed by a newline will become <br>
.
Links.
Inline links.
The construct [A link](https://example.com)
will be converted into
<a href="https://example.com">A link</a>
.
Special case is the form [=somemacro Link title](https://anything)
which
prepends the body of a macro
somemacro
into the <a></a>
tag (here broken into multiple lines for clarity):
<a href="https://anything">
<!-- contents of somemacro -->
Link title</a>
This can, for example, be used to add SVG icons to links. See Macros.
Also, the form [(Link title)](http://asite.com)
will surround the link
title (text between <a></a>
tags) with <span></span>
, like so:
<a href="http://asite.com"><span>Link title</span></a>
This allows for separate styling of link text. It can be combined with the macro-form:
[=somemacro (Link title)](http://asite.com)
It will prepend the body of a macro
somemacro
outside of the <span></span>
:
<a href="http://asite.com">
<!-- contents of somemacro -->
<span>Link title</span></a>
Exception is the form [Some text (parenthesized)](https://somelink)
,
which won't output <span>
tags, and will simply include the parentheses
in the
output instead. This form doesn't apply to macro-form [=macro Some (text)](http://link)
, which will always output <span>
tags.
Regular links.
Everything said about inline links applies to regular links, with the
exception
that instead of the parenthesized
URL,
link text inside brackets should be followed by link id (different than
the
“id
”
attribute!)
inside brackets, and a
separate definition of that link id is needed, usually near the end of
input.
For example:
Here's a link to [my website][mw].
[mw]: https://mysite.com
will produce:
<p>Here's a link to <a href="https://mysite.com">my
website</a>.</p>
This can help reduce the amount of code in the text of the page.
Lists.
Lines starting with a dash (-
), an asterisk (*
) or lowercase
“o”, followed by a space, will start an unordered list (if used
for the first time) and start a list item. Input:
Colors are:
- Red
- Green
- Blue
and so on.
will produce:
<p>Colors are:</p>
<ul>
<li><p>Red
</p></li>
<li><p>Green
</p></li>
<li><p>Blue</p>
</li></ul>
<p>And so on.</p>
Indenting a line after a blank line with two spaces will create a paragraph within the list item, otherwise it will end the list:
- This is a text inside a list item.
Remarkably, this paragraph is as well.
- This is already a second item.
This is after the list.
will produce:
<ul>
<li><p>This is a text inside a list item.</p>
<p>Remarkably, this paragraph is as well.
</p></li>
<li><p>This is already a second item.</p></li></ul>
<p>This is after the list.</p>
Lists can't be nested, and the characters inside an existing list which would otherwise start a list shall just be inserted as-is in the output.
Non-breaking hyphen.
Tilde followed by a hyphen (~-
) will insert ‑
in the output.
Non-breaking space.
Two consecutive tildes (~~
) will produce
in the output.
Paragraphs.
Text surrounded by newlines will be put inside <p></p>
tags. See
KNOWN LIMITATIONS.
Strikethrough.
Text surrounded by tildes (~
) will be put inside <s></s>
.
Tables.
Lines starting with vertical bars (|
) will be transformed into
HTML
tables. The first such line is treated as a header row, second as the
alignment specifier (currently ignored), and the rest regular rows. Table
cells and header cells are specified by additional bar characters. For
example, input:
| Month | Jan | Feb | Mar | Apr |
|-------|-----|-----|-----|-----|
| Qty | 2.35| 1.2 | 8 | 15 |
will be transformed into:
<table>
<thead>
<tr><th> Month </th><th> Jan </th><th> Feb </th><th> Mar </th>
<th> Apr </th></tr>
</thead>
<tbody>
<tr><td> Qty </td><td> 2.35</td><td> 1.2 </td><td> 8 </td>
<td> 15 </td></tr>
</tbody>
</table>
Partial tables
are a modification of Markdown tables, allowing them to be combined with the
TSV
or
CSV
directive. In order for tables to work with templating, they need two
additional lines at the top and the bottom, to mark the start and the end of
the table. All the lines in partial tables need to start with
“|
@”, with only the
character following at-mark being different:
|@\------|-----|-----|-----|-----|
{tsv "../tsv/sales" 1}
|@# $#1 | $#2 | $#3 | $#4 | $#5 |
{/tsv}
|@-------|-----|-----|-----|-----|
{tsv "../tsv/sales"}
|@ $1 | $2 | $3 | $4 | $5 |
{/tsv}
|@/------|-----|-----|-----|-----|
The rest of the line following the third character in top, bottom and lines separating header from body is ignored by the parser and is included in this example only for aesthetic purposes. See TSV/CSV templating.
Math mode is supported through KaTeX. With
katex
installed, anything between dollar signs ($
) will be transformed into
MathML and
HTML
markup. To generate display math, use double dollar signs ($$
). The text
between the dollar signs in both cases should be LaTeX source code, and is
passed to
katex.
The KaTeX stylesheet is not included, and needs to be included separately
through the
stylesheet
or
inline-stylesheet
YAML
variables (see
stylesheet, inline-stylesheet).
Brand “watermark”.
Directive {made-by}
results in a div to be output at the end of the
document, with the id made-by
containing the “watermark” text
and a link to the
slweb
home page.
General tags.
Directive {sometag}{/sometag}
will be transformed into
<sometag></sometag>
.
Class attributes.
Directive {tag.myclass mysecondclass}{/tag}
will be transformed into
<tag class="myclass mysecondclass"></tag>
. Only one class attribute is
permitted per tag directive (subsequent dots will be inserted as part of
the class
attribute), but you can include multiple classes by separating
them with a space, just like in
HTML,
and you can have both a class attribute and an id attribute per tag.
A variation of this is to use a special form {.myclass}{/.myclass}
,
which will be transformed into <div class="myclass"></div>
.
Id attributes.
Directive {tag#myid}{/tag}
will be transformed into <tag id="myid"></tag>
. Only one id attribute is permitted per tag directive
(subsequent hash signs will be included as part of the id
attribute),
and you can have both an id attribute and a class attribute per tag.
A variation of this is to use a special form {#myid}{/#myid}
, which will
be transformed into <div id="myid"></div>
.
Includes.
Directive {include "somefile"}
will fork, parse
somefile.slw
(related to
basedir)
and output the resulting
HTML
as if the option --body-only
was specified. All
YAML
variables will be preserved.
Macros.
Macros can be declared using the directive {=!macroname}{/=!macroname}
,
where
macroname
is the name of the macro. Anything between those two tags will then become the
body of a macro. Whenever {=macroname}
is subsequently encountered in the
file, it will be replaced by the macro body. Macro definitions can't be nested
nor can they contain macro calls, and doing so will produce an error. Tags
won't be processed within the body of a macro.
Previous Git commit information.
Directive {git-log}
is converted into a div with the id git-log
containing the information about the previous commit (as having information
about the current commit would be impossible).
Subdirectory inclusion (blogging directive).
The directive {incdir "
dirname
"
num =macroname
listonly}
(num,
macroname and the literal “listonly
” are optional) will be
expanded as follows:
<ul class="incdir">
tag will be inserted into the document instead of
the directive.<li>
tag will be inserted into the ul
tag.
If the parameter listonly
was
specified, a <li>
tag will instead be inserted for
every .slw file within each of the subdirectories, sorted in
reverse lexicographical order. In this case, each <li>
will contain a permalink with a title preceded by a timestamp formatted
according to list_timestamp_format
. Title and date are read
from each .slw, as YAML variables title and
date. For this case, processing of the
incdir directive stops here.
<details>
tag will be inserted into each <li>
tag.<details>
tag, a <summary>
tag will be inserted with
the name of the subdirectory. If
macroname
is present, the name of the subdirectory will be prepended with the body
of a macro
macroname
(for example, this can be used to include custom
SVG
markup as arrows).<summary>
tag, a <div>
tag will be inserted into <details>
,
containing the concatenated output from processing each of the
.slw
files inside that subdirectory in reverse lexicographical order, in a
manner
similar to the
include
directive. (See
Includes and date, ext-in-permalink, permalink-url,
samedir-permalink variables.)
The output of each file will be surrounded by an <article>
tag.If the included file has the variable incdir-only-summary set to
“1”, only the text within the summary marks (/-
and -/
) will
be output, and if the variable incdir-footer-permalink-text
is also set, <footer>
element with a permalink to the article will be
added after it.
Example: (posts/blog-post.slw
)
---
incdir-only-summary: 1
---
/-This article is great!-/ The quick brown fox jumps over the lazy sleeping dog.
The quick brown fox jumps over the lazy sleeping dog. The quick brown fox jumps
over the lazy sleeping dog. The quick brown fox jumps over the lazy sleeping
dog. The quick brown fox jumps over the lazy sleeping dog. The quick brown fox
jumps over the lazy sleeping dog.
(blog-index.slw
)
---
incdir-footer-permalink-text: Click here!
---
{incdir "."}
will produce
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="slweb">
</head>
<body>
<ul class="incdir">
<li>
<details open>
<summary>posts</summary>
<div>
<article>
This article is great!<footer>
<a href="././posts/blog-post">Click here!</a>
</footer>
</article>
</div>
</details>
</li>
</ul>
</body>
</html>
for blog-index.slw
, and
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="slweb">
</head>
<body>
<p>This article is great! The quick brown fox jumps over the lazy sleeping dog.
The quick brown fox jumps over the lazy sleeping dog. The quick brown fox jumps
over the lazy sleeping dog. The quick brown fox jumps over the lazy sleeping
dog. The quick brown fox jumps over the lazy sleeping dog. The quick brown fox
jumps over the lazy sleeping dog.</p>
</body>
</html>
for posts/blog-post.slw
.
TSV/CSV templating.
Directive {tsv "
tsvfile
"
iter
}{/tsv}
marks a template.
Whatever is between {tsv}
and {/tsv}
will be processed by
slweb
and collected as a template. Afterwards, file tsvfile.tsv will be read and
for each of its lines (up to
iter,
if present) the collected template will be output, substituting each occurrence
of a register mark $
n
(where n is between 1 and 9,
inclusive) with the corresponding
TSV
field of the read line. All of this applies to the similar {csv}
directive
for
CSV
files. In the case of
CSV
files, they need to use semicolons (;) or commas (,) as delimiters (or set
csv-delimiter
as a
YAML
variable in the calling
.slw
file) and double quotation marks (") as field boundaries. First line in the
TSV
file is parsed as a header and associated with register marks $#1
to
$#9
. The symbol $ is represented as $$
. Consequently, math modes
(both inline and display) cannot be used within templates.
Parts of the template can be conditionally rendered by using the construct:
$?n
<!-- code to include if $n nonempty -->
$?!
<!-- code to include if $n empty -->
$?/
where n is between 1 and 9, inclusive. For example, having a file sales.tsv:
Item Q1 2020 Q2 2020 Q3 2020 Q4 2020
Toothpick 15.2 12 10
Teapot 1.2 5 3.5
We can write
{tsv "sales"}
{.sales-segment}
$#1 {q}$1{/q} sales by quarter for the last year in
thousands of units sold (for $$) were as follows:
$?2{.q1}$2{/.q1}$?!{.q1 na}N/A{/.q1}$?/
$?3{.q2}$3{/.q2}$?!{.q2 na}N/A{/.q2}$?/
$?4{.q3}$4{/.q3}$?!{.q3 na}N/A{/.q3}$?/
$?5{.q4}$5{/.q4}$?!{.q4 na}N/A{/.q4}$?/
{/.sales-segment}
{/tsv}
to get:
<div class="sales-segment">
Item <q>Toothpick</q> sales by quarter for the last year in
thousands of units sold (for $) were as follows:
<div class="q1">15.2</div>
<div class="q2">12</div>
<div class="q3 na">N/A</div>
<div class="q4">10</div>
</div>
<div class="sales-segment">
Item <q>Teapot</q> sales by quarter for the last year in
thousands of units sold (for $) were as follows:
<div class="q1">1.2</div>
<div class="q2 na">N/A</div>
<div class="q3">5</div>
<div class="q4">3.5</div>
</div>
TSV directives can't be nested and doing so will produce an error.
The directives {csv-count "
csvfile
"}
and
{tsv-count "
tsvfile
"}
output the number of
“records” in the file csvfile.csv or tsvfile.tsv (number of
lines minus one for the header row).
No newline is output following the number.
add-article-header.
If set to “1”, title, date and header text at the beginning of the
output body will be enclosed in a <header>
tag.
add-figcaption.
If set to “0”, <figure>
and <figcaption>
tags around images
will not be added (otherwise they will by default, see
Images).
add-footnote-div.
If set to “1”, list of footnotes preceded by a horizontal rule
will be surrounded with a div having the id “footnotes
” (see
Footnotes).
add-image-links. If set to “0”, links around images will not be added (otherwise they will by default, see Images).
author.
If set, the contents of this variable will be added to the start of the output
body (inside a <header>
tag if it is set), surrounded by
<address></address>
.
canonical.
Contents of this variable will be added as a value of a rel
attribute for
the <link rel="canonical">
tag.
csv-delimiter. Changes the delimiter used for .csv file parsing. By default, this will be a semicolon (;). See CSV templating.
date.
If present, this variable, assumed to be in ISO 8601 UTC
datetime format, will be parsed to determine the timestamp in permalinks
generated by the
incdir
directive. The actual values used depend on the source-level constants
article_timestamp_format (for incdir
without the argument
“listonly”) and list_timestamp_format (for incdir
with the
argument “listonly”).
ext-in-permalink.
If set to “0”, permalinks generated by the
incdir
directive will not have the extension included in the
href
attribute. For example, instead of blog/2020/august.html
, the resulting
permalink will have blog/2020/august
.
favicon-url.
If present, this
URL
will be used as a favicon
URL
instead of the default, /favicon.ico
.
feed, feed-type, feed-desc.
If all are present, contents of those variables will be added as href and
type and title attributes (respectively) to the <link rel="alternate">
tag in the page's <head>
.
header-text.
The contents of this variable will be added to the start of the output body
(inside a <header>
tag if it is set), surrounded by <p></p>
.
image-file-prefix.
If present, the value of src
attribute will be appended to the contents of
this variable, with path separator added as necessary, when determining the
actual file path to pass to the
identify(1)
command. Otherwise, src
is appended to “.”.
incdir-footer-permalink-text.
If set, and the variable
incdir-only-summary
is set to “1”, when the file where this variable is set is
included by the {incdir}
directive, <footer>
tag containing the permalink
to the calling file will be appended to the text between the summary marks
/-
and -/
.
incdir-only-summary.
If set to “1”, when the file where this variable is set is
included by the {incdir}
directive, only the text within the summary marks
/-
and -/
will be output.
inline-stylesheet.
The contents of this variable will be treated as a
CSS
file name to be included inline in the header using the <style>
tag. You
can add more than one
inline-stylesheet
declaration per
.slw
file.
lang.
Contents of this variable will be used for the lang
attribute of the
<html>
tag.
link-prefix. This variable specifies the prefix which will be prepended to relative URLs in links. Final slash needs to be included if link-prefix is a complete path.
meta.
Contents of this variable is treated as a filename specifying the TSV file
containing a list of values to be inserted into meta tags. The first row of
this TSV file is treated as the header row and ignored. First column in this
TSV file contains the meta variable names, and the second column contains meta
variable values. For each row, a <meta name="firstcol" content="secondcol">
is inserted, where
firstcol
is the first column, and
secondcol
is the second column. If the values in the second column are surrounded with
percent signs (%
), the value between them is treated as the name of the
YAML variable to insert. For example, having the file
m.tsv:
Name Content
og:url %canonical%
and
---
canonical: https://example.com/
meta: m.tsv
---
in the .slw file, a
<meta name="og:url" content="https://example.com/">
line will be added to the output.
permalink-url. If present, this URL will completely replace the link used in the href attribute of permalinks generated by the incdir directive. This variable is more useful in individual .slw files inside the subdirectories of the dirname provided to the incdir directive.
site-desc.
The contents of this variable will be inserted as the value of the
content
attribute of the <meta name="description">
tag.
site-name.
The contents of this variable will be inserted inside the <title></title>
tags.
stylesheet.
The contents of this variable will be treated as a
CSS
file name to be included using the <link rel="stylesheet">
tag. You can
add more than one
stylesheet
declaration per
.slw
file.
title.
If present, contents of this variable will be prepended to the body (inside a
<header>
tag if it is set) as a heading with the level determined by the
title-heading-level
variable, defaulting
to 2. (title: Some title
becomes <h2>Some title</h2>
).
title-heading-level. See title.
sed(1)
Given the file index.slw
in the current directory:
---
site-name: Test website
site-desc: My first website in slweb
---
{main}
# Hello world
This is an _example_ of a statically generated HTML.
{/main}
after using the command:
$ slweb index.slw > index.html
file index.html
contains:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test website</title>
<meta charset="utf-8">
<meta name="description" content="My first website in slweb">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="slweb">
</head>
<body>
<main>
<h1>Hello world</h1>
<p>This is an <em>example</em> of a statically generated HTML.</p>
</main>
</body>
</html>
Currently there is no way to determine where the paragraph inside a tag should
begin and end without adding blank lines or using the {p}{/p}
notation.
For example, the code
{tag}
First paragraph
Second paragraph
{/tag}
will produce
<tag>
First paragraph
<p>Second paragraph
</tag></p>
You can suppress the paragraph starting/ending tags by prepending blank lines with a backslash, like this:
{tag}
First paragraph
\
Second paragraph
{/tag}
which will produce
<tag>
First paragraph
Second paragraph
</tag>
Adding blank lines helps, if paragraphs are desired:
{tag}
First paragraph
Second paragraph
{/tag}
will produce
<tag>
<p>First paragraph</p>
<p>Second paragraph</p>
</tag>
Also see Break marks above.
Strahinya Radich, https://strahinja.org
Bugs can be reported using the ticket tracker at: https://todo.sr.ht/~strahinja/slweb
commit e77c3ab3f8adbf9bd2ec8afd6b32b8528282d549 Author: Страхиња Радић <contact@strahinja.org> Date: 2024-04-28T18:30:48+02:00 Update to 0.9.8 Signed-off-by: Страхиња Радић <contact@strahinja.org>