# Docs Site
This is the Zola-based documentation site for Worktrunk, published at worktrunk.dev.
## Development workflow
The docs dev server starts automatically via the post-start hook. Find the port with `wt list statusline`.
For static builds with local URLs (e.g., testing with a simple HTTP server):
```bash
zola build --base-url "http://127.0.0.1:PORT"
```
### Verifying changes
**Text-only changes** (prose edits, content rewrites): Run pre-commit and provide the dev server link. Playwright verification is not required.
**Visual changes** (CSS, layout, templates, responsive breakpoints): Use Playwright MCP to verify before returning. Visual bugs often hide in CSS specificity, template inheritance, and responsive behavior.
Playwright workflow for visual changes:
1. Navigate to affected page(s)
2. Take a snapshot to verify rendered output
3. Iterate if the result doesn't match expectations
Common visual issues to check:
- Text positioning and visibility
- Spacing and alignment
- Regressions on nearby elements
- Responsive behavior (use `browser_resize`)
**Always include the dev server link** when returning after doc changes:
```
View changes: http://127.0.0.1:<port>
```
## Theme architecture
The docs use a standalone "warm workbench" theme. Key files:
| File | Purpose |
|------|---------|
| `templates/_variables.html` | CSS custom properties (colors, layout, typography) |
| `sass/custom.scss` | All styling, organized by section |
| `templates/base.html` | Head overrides, iOS viewport polyfill |
| `templates/index.html` | Homepage hero and animations |
| `templates/page.html` | Doc page TOC rendering |
### Layout system
The sticky header and TOC use **definitional CSS variables** so positions are always in sync:
```
--wt-header-height: 60px (includes border via box-sizing: border-box)
--wt-main-padding-top: 40px
TOC sticks at: calc(header + padding) = 100px
Anchor scroll-margin: same calculation
```
When either variable changes (including via media queries), all dependent values update automatically. This prevents the TOC from "jumping" when transitioning to sticky mode.
### Key technical decisions
1. **`box-sizing: border-box` on header** - Border is included in height, simplifying calculations
2. **`scrollbar-gutter: stable`** - Reserves scrollbar space to prevent layout shift on navigation
3. **IntersectionObserver intercept** - Ensures scroll-spy doesn't conflict with TOC styling
4. **Logo preload** - Prevents flash when navigating between pages
5. **WCAG AA colors** - `--wt-color-text-soft` is #78716a for 4.5:1 contrast
6. **iOS viewport polyfill** - Sets stable `--vh-full` variable for Firefox iOS/Chrome iOS (see `templates/base.html` for details on the jank issue and what we tried)
### Responsive breakpoints
Variables are overridden in media queries to maintain definitional correctness:
- **≤1024px**: `--wt-main-padding-top: 30px`
- **≤768px**: `--wt-header-height: 50px`, `--wt-main-padding-top: 20px`, TOC hidden
### Extending the theme
When adding new positioned elements:
- Use the layout variables rather than hardcoding pixel values
- Test anchor navigation to verify no visual jumps
- Check both with and without page scroll
## Command documentation
Command pages (e.g., `switch.md`, `merge.md`, `list.md`) are **generated from the CLI source code**. Each file has a **skeleton** (frontmatter) with a marker region that gets replaced by generated content.
### Generation mechanism
Each command page has this structure:
```markdown
+++
title = "wt list"
weight = 11
[extra]
group = "Commands"
+++
<!-- ⚠️ AUTO-GENERATED from `wt list --help-page` — edit cli.rs to update -->
[generated content here]
<!-- END AUTO-GENERATED from `wt list --help-page` -->
```
The END tag mirrors the source ID, enabling precise matching even with nested snapshot markers inside.
The frontmatter (title, weight, group) is preserved in each file. Everything between the START and END markers is generated by:
```bash
wt <command> --help-page
```
The generated content includes:
1. **Conceptual documentation** — from `after_long_help` in `src/cli.rs`
2. **Command Reference section** — the standard `--help` output
### Editing command docs
**To update command documentation, edit `src/cli.rs`**, not the markdown files directly.
- **Conceptual content** — Edit the `after_long_help` attribute on the command
- **Usage/options/examples** — Edit the clap attributes (`about`, `long_about`, doc comments on args)
After editing, run the sync test (which auto-updates out-of-sync pages):
```bash
cargo test --test integration test_command_pages_and_skill_files_are_in_sync
```
### Command documentation structure
Each command has three documentation pieces in `src/cli.rs`:
| Piece | Source | Purpose |
|-------|--------|---------|
| **Definition** | First `///` line | Short identifier for command lists |
| **Subdefinition** | Second `///` line (optional) | Adds context when relevant |
| **after_long_help** | `#[command(after_long_help = "...")]` | Full documentation |
**Where each appears:**
| Context | What's shown |
|---------|--------------|
| Command list (`wt --help`) | Definition only |
| Terminal `-h` | Definition in header |
| Terminal `--help` | Definition (header) + Subdefinition (under header) + after_long_help (after options) |
| Web docs | "Definition. Subdefinition." as lead paragraph, then after_long_help |
**Content principles:**
1. **Definition must be short** — It appears in command lists; keep it to a noun phrase or brief imperative.
2. **Subdefinition adds context** — Only include if it provides information the definition doesn't. If the definition is complete, omit the subdefinition.
3. **after_long_help must not repeat** — Start with NEW information that expands on or provides context for the definition. Each piece should add information the previous pieces didn't provide.
4. **after_long_help should mostly stand alone** — The opener should give context or purpose, not just continue with details that only make sense after reading the definition. Avoid non-sequiturs.
**Good patterns for after_long_help openers:**
- Explain the mental model: "Worktrees are addressed by branch name..."
- Contrast with similar tools: "Unlike `git merge`, this merges current into target..."
- State the use case: "Use when you're done with a feature branch."
- Explain what something is: "Shell commands that run at key points..."
- Describe relationship to other commands: "The building blocks of `wt merge`: commit, squash, rebase, push."
**Patterns to avoid:**
- Repeating the definition with different words
- Starting with details that assume context ("The table shows..." when there's no prior mention of a table)
- Leading with configuration defaults before establishing what the command does
- Non-sequiturs that jump to side-effects without context
### Example output expansion (wt list)
The `wt list` examples use **HTML comments + code blocks** that expand to full snapshot output. In `cli.rs`, you write:
`````
<!-- wt list -->
```console
wt list
```
`````
This renders differently in each context:
- **Terminal help (`--help`)**: HTML comment skipped, code block shows as dimmed `wt list`
- **Web docs (`--help-page`)**: Both are replaced with the standard template format:
```
<!-- ⚠️ AUTO-GENERATED from tests/snapshots/<snapshot_file> — edit source to update -->
{% terminal() %}
<span class="prompt">$</span> <span class="cmd">wt list</span>
<output...>
{% end %}
<!-- END AUTO-GENERATED -->
```
**The mapping** (in `tests/integration_tests/readme_sync.rs`):
| Placeholder | Snapshot File |
|-------------|---------------|
| `<!-- wt list -->` | `readme_example_list.snap` |
| `<!-- wt list --full -->` | `readme_example_list_full.snap` |
| `<!-- wt list --branches --full -->` | `readme_example_list_branches.snap` |
**To update example output:**
1. Edit test setup in `tests/integration_tests/list.rs` → `setup_readme_example_repo()`
2. Run tests to regenerate snapshots: `cargo test --test integration readme_example_list`
3. Accept snapshots: `cargo insta accept`
4. Sync docs: `cargo test --test integration test_command_pages_and_skill_files_are_in_sync`
The examples in `cli.rs` are just command stubs — the actual output comes from snapshots generated by integration tests. This ensures docs always match real CLI behavior.
### Code block convention
All shell commands use `$ ` prefix in `` ```console `` blocks. `convert_dollar_console_to_terminal()` (`src/docs.rs`, shared library function) converts them to terminal shortcodes. The sync test also runs this on hand-written docs.
| Detected pattern | Web output | Highlighting |
|------------------|------------|--------------|
| `$ ` commands | `cmd` parameter with `\|\|\|` delimiter | Full Syntect |
| No `$ ` | `console` → `bash` conversion | Syntect (no `$ ` prompt) |
All `$ ` commands go through the `cmd` parameter path for consistent Syntect highlighting. Multiple commands are joined by `|||`; the template splits and highlights each individually. Commands are wrapped in `<span class="cmd">` (CSS `::before` adds `$ `). Comment lines (`#`) are highlighted but not wrapped (no prompt).
**Placeholders in cmd values:** Tera (Zola's template engine) would interpret `{{ }}` in the `cmd` parameter as template expressions, and `"` would close the parameter string. Since Tera has no backslash-escape mechanism for string literals, these characters are replaced with text placeholders (`__WT_OPEN2__`, `__WT_CLOSE2__`, `__WT_QUOT__`) in the Rust conversion function. The terminal shortcode template replaces them back before Syntect highlighting. The sync test also strips them when generating skill reference files.
Hand-written docs can use either `console` fences with `$ ` (auto-converted by the sync test) or shortcodes directly.
### CLI and web compatibility
Content in `after_long_help` must work in **both** the terminal (`--help`) and the web docs:
- **Tables** — Work in both. Prefer tables over bullet lists for structured data.
- **Markdown links** — Work in both (`[text](/path/)`)
- **Code blocks** — Work in both
- **Raw HTML** — Avoid. Renders as raw text in terminal help.
### Post-processing for web docs
The `--help-page` generator in `src/main.rs` applies post-processing to transform CLI-friendly content into web-friendly HTML:
| CLI Source | Web Output |
|------------|------------|
| `` ```console `` with `$ ` | `terminal` shortcode with `cmd` parameter |
| `` ```console `` (no `$ `) | `` ```bash `` |
| `` `●` green `` | `<span style='color:#0a0'>●</span> green` |
| `` `●` blue `` | `<span style='color:#00a'>●</span> blue` |
| `` `●` red `` | `<span style='color:#a00'>●</span> red` |
| `` `●` yellow `` | `<span style='color:#a60'>●</span> yellow` |
| `` `●` gray `` | `<span style='color:#888'>●</span> gray` |
| `[experimental]` | `<span class="badge-experimental"></span>` (text via CSS `::after`) |
To add web-only styling for new content, edit `post_process_for_html()` in `src/help.rs` — not the markdown files.
Similarly, `md_help::colorize_status_symbols()` applies ANSI colors for terminal `--help` output.
### Subdoc expansion
Include subcommand documentation as H2 sections within a parent command's docs page using subdoc placeholders:
```markdown
<!-- subdoc: subcommand-name -->
```
In `cli.rs`, add the placeholder anywhere in the parent command's `after_long_help`:
```rust
after_long_help = r#"...main documentation...
<!-- subdoc: create -->
...more documentation..."#
```
This expands during `--help-page` generation to:
```markdown
## wt config create
### User config
[subcommand's after_long_help content with heading levels increased]
---
### Command reference
[subcommand's usage and options]
```
**How it works:**
- The placeholder is invisible in terminal `--help` output (HTML comment)
- Heading levels in the subcommand's `after_long_help` are increased by one (## → ###)
- The subcommand's help reference is formatted as a nested `### Command reference`
**Use cases:**
- `wt config create` — Shows the actual config file templates (via `include_str!`)
- `wt config state marker` — Shows per-key examples not in the parent
All AUTO-GENERATED markers use a consistent format with START and END tags:
```html
<!-- ⚠️ AUTO-GENERATED from <source> — edit <file> to update -->
[content]
<!-- END AUTO-GENERATED -->
```
For regions that may contain nested markers (like command pages with embedded snapshots), the END tag mirrors the source ID:
```html
<!-- END AUTO-GENERATED from `wt list --help-page` -->
```
This enables precise matching of the outer region.
## Template examples in documentation
**All template examples must have corresponding tests** in `tests/integration_tests/doc_templates.rs`. This catches issues like operator precedence bugs (PR #373).
When adding template examples, add a test that verifies the template produces expected output. See existing tests for patterns.
## Demo GIF workflow
Demo GIFs (~2MB each) are stored in a separate `worktrunk-assets` repo to avoid bloating git history. Both build and fetch output to `docs/static/assets/` (gitignored), so local builds override fetched assets.
**For local development:**
```bash
task fetch-assets # Download published assets
```
**To regenerate demos** (required after CLI output changes):
```bash
./docs/demos/build docs # Doc site demos (light + dark)
./docs/demos/build social # Social media demos (light only)
task publish-assets # Publish to assets repo
```
Deploy runs `fetch-assets` before building.
For detailed demo development guidelines (timing, debugging, environment setup), see `docs/demos/CLAUDE.md`.
## Social card workflow
Social cards follow the same assets pattern as demos.
**Source files:**
- `social-card.svg` (1200×630) — Open Graph/Twitter link previews, referenced in `base.html`
- `github-social-card.svg` (1280×640) — GitHub repository preview, uploaded manually in repo Settings → Social preview
**To regenerate** (after changing tagline, logo, or layout):
```bash
task build-social-cards # SVG → PNG (downloads fonts if needed)
task publish-assets # Publish to assets repo
```
The build script automatically downloads Inter and Plus Jakarta Sans fonts from GitHub if not installed locally. Requires `rsvg-convert` (from librsvg).
**Referenced in:** `docs/templates/base.html` (og:image, twitter:image meta tags)
### Light/dark theme variants
In markdown, use `<picture>` with media queries:
```html
<figure class="demo">
<picture>
<source srcset="/assets/docs/dark/wt-switch-picker.gif" media="(prefers-color-scheme: dark)">
<img src="/assets/docs/light/wt-switch-picker.gif" alt="wt switch picker demo" width="1600" height="800">
</picture>
</figure>
```
The browser automatically shows the appropriate variant based on system preference.