1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 |
---
title: Templates
aliases: [Theme, Theming]
---
# Templates
mdssg ships one theme. Drop a file with the same name into your site's
`templates/` directory and yours is used instead — for that file only, so
replacing the page layout does not mean re-implementing the rest.
```
templates/
base.html the html shell
index.html the home page
page.html a note
seed.html a seed page
_backlinks.html the "mentioned in" panel
_related.html the "related" panel
style.css the stylesheet
```
## The language
```
{{ page.title }} a value, HTML-escaped
{{ page.html | raw }} trusted HTML, not escaped again
{{ page.date | date:"%d %B %Y" }}
{{ page.tags | join:" / " }}
{% if page.date %}...{% elif x %}...{% else %}...{% endif %}
{% if not backlinks %}...{% endif %}
{% for note in related %}
<a href="{{ note.url }}">{{ note.title }}</a>
{{ loop.index }} {{ loop.first }} {{ loop.last }}
{% endfor %}
{% include "_related.html" %}
{% extends "base.html" %}
{% block content %}...{% endblock %}
```
Filters: `raw`, `escape`, `date`, `slug`, `truncate`, `length`, `default`,
`join`, `upper`, `lower`.
## What it cannot do, on purpose
There is no arithmetic, no comparison, no method call, no indexing, and no way
to register a filter of your own. An expression is a dotted path plus filters
from a fixed table, and anything else is a parse error:
```
error: 'a == b' is not a value path. The template grammar has no arithmetic,
comparisons or calls - put the computation in the page context.
```
This is the whole design, not an unfinished part of it. A template language
that can compute grows until the interesting logic lives in HTML, where it
cannot be tested and cannot be read. If a template seems to need logic, the
value belongs in the context.
Paths also cannot begin with an underscore. Values are looked up with `getattr`
as well as by key, and allowing `_` would put `{{ a.__class__.__init__ }}` one
step away from a template.
## Escaping
Escaping is on by default. `{{ }}` escapes unless the value is already marked
as HTML, which only the markdown renderer and the `raw` filter produce. Getting
this backwards is the standard way a generator grows an injection bug, so it is
the default rather than something to remember.
## What a template receives
Every page:
| name | contents |
|------|----------|
| `site` | `title`, `url`, `description`, `author`, `language` |
| `page` | `title`, `url`, `slug`, `date`, `tags`, `html`, `description`, `is_post`, `is_seed`, `noindex` |
| `related` | list of `{title, url, seed}` |
| `asset_url`, `feed_url` | paths to the stylesheet and feed |
Notes also get `backlinks`, a list of `{title, url, excerpts}` where each
excerpt is HTML with the mention wrapped in `<mark>`. Seed pages get `mentions`
in the same shape. The home page gets `posts` and `notes`.
## Styling the graph
The theme leaves these hooks for you:
```css
.wikilink /* any resolved wikilink */
.wikilink-seed /* points at an unwritten note */
.wikilink-missing /* seeds disabled, nothing there */
.backlinks mark /* the mention inside a quote */
.seed-notice /* the banner on a seed page */
.embed /* transcluded content */
.embed-error /* a cycle or a depth limit */
```
The bundled stylesheet is about 400 lines, uses system fonts, adapts to
`prefers-color-scheme`, and ships no JavaScript at all.
|