engineering blog at https://blog.tangled.sh

Compare changes

Choose any two refs to compare.

Changed files
+214
pages
blog
+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.