# Static website plan

## Overview

A landing page plus auto-generated documentation pages, all static HTML/CSS. No JavaScript frameworks. Hosted from `docs/`.

Markdown files in `docs.md/` remain the canonical source for documentation. A Python build script converts them to HTML and stitches in the shared layout. The landing page is hand-written HTML.

The logo uses blue and orange — the site's color palette follows from that.

## Structure

```
docs/
├── index.html                        # Landing page (hand-written)
├── pages/
│   ├── index.html                    # Docs hub (generated)
│   ├── getting-started.html          # Generated from docs.md/getting-started.md
│   ├── usage.html                    # Generated from docs.md/usage.md
│   ├── tools.html                    # Generated from docs.md/tools.md
│   ├── safety-and-sandboxing.html    # Generated from docs.md/safety-and-sandboxing.md
│   ├── skills.html                   # Generated from docs.md/skills.md
│   ├── customization.html            # Generated from docs.md/customization.md
│   ├── providers.html                # Generated from docs.md/providers.md
│   ├── reports.html                  # Generated from docs.md/reports.md
│   └── agentfs.html                  # Generated from docs.md/agentfs.md
├── css/
│   └── style.css                     # Single stylesheet
├── img/
│   └── logo.png                      # Copied from .media/
└── favicon.ico                       # Generated from logo via build script
build.py                              # Markdown→HTML build script (lives at repo root)
```

Slugs match the markdown filenames exactly (e.g. `safety-and-sandboxing.md` → `safety-and-sandboxing.html`).

## Build script (`build.py`)

A single Python script. Build dependencies are declared in a `[dependency-groups]` section in `pyproject.toml`:

```toml
[dependency-groups]
website = ["markdown", "pillow"]
```

Run with: `uv run --group website python build.py`.

The script:

1. Reads each `docs.md/*.md` file.
2. Converts markdown to HTML via the `markdown` library (with `fenced_code` and `tables` extensions).
3. Rewrites internal links: `.md` → `.html` (handles cross-references like `[Safety](safety-and-sandboxing.md)`).
4. Wraps the content in an HTML template with header, sidebar nav, and footer.
5. Writes output to `docs/pages/<slug>.html`.
6. Generates `docs/pages/index.html` (docs hub) from the nav config (see below).
7. Copies `logo.png` into `docs/img/`.
8. Generates `favicon.ico` from the logo (resize to 32x32 PNG, wrap as ICO via Pillow).

The landing page (`docs/index.html`) is **not** generated — it's hand-written and committed. The build script leaves it untouched.

### Nav config

The page list, grouping, and descriptions live in a single Python data structure at the top of `build.py`:

```python
NAV = [
    ("Basics", [
        ("getting-started", "Getting Started", "Installation, first run, what happens under the hood"),
        ("usage", "Usage", "One-shot mode, REPL mode, CLI flags, piping, exit codes"),
        ("tools", "Tools", "File ops, search, editing, web fetching, thinking, command execution"),
    ]),
    ("Configuration & Deployment", [
        ("safety-and-sandboxing", "Safety & Sandboxing", "Path resolution, symlink protection, command whitelisting"),
        ("skills", "Skills", "Creating and using SKILL.md-based agent skills"),
        ("customization", "Customization", "Project instructions, system prompt overrides, tuning parameters"),
        ("providers", "Providers", "LM Studio and HuggingFace configuration"),
        ("reports", "Reports", "JSON reports for benchmarking and evaluation"),
        ("agentfs", "AgentFS", "Copy-on-write filesystem sandboxing"),
    ]),
]
```

This is the single source of truth for navigation order, display names, and docs hub descriptions. No parsing from the README. Adding a page means adding a tuple here plus the markdown file.

### Link rewriting and validation

The build script finds all `href="...*.md"` patterns in the generated HTML and rewrites them to `.html`. After generation, it runs a broken-link check that:

- Scans all `href` attributes in `docs/pages/*.html`.
- Verifies that local file targets exist.
- Verifies that `#fragment` anchors resolve to an `id` attribute in the target page.
- **Exits with a non-zero status** if any broken links are found (fails the build).

## Landing page (`index.html`)

Top-down layout:

1. **Header** — Logo + nav links (Docs, GitHub).
2. **Hero** — Tagline ("A small, powerful CLI coding agent for open models"), one-liner description, and a terminal-style code block showing `uv tool install swival` then `swival "Refactor the error handling in src/api.py"`.
3. **Features grid** — 4 cards:
   - "Works with open models" — LM Studio auto-discovery, HuggingFace support.
   - "Sandboxed by default" — File access is scoped, commands are off unless you opt in.
   - "Tool-calling loop" — File ops, grep, edit, web fetch, structured thinking.
   - "Zero config" — Install, load a model, go.
4. **Quickstart** — The 4 steps from the README, as a compact numbered list with code blocks.
5. **Footer** — MIT license, GitHub link.

### Meta tags

All pages include:
- Standard `<meta>` (charset, viewport, description)
- Open Graph tags (`og:title`, `og:description`, `og:image`, `og:type`)
- Twitter card tags (`twitter:card`, `twitter:title`, `twitter:description`, `twitter:image`)

`og:image` and `twitter:image` use absolute URLs (`https://swival.dev/img/logo.png` or whatever the final domain is). The base URL is a constant at the top of `build.py` so it's easy to change. The build script injects these for docs pages. The landing page has them hand-written.

## Docs pages

Each docs page is generated from the corresponding markdown file. Same layout: header, sidebar nav, content area, footer.

The docs hub (`pages/index.html`) is generated from the `NAV` config in `build.py` — a grouped list of pages with descriptions.

### Sidebar nav (docs only)

Grouped into two sections to handle growth:

**Basics**
- Getting Started
- Usage
- Tools

**Configuration & Deployment**
- Safety & Sandboxing
- Skills
- Customization
- Providers
- Reports
- AgentFS

Active page is highlighted. Adding a new page means adding a markdown file to `docs.md/` and an entry to the nav config in `build.py`.

### Mobile nav

On narrow screens (< 768px), the sidebar becomes a **vertical list above the content**, not horizontal. Section headers are preserved. This avoids the awkward 9-item horizontal scroll.

## Design

- **Palette**: Blue (#1a5fb4) and orange (#d65d0e) on white (#ffffff). Dark text (#1a1a2e). Light gray (#f5f5f5) for code blocks and alternating sections. Orange adjusted from #e85d04 to #d65d0e for better WCAG AA contrast on white (ratio ~4.6:1). Blue on white is 5.6:1 — passes AA.
- **Typography**: System font stack (`-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`). Monospace for code (`"SF Mono", "Fira Code", "Cascadia Code", monospace`).
- **Code blocks**: Dark background (#1a1a2e), light text (#e0e0e0), subtle border. The hero terminal block gets a fake title bar with colored dots. No copy button (no JS) — users select manually.
- **Responsive**: Flexbox/grid. Max content width ~48rem. Sidebar collapses to vertical list on mobile.
- **Dark mode**: `prefers-color-scheme: dark` media query. Swap background to #1a1a2e, text to #e0e0e0, code blocks to #0d0d1a, adjust link colors for contrast on dark.

## What to skip

- No search (grouped sidebar is enough for 9 pages).
- No analytics.
- No cookie banners.
- No JavaScript at all (no copy buttons, no hamburger menu, no search).
- No custom fonts (system stack only).
- No 404 page (static hosting usually provides one, and there are only ~12 URLs total).

## Source of truth

`docs.md/*.md` files are canonical. `docs/pages/*.html` is generated output. The landing page (`docs/index.html`) and stylesheet (`docs/css/style.css`) are hand-written and committed.

Workflow for docs changes:
1. Edit `docs.md/whatever.md`.
2. Run `uv run --group website python build.py`.
3. Commit both the markdown change and the regenerated HTML.

## Implementation order

1. `docs/css/style.css` — full stylesheet (both light and dark mode).
2. `docs/index.html` — landing page with meta tags.
3. Add `website` dependency group to `pyproject.toml`.
4. `build.py` — the markdown→HTML build script with link rewriting and broken-link check. The script also copies the logo and generates the favicon.
5. Run the build script to generate all docs pages and assets.
6. Test:
   - Open in browser at desktop width — check layout, nav highlights, code blocks.
   - Resize to mobile width (< 768px) — verify sidebar becomes vertical list, content reflows.
   - Toggle dark mode (system preference) — verify colors, contrast, code block readability.
   - Build exits 0 (link checker passes, no broken links or fragments).
   - Spot-check 2-3 docs pages against their markdown source for content fidelity.
