|
| 1 | +--- |
| 2 | +title: "Docs authoring guide" |
| 3 | +--- |
| 4 | + |
| 5 | +# Docs authoring guide |
| 6 | + |
| 7 | +How to write, structure, and ship the documentation that lives under `docs/`. |
| 8 | +KmpToolkit has a richer docs surface than a single-module library — this guide |
| 9 | +captures the three content types (narrative, cookbook recipes, per-module |
| 10 | +pages), the conventions each one follows, and the publishing pipeline that |
| 11 | +serves them. |
| 12 | + |
| 13 | +## The three surfaces |
| 14 | + |
| 15 | +| Surface | Source | Lands at | |
| 16 | +|---------|--------|----------| |
| 17 | +| **mkdocs site** (canonical) | `docs/**`, `mkdocs.yml` | `https://mobilebytelabs.github.io/KmpToolkit/` | |
| 18 | +| **Dokka API reference** | Kotlin `///` KDoc in source | Bundled inside each module's `-javadoc.jar` on Maven Central | |
| 19 | +| **GitHub Wiki** | `docs/**` mirrored | `https://github.com/MobileByteLabs/KmpToolkit/wiki/<basename>` | |
| 20 | + |
| 21 | +The mkdocs site is the primary product. The Dokka HTML is reference-only and |
| 22 | +opens from Maven Central. The wiki is a passive mirror for users who prefer |
| 23 | +GitHub's UI. |
| 24 | + |
| 25 | +## The three content types |
| 26 | + |
| 27 | +### 1. Narrative docs (`docs/index.md`, `docs/getting-started.md`) |
| 28 | + |
| 29 | +Top-of-funnel pages. Authored prose, full freedom on structure. Keep these |
| 30 | +short — the user is here to find a path into the library, not read a book. |
| 31 | + |
| 32 | +### 2. Per-module pages (`docs/modules/cmp-*.md`) |
| 33 | + |
| 34 | +One page per published module. 21 today, mirroring the 21 `cmp-*` modules |
| 35 | +in the source tree. Two flavors: |
| 36 | + |
| 37 | +- **README-embedded** (10 modules with `cmp-*/README.md`): the per-module |
| 38 | + page uses the `mkdocs-include-markdown-plugin` Liquid-style directive to |
| 39 | + embed the module's source-tree README. See any existing |
| 40 | + `docs/modules/cmp-network-monitor.md` for the live syntax. **Don't |
| 41 | + duplicate** the README content into the `docs/modules/` page — let the |
| 42 | + include do its job. |
| 43 | +- **Placeholder** (11 modules without README.md yet): minimal "this module is |
| 44 | + shipped; full docs coming" page with a link to the GitHub source. |
| 45 | + |
| 46 | +Whichever flavor: **always** include a one-line note about the API reference |
| 47 | +being inside the `-javadoc.jar` on Maven Central. |
| 48 | + |
| 49 | +When a module gains a `cmp-*/README.md`, convert its `docs/modules/cmp-*.md` |
| 50 | +placeholder to the embedded form in the same PR. |
| 51 | + |
| 52 | +### 3. Cookbook recipes (`docs/cookbook/{topic}/{recipe}.md`) |
| 53 | + |
| 54 | +The bulk of the user-facing docs. Strict format, CI-enforced. Use the |
| 55 | +template at [`_partials/cookbook-recipe-template.md`](_partials/cookbook-recipe-template.md): |
| 56 | + |
| 57 | +```markdown |
| 58 | +--- |
| 59 | +title: "How do I {task}?" |
| 60 | +reviewed_by: |
| 61 | + date: 2026-06 # YYYY-MM — bumped on review |
| 62 | + version: 3.5.x # last verified kmp-toolkit version |
| 63 | +--- |
| 64 | + |
| 65 | +# How do I {task}? |
| 66 | + |
| 67 | +## Quick start (minimal MWE) |
| 68 | +```kotlin |
| 69 | +// ≤ 15 lines runnable. |
| 70 | +``` |
| 71 | + |
| 72 | +## Caveats / per-platform notes |
| 73 | +- **Android:** … |
| 74 | +- **iOS:** … |
| 75 | + |
| 76 | +## Related |
| 77 | +- Module: [cmp-{name}](../../modules/cmp-{name}.md) |
| 78 | +- Sample: [`samples/sample-cmp-{name}/.../File.kt`](https://github.com/MobileByteLabs/KmpToolkit/tree/development/samples/sample-cmp-{name}) |
| 79 | +``` |
| 80 | + |
| 81 | +**Hard constraints (CI-checked):** |
| 82 | + |
| 83 | +- ≤ 80 lines total (`wc -l`) |
| 84 | +- ≥ 1 ` ```kotlin ` code block (`grep`) |
| 85 | +- Frontmatter has `reviewed_by.date` (YYYY-MM) + `version` |
| 86 | +- "How do I {task}?" title — phrased as a user question |
| 87 | + |
| 88 | +**Soft conventions:** |
| 89 | + |
| 90 | +- Quick start is ≤ 15 lines of copy-paste-runnable code |
| 91 | +- Caveats prefer per-platform bullets over prose |
| 92 | +- Related links: module page + sample + (optional) ADR |
| 93 | + |
| 94 | +A new cookbook topic gets its own subdir + an `index.md` topic index that |
| 95 | +lists the recipes + the underlying modules. See |
| 96 | +[`docs/cookbook/network-monitor/index.md`](cookbook/network-monitor/index.md) |
| 97 | +for the shape. |
| 98 | + |
| 99 | +## Files with special meaning |
| 100 | + |
| 101 | +| File | Used by | Purpose | |
| 102 | +|------|---------|---------| |
| 103 | +| `index.md` | mkdocs | Root URL of the site (`/`). | |
| 104 | +| `_partials/cookbook-recipe-template.md` | authors (manual copy) | The canonical recipe shape. Don't edit casually — every recipe inherits. | |
| 105 | +| `requirements.txt` | docs-publish workflow | Pinned mkdocs deps. Change a version here, not in the workflow. | |
| 106 | +| `stylesheets/mbs-brand.css` | mkdocs | Brand polish. | |
| 107 | + |
| 108 | +The `cmp-*/README.md` and `cmp-*/DEVELOPMENT.md` files at module roots are |
| 109 | +**not under docs/** but feed the docs pipeline (paths trigger the |
| 110 | +docs-publish workflow on push). |
| 111 | + |
| 112 | +## Excluded legacy directories |
| 113 | + |
| 114 | +These per-module docs subdirs predate the mkdocs site and use relative links |
| 115 | +that resolve only in the GitHub UI. They live in `docs/` for backward compat |
| 116 | +but are excluded from the mkdocs build via `mkdocs.yml` → `exclude_docs:`: |
| 117 | + |
| 118 | +``` |
| 119 | +docs/app-intents/ docs/bubble/ docs/clipboard/ |
| 120 | +docs/firebase-analytics/ docs/in-app-update/ docs/intent-launcher/ |
| 121 | +docs/inter-app-comms/ docs/network-monitor/ docs/open-url/ |
| 122 | +docs/pdf-generator/ docs/remote-config/ docs/share/ |
| 123 | +docs/toast/ docs/user-tickets/ docs/BUBBLE.md |
| 124 | +docs/CLIPBOARD_MONITOR.md docs/FEATURE_REQUEST.md |
| 125 | +docs/REMOTE_CONFIG.md docs/REMOTE_CONFIG_SAMPLES.md |
| 126 | +``` |
| 127 | + |
| 128 | +**Don't add new content to those directories.** Either: |
| 129 | + |
| 130 | +- New module-level docs → write a `cmp-*/README.md` (it becomes the source |
| 131 | + for `docs/modules/cmp-*.md` via include-markdown) |
| 132 | +- New how-to → write a cookbook recipe under `docs/cookbook/{topic}/` |
| 133 | +- New narrative → write under `docs/` root + register in `mkdocs.yml` nav |
| 134 | + |
| 135 | +When a legacy directory's content gets migrated to a current surface, drop |
| 136 | +its line from `exclude_docs:` in the same PR. |
| 137 | + |
| 138 | +## Adding new content |
| 139 | + |
| 140 | +### A new cookbook recipe |
| 141 | + |
| 142 | +1. Pick the topic subdir (or create one — see "A new cookbook topic" below) |
| 143 | +2. Copy `_partials/cookbook-recipe-template.md` → `cookbook/{topic}/{slug}.md` |
| 144 | +3. Fill in frontmatter + body (≤80 lines, ≥1 kotlin block) |
| 145 | +4. Add the entry to `cookbook/{topic}/index.md`'s recipe list |
| 146 | +5. **Don't** add individual recipes to `mkdocs.yml` nav — only the topic |
| 147 | + `index.md` is in nav; recipes are reached via the topic index |
| 148 | + |
| 149 | +### A new cookbook topic |
| 150 | + |
| 151 | +1. Create `cookbook/{topic}/index.md` listing the recipes + modules |
| 152 | +2. Add the entry to `mkdocs.yml` → `nav: Cookbook:` (one line per topic) |
| 153 | + |
| 154 | +### A new module landing page |
| 155 | + |
| 156 | +When you ship a new `cmp-*` module: |
| 157 | + |
| 158 | +1. Write `cmp-*/README.md` (the source of truth) |
| 159 | +2. Create `docs/modules/cmp-{name}.md` with an include-markdown that points |
| 160 | + at the README |
| 161 | +3. Add the entry to `mkdocs.yml` → `nav: Modules:` (alphabetical insertion) |
| 162 | + |
| 163 | +### A new narrative page |
| 164 | + |
| 165 | +Rare. Authored at `docs/{slug}.md` + registered in `mkdocs.yml` nav. |
| 166 | + |
| 167 | +## Style guide |
| 168 | + |
| 169 | +### Code blocks |
| 170 | + |
| 171 | +Always declare language. mkdocs-material renders Kotlin, Swift, Bash, YAML, |
| 172 | +JSON, TOML out of the box. |
| 173 | + |
| 174 | +````markdown |
| 175 | +```kotlin |
| 176 | +val monitor = createNetworkMonitor() |
| 177 | +``` |
| 178 | +```` |
| 179 | + |
| 180 | +Inline `code` for symbols, API names, flag names. |
| 181 | + |
| 182 | +### Per-platform caveats |
| 183 | + |
| 184 | +When behavior varies, structure as bullet list with bold platform name: |
| 185 | + |
| 186 | +```markdown |
| 187 | +- **Android:** auto-init via ContentProvider; no manual `init()` needed. |
| 188 | +- **iOS:** call `Bundle.main.URLForResource(...)` from `applicationDidFinishLaunching`. |
| 189 | +- **JVM Desktop:** prints to `System.out`; ANSI color enabled if TTY. |
| 190 | +- **JS / wasmJs:** requires a user gesture on first invocation. |
| 191 | +``` |
| 192 | + |
| 193 | +### Tables |
| 194 | + |
| 195 | +Use for any comparison with ≥ 3 dimensions. The 21-module index in |
| 196 | +[`index.md`](index.md) and the platform-support matrices are good examples. |
| 197 | + |
| 198 | +### Links |
| 199 | + |
| 200 | +- **Internal** (within `docs/`): relative paths. `mkdocs build --strict` |
| 201 | + validates these. |
| 202 | +- **Cross-repo source** (`workspaces/mbs/...`): downgraded to INFO-level |
| 203 | + warning via `mkdocs.yml` → `validation.links.not_found: info`. Expected. |
| 204 | +- **External**: full URLs. |
| 205 | +- **Maven Central / API reference**: prefer |
| 206 | + `https://central.sonatype.com/artifact/io.github.mobilebytelabs/cmp-{name}` |
| 207 | + over the Maven URL — better UX. |
| 208 | + |
| 209 | +## Test locally |
| 210 | + |
| 211 | +```bash |
| 212 | +pip install -r docs/requirements.txt |
| 213 | +mkdocs serve |
| 214 | +# open http://127.0.0.1:8000 |
| 215 | +``` |
| 216 | + |
| 217 | +`mkdocs build --strict` is what CI runs. Most common cause of strict |
| 218 | +failures: a new `cookbook/{topic}/{recipe}.md` added without an entry in the |
| 219 | +topic `index.md`'s recipe list (the relative link from the index breaks). |
| 220 | + |
| 221 | +## Recipe-freshness audit |
| 222 | + |
| 223 | +Every recipe has `reviewed_by.date` + `version` in frontmatter. Once per |
| 224 | +release cycle, scan for recipes whose `reviewed_by.version` is more than one |
| 225 | +minor behind current and re-verify their code blocks against the current API. |
| 226 | +Bump the date + version after each successful re-verification. |
| 227 | + |
| 228 | +(No CI gate on this yet; expected manual cadence is per-release.) |
| 229 | + |
| 230 | +## What NOT to do |
| 231 | + |
| 232 | +- **Don't hand-author `site/`** — that directory is the mkdocs build output. |
| 233 | +- **Don't add a new recipe outside the template format** — CI enforces the |
| 234 | + shape (line count + kotlin block presence). Use the template even for |
| 235 | + small recipes; consistency is the point. |
| 236 | +- **Don't write content into a legacy `docs/{module}/` subdir** — those are |
| 237 | + excluded from the build. Use `cmp-*/README.md` or `docs/cookbook/` instead. |
| 238 | +- **Don't edit the workflow** to change build behavior — the logic lives in |
| 239 | + `mbl-actionhub/docs-publish-mkdocs.yml`. Bump the `@vX.Y.Z` pin in |
| 240 | + `.github/workflows/docs-publish.yml` to upgrade. |
| 241 | +- **Don't author Liquid templating** in markdown (other than the |
| 242 | + `include-markdown` plugin's own directive). Liquid-style braces break |
| 243 | + rendering if Pages is ever set back to legacy Jekyll. |
| 244 | +- **Don't link recipes from `mkdocs.yml` nav directly** — keep nav to topic |
| 245 | + indexes only; recipes are reached via the index. Direct nav entries clutter |
| 246 | + the tab bar fast (12 recipes × 4 topics = 48 entries). |
| 247 | + |
| 248 | +## When the site breaks |
| 249 | + |
| 250 | +| Symptom | Cause | Fix | |
| 251 | +|---------|-------|-----| |
| 252 | +| `/` returns 404 | `docs/index.md` missing | Restore it. | |
| 253 | +| Build fails: nav references file that doesn't exist | Stale `mkdocs.yml` nav entry | Remove the entry or create the file. | |
| 254 | +| Cookbook recipe rejected by CI for length | Recipe > 80 lines | Split into two recipes OR move detail into a linked sample / ADR. | |
| 255 | +| Cookbook recipe rejected for missing kotlin block | All code blocks are bash / yaml / etc. | Add at least one ` ```kotlin ` block, even if a 3-line snippet. | |
| 256 | +| `mkdocs build --strict` fails on relative link | Cross-repo source link (`workspaces/mbs/...`) | Already downgraded to INFO via `validation.links.not_found: info`. If you're seeing ERROR, check that the link target literally cannot resolve in any way — even GitHub. | |
| 257 | + |
| 258 | +## Pipeline architecture (one-paragraph version) |
| 259 | + |
| 260 | +The mkdocs build + Pages deploy logic lives **once** in |
| 261 | +[`mbl-actionhub/docs-publish-mkdocs.yml`](https://github.com/MobileByteLabs/mbl-actionhub/blob/main/.github/workflows/docs-publish-mkdocs.yml). |
| 262 | +This repo's `.github/workflows/docs-publish.yml` is a 5-line caller pinned to |
| 263 | +a specific version. The wiki sync is a separate workflow |
| 264 | +(`sync-docs-to-wiki.yml`) that mirrors `docs/` to the GitHub Wiki via the |
| 265 | +`mbl-actionhub-docshub` composite action. The Dokka API reference is built |
| 266 | +inside the Maven publish pipeline (per-module `dokkaGeneratePublicationHtml` |
| 267 | +task, bundled into `-javadoc.jar` via `vanniktech.mavenPublish`'s |
| 268 | +`JavadocJar.Dokka("dokkaGeneratePublicationHtml")` config). All three |
| 269 | +pipelines are independent; a failure in one doesn't block the others. |
0 commit comments