Two additions to the screenshot-free writing standard, both owner- ruled during the admin-docs planning session: - Tone by audience gains the Administration tier, ending the 'not yet decided' limbo for admin docs: plain language like the user docs, plus what every control actually does (semantics, cost, consequences) before the reader clicks, and per-setting what/does/means/range coverage. Calibration in one line: user = what you see, admin = what it does, developer = how it works. The Developer section keeps its separate tone discussion for the deferred dev-docs pass. - New 'Promises need approval' section: no coming-soon / under- consideration / future-release language anywhere without the owner's explicit approval of that exact promise; speculative text found in the docs is reported, never quietly kept or quietly deleted. Provenance recorded (the 2026-11-02 device-promise removals; the Operations placeholders knowingly kept).
142 lines
7.1 KiB
Markdown
142 lines
7.1 KiB
Markdown
# Writing UI documentation without screenshots
|
||
|
||
Bookhoard's UI documentation uses no screenshots. This page is the standard
|
||
for writing them. The flagship example of the style is the
|
||
[filter system documentation](../user/filters/index.md): one system taught
|
||
once, the places it appears listed as a *pattern*, samples for every field in
|
||
a [generated reference](../user/filters/fields.md) that cannot drift from the
|
||
UI.
|
||
|
||
## Why no screenshots
|
||
|
||
Screenshots fail in every way the rest of this repo's rot is already gated
|
||
against:
|
||
|
||
- **They rot silently.** No gate can diff pixels against the running UI. A
|
||
renamed button makes the screenshot wrong and nothing fails.
|
||
- **They cannot be verified.** Shown text can: this app's UI is
|
||
server-rendered from `templates/*.templ`, so every stable label and heading
|
||
is a literal in the source — checkable, like routes and API examples are.
|
||
- **They die with a redesign.** Text keyed to *function* survives a
|
||
re-layout. Text keyed to a layout dies with its redesign.
|
||
- **They exclude** (screen readers, text search) and bloat diffs.
|
||
|
||
The one thing screenshots do better — "which button is it?" — text does via
|
||
**exact quoted labels** plus a semantic path, and the label gate then holds
|
||
that text against the source.
|
||
|
||
## The layering — what is load-bearing
|
||
|
||
Write every UI page in this order. The top layers are why users don't get
|
||
lost when the UI changes:
|
||
|
||
| # | Layer | Content | Lifespan |
|
||
| --- | --- | --- | --- |
|
||
| 1 | **The pattern** | What the thing *is*, what is invariant, every place the same pattern appears | Redesign-proof |
|
||
| 2 | **Function inventory** | What the user *can do*, phrased without control names | Redesign-proof |
|
||
| 3 | **How-to with examples** | Numbered flows: current label → the outcome it produces | Survives most changes |
|
||
| 4 | **Current labels** | Exactly quoted, bold | Gate-checked today |
|
||
| 5 | **Generated references** | Enumerables regenerated from source with a currency test | Cannot drift |
|
||
|
||
Labels are **tips, not the map**. Layer 3 may quote a label; layer 1 must
|
||
make the page followable even if that label is renamed next week.
|
||
|
||
## The page template
|
||
|
||
1. **The pattern first.** The concept, its invariants, and everywhere it
|
||
appears — "one system, three places". Never page cartography.
|
||
2. **Function inventory.** Capabilities without control names: "you can pin
|
||
any narrowing to a named filter you can reopen later".
|
||
3. **How-to with examples.** Each step: the label of what you interact with,
|
||
then the outcome ("the count at the bottom shows how many books will
|
||
match"). An example beats an adjective.
|
||
4. **Findability note.** One line telling the reader how to re-locate the
|
||
feature *by function again* after any UI change — deliberately
|
||
label-independent ("it's the drawer opened from any books or podcasts
|
||
list — the only place whose header can read Match ALL").
|
||
5. **Current labels.** Quoted exactly as rendered, one canonical spelling
|
||
per control ([arbitrated by the glossary](ui-controls.md)).
|
||
|
||
## Writing rules
|
||
|
||
- **Semantic placement, never spatial.** No "top-right" or "at the bottom of
|
||
the screen" talk. "Devices page → the **Register Device** button" survives
|
||
any re-layout.
|
||
- **One canonical label per control.** Two spellings of the same control is
|
||
a defect, not a synonym.
|
||
- **Status updates get a what-it-means table**, not a color description.
|
||
- **Screenshots are never load-bearing.** Default is none.
|
||
|
||
## Tone by audience
|
||
|
||
- **User Guide (`docs/user/`) — plain language, deliberately.** Short
|
||
sentences. Everyday words. No developer jargon; any term the reader
|
||
genuinely needs (say, *rule*) is explained once, in place, then used
|
||
consistently. The reader is assumed to know books, while the app's own
|
||
words are the only technical vocabulary allowed.
|
||
- **Administration (`docs/admin/`) — plain language, one layer deeper.**
|
||
Same bones as the user docs: task-first, what to click for what, exact
|
||
quoted labels. Added: every control also states what the click *actually
|
||
does* — semantics, cost, and consequences — before the reader clicks it.
|
||
The canonical example: **Force Rescan All Libraries** re-reads and
|
||
re-extracts every file and recomputes hashes, while the other scan
|
||
buttons are shallow scans that skip unchanged files — an admin should
|
||
know that before clicking, not after. Every setting states what it is,
|
||
what it does, what it means when that is confusing, and its valid range.
|
||
No API-client detail: request and response schemas live in the developer
|
||
docs. The calibration in one line: **user = what you see, admin = what
|
||
it does, developer = how it works.**
|
||
- **Developer section — tone is a separate discussion, not yet decided.**
|
||
Write it plainly and hand-off-rich as well; do not import opinions about
|
||
its voice until that pass happens.
|
||
|
||
## Generate what has a registry
|
||
|
||
Anything enumerable gets generated from source with a currency test, not
|
||
hand-typed. The precedent: every filter field is
|
||
[generated](../user/filters/fields.md) by `TestFieldReferenceDocIsCurrent`
|
||
from the same registry the menus render — the doc cannot disagree with the
|
||
UI. Hand-typed lists drift fastest; when no registry exists yet, say so in
|
||
the page and lean on the pattern layers.
|
||
|
||
## The gates
|
||
|
||
- `TestUIControlLabels` (`internal/docs/ui_labels_test.go`): every control in
|
||
the [glossary](ui-controls.md) must exist as a literal in
|
||
`templates/*.templ` (or `web/src` for TS-rendered surfaces, marked
|
||
`source: ts`). Renames fail `make test` in the same commit as the rename.
|
||
- `TestDocsRelativeLinks`: every relative link in the docs tree resolves to
|
||
a real file.
|
||
|
||
Naming a new control in prose? Add its glossary entry in the same commit.
|
||
Form labels inside one modal (Name, Description…) are cited inline and not
|
||
glossary-tracked; the glossary carries the controls people navigate by —
|
||
buttons, tabs, badges, page titles.
|
||
|
||
## The process rule
|
||
|
||
**A UI change is not finished until the docs audit runs.** Moved card,
|
||
renamed button, restructured page — all of it triggers: labels still
|
||
resolve, links still resolve, affected pages re-read against the new
|
||
surface. The pattern layers keep users oriented between audits; the audit
|
||
keeps layers 3–5 honest.
|
||
|
||
## Promises need approval
|
||
|
||
Docs never promise future behavior on the owner's behalf. No "coming soon",
|
||
"under consideration", "in a future release", or any other forward-looking
|
||
commitment gets written without the owner explicitly approving that exact
|
||
promise — and anything speculative found in the docs gets reported, not
|
||
quietly kept and not quietly deleted. (Rule origin, 2026-11-02: the owner
|
||
ruled out "Kindle and reMarkable are under consideration" and a "Custom
|
||
Calibre Columns (Future)" section — no such plans existed — while the
|
||
Operations guide's pre-existing "Coming Soon" placeholders were reported
|
||
and knowingly kept for the future operations-docs pass.)
|
||
|
||
## What this does not replace
|
||
|
||
The filter documentation (and the older filtering overview) is hands-off
|
||
exemplar territory — read it as the model, never restyle it here. New UI
|
||
surfaces (scanning, devices, settings, podcasts, the reader, collections)
|
||
are written this way from the start.
|