+214
pages/blog/docs.md
+214
pages/blog/docs.md
···
···
1
+
---
2
+
atroot: true
3
+
template:
4
+
slug: docs
5
+
title: you don't need mintlify
6
+
subtitle: or, why we rolled our own documentation site
7
+
date: 2026-01-06
8
+
authors:
9
+
- name: Akshay
10
+
email: akshay@tangled.org
11
+
handle: oppi.li
12
+
draft: true
13
+
---
14
+
15
+
We recently organized our documentation and put it up on
16
+
https://docs.tangled.org, using pandoc and a bit of nix. For
17
+
several reasons, using pandoc to roll your own static sites
18
+
is more than sufficient for small projects.
19
+
20
+
## requirements
21
+
22
+
- Lives in [our
23
+
monorepo](https://tangled.org/tangled.org/core).
24
+
- No JS: a collection of pages containing just text
25
+
should not require JS to view!
26
+
- Searchability: in practice, documentation engines that
27
+
come bundled with a search-engine have always been lack
28
+
lustre. I tend to Ctrl+F or use an actual search engine in
29
+
most scenarios.
30
+
- Low complexity: building, testing, deploying should be
31
+
easy.
32
+
33
+
## evaluating the ecosystem
34
+
35
+
- [Mintlify](https://www.mintlify.com/): It is quite obvious
36
+
from their homepage that mintlify is performing an AI
37
+
pivot for the sake of doing so.
38
+
- [Docusaurus](https://docusaurus.io/): The generated
39
+
documentation site is quite nice, but the value of pages
40
+
being served as a full-blown React SPA is questionable.
41
+
- [MkDocs](https://www.mkdocs.org/): Works great with JS
42
+
disabled, however the table of contents needs to be
43
+
maintained via `mkdocs.yml`, which can be quite tedious.
44
+
- [MdBook](https://rust-lang.github.io/mdBook/index.html):
45
+
As above, you need a `SUMMARY.md` file to control the
46
+
table-of-contents.
47
+
48
+
MkDocs and MdBook are still on my radar however, in case we
49
+
need a bigger feature set.
50
+
51
+
## using pandoc
52
+
53
+
[pandoc](https://pandoc.org/) is a wonderfully customizable
54
+
markup converter. It provides a "chunkedhtml" output format,
55
+
which is perfect for generating documentation sites. Without
56
+
any customization,
57
+
[this](https://pandoc.org/demo/example33/) is the generated
58
+
output, for this [markdown file
59
+
input](https://pandoc.org/demo/MANUAL.txt).
60
+
61
+
- You get an autogenerated TOC based on the document layout
62
+
- Each section is turned into a page of its own
63
+
64
+
Massaging pandoc to work for us was quite straightforward:
65
+
66
+
- I first combined all our individual markdown files into
67
+
[one big
68
+
`DOCS.md`](https://tangled.org/tangled.org/core/blob/master/docs/DOCS.md)
69
+
file.
70
+
- Modified the [default
71
+
template](https://github.com/jgm/pandoc-templates/blob/master/default.chunkedhtml)
72
+
to put the TOC on every page, to form a "sidebar", see
73
+
[`docs/template.html`](https://tangled.org/tangled.org/core/blob/master/docs/template.html)
74
+
- Inserted tailwind `prose` classes where necessary, such
75
+
that markdown content is rendered the same way between
76
+
`tangled.org` and `docs.tangled.org`
77
+
78
+
Generating the docs is done with one pandoc command:
79
+
80
+
```bash
81
+
pandoc docs/DOCS.md \
82
+
-o out/ \
83
+
-t chunkedhtml \
84
+
--variable toc \
85
+
--toc-depth=2 \
86
+
--css=docs/stylesheet.css \
87
+
--chunk-template="%i.html" \
88
+
--highlight-style=docs/highlight.theme \
89
+
--template=docs/template.html
90
+
```
91
+
92
+
## avoiding javascript
93
+
94
+
The "sidebar" style table-of-contents needs to be collapsed
95
+
on mobile displays. Most of the engines I evaluated seem to
96
+
require JS to collapse and expand the sidebar, with MkDocs
97
+
being the outlier, it uses a checkbox with the
98
+
[`:checked`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:checked)
99
+
pseudo-class trick to avoid JS.
100
+
101
+
The other ways to do this are:
102
+
103
+
- Use `<details` and `<summary>`: this is definitely a
104
+
"hack", clicking outside the sidebar does not collapse it.
105
+
Using Ctrl+F or "Find in page" still works through the
106
+
details tag though.
107
+
- Use the new `popover` API: this seems like the perfect fit
108
+
for a "sidebar" component.
109
+
110
+
The bar at the top includes a button to trigger the popover:
111
+
112
+
```html
113
+
<button popovertarget="toc-popover">Table of Contents</button>
114
+
```
115
+
116
+
And a `fixed` position div includes the TOC itself:
117
+
118
+
```html
119
+
<div id="toc-popover" popover class="fixed top-0">
120
+
<ul>
121
+
Quick Start
122
+
<li>...</li>
123
+
<li>...</li>
124
+
<li>...</li>
125
+
</ul>
126
+
</div>
127
+
```
128
+
129
+
The TOC is scrollable independently and can be collapsed by
130
+
clicking anywhere on the screen outside the sidebar.
131
+
Searching for content in the page via "Find in page" does
132
+
not show any results that are present in the popover
133
+
however. The collapsible TOC is only available on smaller
134
+
viewports, the TOC is not hidden on larger viewports.
135
+
136
+
## search
137
+
138
+
There is no search on the site for now. I mentioned earlier
139
+
that Ctrl+F has typically worked better for me than, say,
140
+
the search engine provided by Docusaurus. To that end, the
141
+
same docs have been exported to a ["single page"
142
+
format](https://docs.tangled.org/single-page.html), by just
143
+
removing the chunkedhtml related options:
144
+
145
+
```diff
146
+
pandoc docs/DOCS.md \
147
+
-o out/ \
148
+
- -t chunkedhtml \
149
+
--variable toc \
150
+
--toc-depth=2 \
151
+
--css=docs/stylesheet.css \
152
+
- --chunk-template="%i.html" \
153
+
--highlight-style=docs/highlight.theme \
154
+
--template=docs/template.html
155
+
```
156
+
157
+
With all the content on a single page, it is trivial to
158
+
search through the entire site with the browser. If the docs
159
+
do outgrow this, I will consider other options!
160
+
161
+
## building and deploying
162
+
163
+
We use [nix](https://nixos.org) and
164
+
[colmena](https://colmena.cli.rs/) to build and deploy all
165
+
Tangled services. A nix derivation to [build the
166
+
documentation](https://tangled.org/tangled.org/core/blob/master/nix/pkgs/docs.nix)
167
+
site is written very easily with the `runCommandLocal`
168
+
helper:
169
+
170
+
```nix
171
+
runCommandLocal "docs" {} ''
172
+
.
173
+
.
174
+
.
175
+
${pandoc}/bin/pandoc ${src}/docs/DOCS.md ...
176
+
.
177
+
.
178
+
.
179
+
''
180
+
```
181
+
182
+
The nixos machine is configured to serve the site [via
183
+
nginx](https://tangled.org/tangled.org/infra/blob/master/hosts/nixery/services/nginx.nix#L7):
184
+
185
+
```nix
186
+
services.nginx = {
187
+
enable = true;
188
+
virtualHosts = {
189
+
"docs.tangled.org" = {
190
+
root = "${tangled-pkgs.docs}";
191
+
locations."/" = {
192
+
tryFiles = "$uri $uri/ =404";
193
+
index = "index.html";
194
+
};
195
+
};
196
+
};
197
+
};
198
+
```
199
+
200
+
And deployed using `colmena`:
201
+
202
+
```bash
203
+
nix run nixpkgs#colmena -- apply
204
+
```
205
+
206
+
To update the site, I first run:
207
+
208
+
```bash
209
+
nix flake update tangled
210
+
```
211
+
212
+
Which bumps the `tangled` flake input, and thus
213
+
`tangled-pkgs.docs`. The above `colmena` invocation applies
214
+
the changes to the machine serving the site.