krz/orgo
Lightning fast org-mode static site generator.
clone: git clone https://gitbay.org/krz/orgo.git
v0.19.1: docs/guide/06-authoring.org · raw
1#+TITLE: Authoring
2#+DESCRIPTION: URLs, drafts, excerpts, tables of contents — the metadata that shapes a page.
3#+LEDE: What to put at the top of a file, and what each keyword buys you.
4
5* URLs
6
7By default a source path becomes the matching output path: =blog/post.org= →
8=blog/post.html=.
9
10=#+SLUG:= overrides the *filename*, never the directory:
11
12#+BEGIN_SRC org
13,#+TITLE: AES Encryption
14,#+SLUG: aes-encryption
15#+END_SRC
16
17=blog/2018-11-28-aes-encryption.org= now publishes at =blog/aes-encryption.html=. This is
18how a date-prefixed filename — useful for sorting in a file manager — becomes a clean
19address.
20
21Slugs are reduced to a single safe path component, so a slug cannot escape the output
22directory however it was written. Two pages claiming one URL is a build error rather than
23one silently overwriting the other.
24
25Links follow slugs automatically: =[[file:blog/2018-11-28-aes-encryption.org]]= resolves
26to =blog/aes-encryption.html=.
27
28* Drafts
29
30#+BEGIN_SRC org
31,#+DRAFT: t
32#+END_SRC
33
34The page is not written at all, and is absent from listings, tag pages and navigation —
35not merely unlinked.
36
37It is also out of the symbol table, so a link *to* a draft is reported as a broken link.
38That is deliberate: it is what that link would be on the published site, and better found
39now than by a reader.
40
41#+BEGIN_SRC sh
42orgo serve content -o _site --drafts
43#+END_SRC
44
45The keyword is read forgivingly. =t=, =yes=, =1= and a bare =#+DRAFT:= all mean draft,
46because writing the keyword at all is the signal. Only an explicit =nil=, =false=, =no=,
47=0= or =off= means published — publishing someone's unfinished post because they typed
48=yes= instead of =t= is the wrong way to be strict.
49
50* Dates
51
52#+BEGIN_SRC org
53,#+DATE: <2026-02-02 Mon>
54,#+DATE: [2025-09-05 Fri 10:21:00]
55,#+DATE: 2024-05-01
56#+END_SRC
57
58All three work. =page.date= keeps what you wrote, and =page.date_iso= is the
59=YYYY-MM-DD= inside it — the value listings sort on and templates usually print.
60
61A page with no parseable date sorts *last* in a dated listing, in either direction, so a
62draft with no date never leads an archive.
63
64* Excerpts
65
66=page.excerpt= is =#+DESCRIPTION:= when the page sets one, and its first paragraph
67otherwise:
68
69#+BEGIN_SRC org
70,#+DESCRIPTION: How the borrow checker thinks about lifetimes.
71#+END_SRC
72
73The fallback matters more than the keyword: it means a listing has something to show
74whether or not the author ever thought about summaries. Use =truncate= in the template to
75cut a long paragraph to size.
76
77* Reading time
78
79=page.word_count= and =page.reading_time= (minutes at 200 wpm, rounded up) count *prose
80only*. Source and example blocks are excluded, because a post that is mostly a shell
81transcript should not read as an hour's work. =#+TITLE:= is metadata rendered as chrome,
82so it is not counted either.
83
84* Tags
85
86#+BEGIN_SRC org
87,#+FILETAGS: :rust:web:
88#+END_SRC
89
90Available as =page.tags=, and the input to tag pages — see
91[[file:03-collections.org][Collections]].
92
93* Table of contents
94
95Every page's heading tree is available as =page.toc= without any markup in the file. Turn
96it off for one document the way org already does:
97
98#+BEGIN_SRC org
99,#+OPTIONS: toc:nil
100#+END_SRC
101
102Or site-wide with =[html] toc = false=. Rendering it is the template's business; see
103[[file:04-templates.org][Templates]].
104
105* Section numbers
106
107Off by default, unlike Emacs. Turn them on for one document:
108
109#+BEGIN_SRC org
110,#+OPTIONS: num:t
111#+END_SRC
112
113Or site-wide with =[html] section_numbers = true=.
114
115* Your own metadata
116
117Every =#+KEYWORD:= reaches templates under its lowercased name:
118
119#+BEGIN_SRC org
120,#+SUBTITLE: A closer look
121,#+REVIEWED_BY: someone
122#+END_SRC
123
124#+BEGIN_SRC html
125{% if page.keywords.subtitle %}<p class="subtitle">{{ page.keywords.subtitle }}</p>{% endif %}
126#+END_SRC
127
128Nothing needs to be registered, and orgo needs no release to support a keyword you
129invented.
130
131* Assets
132
133Any non-=.org= file in the source directory is copied to the output, preserving layout:
134=content/img/diagram.png= → =_site/img/diagram.png=. Reference it from a page with an
135ordinary relative link, and from a template with ={{ root }}img/diagram.png=.
136
137Four things are *never* published:
138
139- Dot-entries such as =.git= and =.env=. A source directory is often a repository, and
140 publishing its history next to the homepage is a real way to leak a project.
141- =orgo.toml=, which is a build input.
142- The templates directory, likewise.
143- The output directory, when it lives inside the source — so =orgo build . -o _site=
144 does the obvious thing rather than copying its own output back into itself.
145
146Note that excluding dot-entries also means =.well-known/= cannot be published.