···11+---
22+atroot: true
33+template:
44+slug: docs
55+title: why we rolled our own documentation site
66+subtitle: you don't need mintlify
77+date: 2026-01-06
88+authors:
99+ - name: Akshay
1010+ email: akshay@tangled.org
1111+ handle: oppi.li
1212+draft: true
1313+---
1414+1515+We recently organized our documentation and put it up on
1616+https://docs.tangled.org, using just pandoc. For several
1717+reasons, using pandoc to roll your own static sites is more
1818+than sufficient for small projects.
1919+2020+
2121+2222+## requirements
2323+2424+- Lives in [our
2525+ monorepo](https://tangled.org/tangled.org/core).
2626+- No JS: a collection of pages containing just text
2727+ should not require JS to view!
2828+- Searchability: in practice, documentation engines that
2929+ come bundled with a search-engine have always been lack
3030+ lustre. I tend to Ctrl+F or use an actual search engine in
3131+ most scenarios.
3232+- Low complexity: building, testing, deploying should be
3333+ easy.
3434+- Easy to style
3535+3636+## evaluating the ecosystem
3737+3838+I took the time to evaluate several documentation engine
3939+solutions:
4040+4141+- [Mintlify](https://www.mintlify.com/): It is quite obvious
4242+ from their homepage that mintlify is performing an AI
4343+ pivot for the sake of doing so.
4444+- [Docusaurus](https://docusaurus.io/): The generated
4545+ documentation site is quite nice, but the value of pages
4646+ being served as a full-blown React SPA is questionable.
4747+- [MkDocs](https://www.mkdocs.org/): Works great with JS
4848+ disabled, however the table of contents needs to be
4949+ maintained via `mkdocs.yml`, which can be quite tedious.
5050+- [MdBook](https://rust-lang.github.io/mdBook/index.html):
5151+ As above, you need a `SUMMARY.md` file to control the
5252+ table-of-contents.
5353+5454+MkDocs and MdBook are still on my radar however, in case we
5555+need a bigger feature set.
5656+5757+## using pandoc
5858+5959+[pandoc](https://pandoc.org/) is a wonderfully customizable
6060+markup converter. It provides a "chunkedhtml" output format,
6161+which is perfect for generating documentation sites. Without
6262+any customization,
6363+[this](https://pandoc.org/demo/example33/) is the generated
6464+output, for this [markdown file
6565+input](https://pandoc.org/demo/MANUAL.txt).
6666+6767+- You get an autogenerated TOC based on the document layout
6868+- Each section is turned into a page of its own
6969+7070+Massaging pandoc to work for us was quite straightforward:
7171+7272+- I first combined all our individual markdown files into
7373+ [one big
7474+ `DOCS.md`](https://tangled.org/tangled.org/core/blob/master/docs/DOCS.md)
7575+ file.
7676+- Modified the [default
7777+ template](https://github.com/jgm/pandoc-templates/blob/master/default.chunkedhtml)
7878+ to put the TOC on every page, to form a "sidebar", see
7979+ [`docs/template.html`](https://tangled.org/tangled.org/core/blob/master/docs/template.html)
8080+- Inserted tailwind `prose` classes where necessary, such
8181+ that markdown content is rendered the same way between
8282+ `tangled.org` and `docs.tangled.org`
8383+8484+Generating the docs is done with one pandoc command:
8585+8686+```bash
8787+pandoc docs/DOCS.md \
8888+ -o out/ \
8989+ -t chunkedhtml \
9090+ --variable toc \
9191+ --toc-depth=2 \
9292+ --css=docs/stylesheet.css \
9393+ --chunk-template="%i.html" \
9494+ --highlight-style=docs/highlight.theme \
9595+ --template=docs/template.html
9696+```
9797+9898+## avoiding javascript
9999+100100+The "sidebar" style table-of-contents needs to be collapsed
101101+on mobile displays. Most of the engines I evaluated seem to
102102+require JS to collapse and expand the sidebar, with MkDocs
103103+being the outlier, it uses a checkbox with the
104104+[`:checked`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:checked)
105105+pseudo-class trick to avoid JS.
106106+107107+The other ways to do this are:
108108+109109+- Use `<details` and `<summary>`: this is definitely a
110110+ "hack", clicking outside the sidebar does not collapse it.
111111+ Using Ctrl+F or "Find in page" still works through the
112112+ details tag though.
113113+- Use the new `popover` API: this seems like the perfect fit
114114+ for a "sidebar" component.
115115+116116+The bar at the top includes a button to trigger the popover:
117117+118118+```html
119119+<button popovertarget="toc-popover">Table of Contents</button>
120120+```
121121+122122+And a `fixed` position div includes the TOC itself:
123123+124124+```html
125125+<div id="toc-popover" popover class="fixed top-0">
126126+ <ul>
127127+ Quick Start
128128+ <li>...</li>
129129+ <li>...</li>
130130+ <li>...</li>
131131+ </ul>
132132+</div>
133133+```
134134+135135+The TOC is scrollable independently and can be collapsed by
136136+clicking anywhere on the screen outside the sidebar.
137137+Searching for content in the page via "Find in page" does
138138+not show any results that are present in the popover
139139+however. The collapsible TOC is only available on smaller
140140+viewports, the TOC is not hidden on larger viewports.
141141+142142+## search
143143+144144+There is native search on the site for now. Taking
145145+inspiration from https://htmx.org's search bar, our search
146146+bar also simply redirects to Google:
147147+148148+```
149149+<form action="https://google.com/search">
150150+ <input type="hidden" name="q" value="+[inurl:https://docs.tangled.org]">
151151+ ...
152152+</form>
153153+```
154154+155155+I mentioned earlier that Ctrl+F has typically worked better
156156+for me than, say, the search engine provided by Docusaurus.
157157+To that end, the same docs have been exported to a ["single
158158+page" format](https://docs.tangled.org/single-page.html), by
159159+just removing the `chunkedhtml` related options:
160160+161161+```diff
162162+ pandoc docs/DOCS.md \
163163+ -o out/ \
164164+- -t chunkedhtml \
165165+ --variable toc \
166166+ --toc-depth=2 \
167167+ --css=docs/stylesheet.css \
168168+- --chunk-template="%i.html" \
169169+ --highlight-style=docs/highlight.theme \
170170+ --template=docs/template.html
171171+```
172172+173173+With all the content on a single page, it is trivial to
174174+search through the entire site with the browser. If the docs
175175+do outgrow this, I will consider other options!
176176+177177+## building and deploying
178178+179179+We use [nix](https://nixos.org) and
180180+[colmena](https://colmena.cli.rs/) to build and deploy all
181181+Tangled services. A nix derivation to [build the
182182+documentation](https://tangled.org/tangled.org/core/blob/master/nix/pkgs/docs.nix)
183183+site is written very easily with the `runCommandLocal`
184184+helper:
185185+186186+```nix
187187+runCommandLocal "docs" {} ''
188188+ .
189189+ .
190190+ .
191191+ ${pandoc}/bin/pandoc ${src}/docs/DOCS.md ...
192192+ .
193193+ .
194194+ .
195195+''
196196+```
197197+198198+The nixos machine is configured to serve the site [via
199199+nginx](https://tangled.org/tangled.org/infra/blob/master/hosts/nixery/services/nginx.nix#L7):
200200+201201+```nix
202202+services.nginx = {
203203+ enable = true;
204204+ virtualHosts = {
205205+ "docs.tangled.org" = {
206206+ root = "${tangled-pkgs.docs}";
207207+ locations."/" = {
208208+ tryFiles = "$uri $uri/ =404";
209209+ index = "index.html";
210210+ };
211211+ };
212212+ };
213213+};
214214+```
215215+216216+And deployed using `colmena`:
217217+218218+```bash
219219+nix run nixpkgs#colmena -- apply
220220+```
221221+222222+To update the site, I first run:
223223+224224+```bash
225225+nix flake update tangled
226226+```
227227+228228+Which bumps the `tangled` flake input, and thus
229229+`tangled-pkgs.docs`. The above `colmena` invocation applies
230230+the changes to the machine serving the site.
231231+232232+## notes
233233+234234+Going homegrown has made it a lot easier to style the
235235+documentation site to match the main site. Unfortunately
236236+there are still a few discrepancies between pandoc's
237237+markdown rendering and
238238+[goldmark's](https://pkg.go.dev/github.com/yuin/goldmark/)
239239+markdown rendering (which is what we use in Tangled). We may
240240+yet roll our own SSG,
241241+[TigerStyle](https://tigerbeetle.com/blog/2025-02-27-why-we-designed-tigerbeetles-docs-from-scratch/)!