krz/orgo
Lightning fast org-mode static site generator.
clone: git clone https://gitbay.org/krz/orgo.git
v0.19.1: docs/guide/02-configuration.org · raw
1#+TITLE: Configuration
2#+DESCRIPTION: Every setting in orgo.toml, what it changes, and what it costs.
3#+LEDE: All of it optional. A missing config is a valid config.
4
5orgo looks for =orgo.toml= in the source directory. Pass a different path with
6=--config=. Every field has a default, so a directory of org files with no config still
7builds a complete site.
8
9A *missing* config is normal and silent. A *malformed* one is an error, and an unknown
10key is rejected by name — a misspelled setting that silently does nothing is how people
11lose an afternoon.
12
13* The whole file
14
15#+BEGIN_SRC toml
16[site]
17title = "orgo site"
18base_url = ""
19description = ""
20language = "en"
21
22[nav]
23mode = "top-level"
24# pages = ["index.org", "about.org"]
25
26[templates]
27dir = "templates"
28expose_page_list = false
29
30[highlight]
31theme = "InspiredGitHub"
32syntaxes_dir = "syntaxes"
33
34[build]
35drafts = false
36assets = []
37
38[html]
39heading_offset = 1
40toc = true
41section_numbers = false
42#+END_SRC
43
44Plus any number of =[[collections]]= blocks, documented in [[file:03-collections.org][Collections]].
45
46* [site]
47
48| Key | Default | Meaning |
49|-----+---------+---------|
50| =title= | ="orgo site"= | Site name. Available as ={{ site.title }}=. |
51| =base_url= | ="" | Absolute origin, *no trailing slash*. |
52| =description= | ="" | Available as ={{ site.description }}=. |
53| =language= | ="en"= | Goes in =<html lang>= in the built-in layout. |
54
55** base_url
56
57Leave it empty and the site is built entirely with relative URLs, which means it works
58from a subdirectory, from a filesystem path, and from any origin. That portability is why
59it is the default.
60
61Set it when you need absolute URLs, which two things require: *feeds*, because a feed is
62read away from the site that served it, and *canonical links*. The =absolute= template
63filter turns a site-root-relative path into a full URL, and errors if there is no base
64URL to build one from — rather than quietly emitting a relative URL that would make the
65feed invalid everywhere while looking fine.
66
67A trailing slash is rejected, because =https://example.com/= plus =blog/x.html= is
68=https://example.com//blog/x.html=.
69
70* [nav]
71
72The navigation shared by every page.
73
74| =mode= | Includes |
75|--------+----------|
76| ="top-level"= (default) | Pages at the site root. |
77| ="all"= | Every page. |
78| ="explicit"= | Only =nav.pages=, in the order listed. |
79| ="none"= | Nothing. |
80
81*"all" makes output quadratic.* Each of /n/ pages carries /n/ links, so total output
82grows with the square of the site. On a 1,790-page site that was 284 MB of mostly
83navigation. It is fine for a handful of pages and a trap beyond that.
84
85="top-level"= keeps the nav a map of the site's top level rather than an index of its
86contents, so nav size does not depend on how much you write.
87
88** explicit
89
90#+BEGIN_SRC toml
91[nav]
92mode = "explicit"
93pages = ["index.org", "about.org", "uses.org"]
94#+END_SRC
95
96Paths are *source* paths relative to the source root, and the order given is the order
97rendered — a hand-written nav is a designed sequence, not an alphabetical one. Naming a
98page that does not exist is an error, because a silently shorter nav is a poor way to
99learn about a typo.
100
101** Section landing pages in the nav
102
103If your sections live in subdirectories, none of them are top-level pages. Put the
104section's *generated* index in the nav instead, with =nav = true= on its collection —
105that is the page a nav entry should point at anyway.
106
107A generated page has no source file, so name it in =pages= by its *output* path:
108
109#+BEGIN_SRC toml
110[nav]
111mode = "explicit"
112pages = ["blog/index.html", "garden/index.html", "about.org"]
113#+END_SRC
114
115That is the only way to interleave the two: a collection that sets =nav = true= without
116being listed is appended after everything you did list, so ="about.org"= alone would put
117=About= first and the sections after it. Listing all of them puts each exactly where you
118said. Either spelling works for an authored page too — its source path or its output
119path — though the source path is the one that survives a =#+SLUG:=.
120
121* [[pages]]
122
123Which layout a page renders through. Without any of these, every authored page uses
124=base.html=.
125
126#+BEGIN_SRC toml
127[[pages]]
128match = "blog"
129template = "post.html"
130#+END_SRC
131
132=match= is a *source* path relative to the source root — a directory, covering every page
133beneath it however deep, or one =.org= file. It is matched by path component, so =blog=
134covers =blog/2026/post.org= and does not touch =blogroll.org=.
135
136A section's layout is a property of the section, which is why this is a rule and not
137something you write in each file: a blog post carries the same byline and reply footer as
138every other one, and repeating that in 200 files means maintaining one fact 200 times.
139
140** Which rule wins
141
142Most specific, by path depth — =blog/notes= beats =blog=, whatever order they appear in.
143An empty =match= covers the whole site, which is how you rename the default layout.
144
145A page that differs from its section says so itself, and that wins over any rule:
146
147#+BEGIN_SRC org
148,#+TITLE: Colophon
149,#+TEMPLATE: wide.html
150#+END_SRC
151
152Naming a template that is not in the templates directory is an error that names the page,
153the template and what does exist — a layout typo should not be a hunt.
154
155* [templates]
156
157| Key | Default | Meaning |
158|-----+---------+---------|
159| =dir= | ="templates"= | Directory of templates, relative to the source root. |
160| =expose_page_list= | =false= | Give every template a =pages= list of all page metadata. |
161
162** expose_page_list costs incremental precision
163
164With it on, any page can read every page's metadata — so adding one page can change any
165page's output, and the whole site must re-render on every add, rename or retitle. That is
166the trade for building an index by hand in a template. Most people want a
167[[file:03-collections.org][collection]] instead, which gets the same result while keeping
168adding a post a one-page rebuild.
169
170* [highlight]
171
172| Key | Default | Meaning |
173|-----+---------+---------|
174| =theme= | ="InspiredGitHub"= | A syntect theme name. |
175| =syntaxes_dir= | ="syntaxes"= | Extra =.sublime-syntax= files. |
176
177Any theme syntect ships: =InspiredGitHub=, =Solarized (dark)=, =Solarized (light)=,
178=base16-ocean.dark=, =base16-ocean.light=, =base16-eighties.dark=, =base16-mocha.dark=.
179An unknown name is an error listing the valid ones.
180
181Highlighting emits *CSS classes*, never inline styles, so themes live in a stylesheet.
182Each build writes =syntax.css= into the output and every page links it.
183
184orgo bundles TOML and Org on top of syntect's built-in languages. Anything else
185missing is a file away: put a =.sublime-syntax= definition in =syntaxes_dir= and it is
186loaded. A definition that fails to parse is reported and skipped, because one bad file
187should not stop a site from building.
188
189* [build]
190
191| Key | Default | Meaning |
192|-----+---------+---------|
193| =drafts= | =false= | Include pages marked =#+DRAFT:=. |
194| =assets= | =[]= | Extra directories copied to the *site root*. |
195
196=--drafts= on the command line turns this on for one run. The flag can only turn drafts
197on; it never turns off a config that asked for them.
198
199** Static files that live elsewhere
200
201A site's static files do not always sit where its writing does. weblorg publishes
202=theme/static/= at =/=, and a repository migrating from it should not have to move
203=robots.txt= next to its blog posts to keep the URL:
204
205#+BEGIN_SRC toml
206[build]
207assets = ["../theme/static"]
208#+END_SRC
209
210Paths are relative to the source root and may point outside it. Each directory's
211*contents* land at the site root — =theme/static/img/logo.svg= publishes at =/img/logo.svg=,
212not =/static/img/logo.svg=.
213
214Two files claiming one URL is a build error naming both, rather than a coin flip decided
215by directory order. A path that is not a directory is an error too, since it is a typo.
216
217Under =watch= and =serve= these directories are watched as well, so editing a stylesheet
218outside the source tree still reloads the page.
219
220* [html]
221
222| Key | Default | Meaning |
223|-----+---------+---------|
224| =heading_offset= | =1= | Added to every org heading level. |
225| =toc= | =true= | Make =page.toc= available to templates. |
226| =section_numbers= | =false= | Number headings =1.=, =1.1.=, … |
227
228** heading_offset
229
230A level-1 org heading renders as =<h2>= by default, because the layout supplies the page
231title as the =<h1>=. This matches Emacs, whose =org-html-toplevel-hlevel= is 2 for the
232same reason.
233
234Set it to =0= if your template renders no title of its own — otherwise the document
235starts at =<h2>= with nothing above it.
236
237** section_numbers differs from Emacs on purpose
238
239=org-export-with-section-numbers= is on in Emacs, so an org-published site inherits
240numbered headings whether or not anyone chose them. Most sites do not want them, so the
241default here is the taste rather than the inheritance. Turning it on emits Emacs' own
242=section-number-N= classes.
243
244* Per-file overrides
245
246Org's own =#+OPTIONS:= switches override the site setting for one document:
247
248#+BEGIN_SRC org
249,#+OPTIONS: toc:nil num:t
250#+END_SRC
251
252| Switch | Overrides |
253|--------+-----------|
254| =toc:nil= / =toc:t= | =[html] toc= |
255| =num:t= / =num:nil= | =[html] section_numbers= |
256
257Off is spelled =nil=, =false=, =no=, =0= or =off=; anything else is on.
258
259* Configuration is a cache input
260
261The resolved config is hashed into every page's render key, so editing =orgo.toml=
262re-renders exactly the pages it affects — which for most settings is all of them. You
263never need =--no-cache= after a config change.