# Authoring rules for `docs/index.html`

> **TL;DR — Android is the spec. The HTML doc mirrors it. Don't invent.**

This doc exists because the team tried Figma first and the renderings drifted from code. The whole point of switching to HTML was to keep the spec accurate. Every visual replica must trace back to actual Compose source.

---

## Before you touch any component

For every component visualization (every `cv-*` CSS class, every replica frame in `index.html`):

1. **Open the Kotlin file.** It's the source of truth. Examples:
   - `packages/android/.../ui/components/hero/cards/HeroVideoCard.kt`
   - `packages/android/.../ui/components/hero/HeroCardShell.kt`
   - `packages/android/.../ui/components/cards/CardPoster.kt` (etc.)
   - `packages/android/.../ui/components/sections/ButtonGroupSection.kt`
   - `packages/android/.../ui/components/overlays/MenuDrawerOverlay.kt`
   - `packages/android/.../ui/components/BtvLogo.kt`
2. **Open the screenshot.** `docs/screenshots/<component>.png`. If there isn't one, capture one from the running app.
3. **Render only what's there.** No extra elements. No extra states. No invented colors.

## Tokens — never hand-pick values

Token defaults live in two places:

- `packages/design-tokens-api/tokens.json` — primitives + semantic + component defaults
- `packages/android/.../data/models/MediathekTokenModels.kt` — Android consumer wrappers (these are what the Kotlin reads from)

Match these exactly:

| Got from | Use this |
|---|---|
| Compose `tokens.heroCarousel.cornerRadius` | `border-radius: 24px` (token says 24dp) |
| Compose `tokens.heroCarousel.borderColor` (dark) | `border: 1px solid rgba(255, 255, 255, 0.15)` |
| Compose `tokens.heroVideo.contentTopPadding` | `padding-top: 140px` |
| Extracted `colors.gradientBase` | The actual extracted color (we use a representative dark teal in examples — annotate "dynamic per slide") |

**Rule:** If you can't trace the value back to `tokens.json` or `MediathekTokenModels.kt`, don't render it.

## Variable-first rule (added by 081-build-a-tokens)

The generator now emits `partials/component-tokens.css` with one CSS custom property per leaf in `tokens.json`'s component layer (`--t-{component}-{path}`) and the AI-gradient subset of the semantic layer (`--s-ai-gradient-{path}`). The doc's `<head>` includes this partial as a stylesheet, so the variables are available to every `.cmp-frame` instance.

**When styling a component visual replica in `spec.css`:**

1. **Prefer `var(--t-…)` or `var(--s-…)` over a hardcoded value.** If the value exists as a token, use the variable. Example:
   ```css
   /* WRONG — drift risk */
   .cv-shell { border-radius: 24px; }

   /* RIGHT — auto-syncs with tokens.json */
   .cv-shell { border-radius: var(--t-heroCarousel-cornerRadius); }
   ```

2. **Look up the available variables**:
   ```bash
   grep -E '^\s+--[ts]-' specs/080-create-a-plan/docs/partials/component-tokens.css
   ```
   This lists every `--t-*` and `--s-*` variable currently exposed.

3. **If the variable you need doesn't exist**, the fix is to add it to `tokens.json` and regenerate — *not* to hardcode it in `spec.css`. Some Kotlin defaults live in `MediathekTokenModels.kt` data classes but aren't serialized into `tokens.json`; in that case either (a) extend `tokens.json` if the value is meaningfully a design-system token, or (b) leave the literal in `spec.css` with a `/* not in tokens.json — kt default */` comment so the reason is visible.

4. **Never edit `partials/component-tokens.css` by hand.** It is regenerated on every run. Treat it as build output.

## Optional content stays optional

Many composables have conditional renders. Don't show optional pieces in the default replica:

- `HeroVideoCard` mobile: shows eyebrow + title + primary CTA. Description and secondary CTA are conditional. **Don't render them unless documenting that specific variant.**
- `MenuDrawer` nav rows have icons; toggle rows have switches. Both are real. The login button is always there. The "Mehr…" overflow only appears when items don't fit.
- `ButtonGroupSection`: single-AI-item case AND multi-item-with-overflow case both exist; show each separately, don't conflate.

When you DO want to document an optional state, give it its own frame and label it (e.g. "with description", "with progress bar").

## Anatomy must match the Compose tree

The visual hierarchy in CSS should mirror the Compose hierarchy. For HeroVideoCard mobile:

```
HeroCardShell (cornerRadius=24dp, 1dp border, dark-only shadow)
├── Image (full-bleed, ContentScale.Crop)
├── Box(fillMaxWidth, padding(paddingDefault=16dp))
│   └── BtvBadge(category)                    ← top of card, inside padding
└── Box(align=BottomCenter, fillMaxWidth, gradient bg, padding(top=140dp, sides=16dp, bottom=16dp))
    └── RegularHeroContent
        ├── eyebrow (optional)
        ├── title
        ├── description (optional)
        └── MediathekButton Primary Small + (optional) Secondary
```

Don't merge layers, don't position the badge "absolutely top-left of the card" because in Compose it's inside a padded Box at the top of the card. The position is the same but the conceptual structure is what gets translated to other platforms.

## Light + dark are NOT optional

Every component frame uses `data-mode="dark"` or `data-mode="light"`. Both must render. The `cmp-frame` CSS provides theme-scoped variables so you don't have to worry about page theme.

For components with mobile/tablet differences (HeroVideoCard, MenuDrawer, hero carousel), render BOTH viewport variants in BOTH themes — that's 4 frames per such component.

## Token tables are generated, not hand-edited

`docs/index.html` has marker comments like `<!-- TOKENS:PRIMITIVES:START -->` / `<!-- TOKENS:PRIMITIVES:END -->`. Everything between them is regenerated by:

```bash
node specs/080-create-a-plan/docs/scripts/generate-tokens.mjs
```

**Never hand-edit between markers.** If a token changes, edit `tokens.json` (or trace upstream to whatever generated `tokens.json`) and rerun the generator.

## Prose stays platform-neutral

The doc is a handover for Android, iOS, web, TV. Keep the body prose generic ("a single-pass measurement determines visible items"). When you need to mention a specific platform API, put it in a Platform Mapping callout (`<div class="platform-mapping">`), not in the main copy.

Examples of Android-isms to avoid in body prose: `Modifier`, `LazyRow`, `SubcomposeLayout`, `Compose`, `Box(align=...)`. These belong in the Platform Mapping for the Android cell.

## Screenshots are the visual truth

When in doubt about what the actual rendering looks like, the screenshots in `docs/screenshots/` are the truth. They were captured from the running Android app. If a CSS replica disagrees with a screenshot, the screenshot wins — fix the CSS.

If the Android app currently has a regression (e.g. `MenuDrawer` row backgrounds went from 5%-alpha tint to solid color via commit `4419c7fe`), document the target state in the schematic AND note the regression with a reference to the workstream B fix. Don't replicate the bug.

## Quick checklist before finishing a component

- [ ] Opened the `.kt` file
- [ ] Compared against the screenshot
- [ ] All token values traced to `tokens.json` / `MediathekTokenModels.kt`
- [ ] Anatomy matches the Compose tree
- [ ] Default state only — optional pieces not shown unless documenting that variant
- [ ] Both `data-mode="dark"` and `data-mode="light"` frames render
- [ ] Mobile + tablet variants where applicable
- [ ] No prose mentions framework APIs outside Platform Mapping callouts
- [ ] Token tables not hand-edited (use the generator)

---

# Part 2 — Lessons added during Iteration 1 authoring

The rules above cover the visual replica. The rules below cover the *prose* and *structure* of the spec — how to write component sections, document API contracts, and add diagrams.

## Source citation is mandatory, not decorative

Every numeric value, behavior rule, or "this happens when X" claim must end with a `file.kt:line` citation. Format:

```html
<span class="src-ref">HeroVideoCard.kt:175–186</span>
```

If you can't find a citation, you don't know the value — go read the code. **No approximation, no "around 16dp", no memory.** This rule exists because earlier drafts of the doc drifted enough to mislead an external implementer.

When the value is dynamic (changes at runtime), say so explicitly and cite the function that computes it: `rememberHeroVideoDynamics() · HeroVideoCard.kt:124–158`.

## Wireframes are proportional rectangles, not pixel-perfect replicas

Two approaches failed before settling on the current style:

1. **Isometric SVG mockups** — looked nice, but the proportions drifted from real layout numbers.
2. **Photo overlays with callout dots** — required pixel-perfect coordinate measurement that was always slightly off, and the overlays got rejected as misleading.

**Use proportional wireframes:** rectangles, generic gradient fills, numbered callouts (1–9), each callout key item ending with a `file.kt:line` citation. Two examples in the doc:

- `ButtonGroupSection` (single + default + multi-item-overflow)
- `HeroVideoCard` (mobile + tablet)

The wireframe shows *structure and proportion*. The callout key shows *values and citations*. The screenshot beside it shows *truth*.

## Icons are Material Symbols Rounded — write the symbol name, never the glyph

The codebase uses `androidx.compose.material.icons.Icons.Rounded.*` exposed via `MaterialSymbols.kt`. In wireframes and prose:

- ✅ `[auto_awesome]`, `[keyboard_arrow_down]`, `[play_arrow]`, `[cast]`, `[person]`
- ❌ ✨, ▾, ▶, ★, ☰, ⤡ (these are Unicode emoji / glyphs — they're not in the design system)
- ❌ FontAwesome class names

Whenever you find yourself reaching for an emoji to suggest an icon in an SVG label, grep the source first:

```bash
grep -rn "MaterialSymbols\." packages/android/app/src/main/java/com/protobible/android/ui/components/<component>/
```

Use the real symbol name. If the component doesn't use a Material Symbol at that position (e.g. it's a logo, a wordmark, or a static SVG), say what it actually is.

## API ↔ component mapping — match `CompositionModels.kt` 1:1

Every component that consumes data from the composition API needs a **"Composition API contract"** subsection with:

1. **The API engineer's annotation screenshot** as a figure (if one exists — see `docs/screenshots/api-*.png`), with a caption.
2. **A field-mapping table** matched 1:1 to `packages/android/app/src/main/java/com/protobible/android/data/models/CompositionModels.kt`.

Table columns:

| Column | Content |
|---|---|
| Field | The exact field name on `CompositionCard` / sibling class |
| Type | The Kotlin type (e.g. `CompositionBadge?`, `Int?`, `Map<String, String>`) |
| Notes | Verbatim German quote from the API engineer (in `<blockquote>` or `<em>`) + English unpack + line-cited link to the model |

**Rule:** Don't paraphrase the API engineer's notes — quote them verbatim in German with a translation underneath. The German is the contract; the English is the explanation. Both languages help reduce drift between API team and implementer team.

**Rule:** Every type in the table must match the actual Kotlin model declaration. Cite the line number (`CompositionModels.kt:116`). If the engineer's description and the model disagree, choose one of:

- A **"usage rule"** — the field exists on the wire but this specific component ignores it (example: `CompositionButton.backgroundColor` exists but HeroCard_Video uses Design Tokens).
- A **"rendering condition"** — the field is consumed but only under specific conditions (example: badge rendered only when `badge != null`).
- A **deferred clarification** — flag for the API engineer to resolve before contract freeze. Don't silently pick one.

**Rule:** The current Android implementation is not the API contract. If the contract says "hide progress bar at 0 and 100" but the code currently hides only at 0, document the **contract**, then add an "implementer alignment notes" footer that flags the gap.

## Verbatim German for API contract content

The product is German-first. When quoting the API engineer's annotations, the German text is the source of truth — keep it verbatim. Pattern:

```html
<blockquote style="margin:6px 0;padding-left:10px;border-left:3px solid var(--doc-rule);font-style:italic">
  "Falls User nicht eingeloggt / keine Infos zu diesem User vorliegen, wird das Property <code>user=null</code> (aber vorhanden) sein. Wenn der Wert 0 oder 100 ist, gibt es keine Leiste."
</blockquote>
→ If the user is logged out, or there's no per-user data for this item, <code>user</code> is sent as <code>null</code>. Bar hidden at <code>0</code> and <code>100</code>.
```

The English line starts with `→` and unpacks the German. Don't translate the German away.

## "Drift" vs "usage rule" vs "implementation gap"

Three different things, three different doc treatments:

| What you found | Treatment |
|---|---|
| Wire model has fields, but a specific component chooses not to use them | **Usage rule** — document the choice in the field row ("HeroCard_Video uses Design Tokens, ignores payload colors"). |
| API engineer's verbal description and the model don't agree (e.g. "optional" vs declared non-null) | **Drift** — flag for the API engineer to resolve. State both versions. |
| Documented contract and current code don't agree (e.g. contract says hide at 100, code renders at 100) | **Implementation gap** — footer note flagging the code line that needs to change. Doc still documents the contract. |

Don't conflate them. Each has a different owner and a different remediation path.

## Section structure — keep the order

Every component section follows the same structure. Don't reorder, don't skip:

1. **Heading** + brief description (1–2 sentences, platform-neutral)
2. **Anatomy** — wireframe(s) + callout key(s)
3. **Composition API contract** (if data-driven) — screenshot figure + field table
4. **Dimensions** — token-cited values table
5. **States** — `<dl class="state-grid">` with one entry per state
6. **Behavior** — `<div class="behavior-block">` per behavior
7. **Accessibility** — bullet list (semantic role, label, contrast, keyboard)
8. **Motion** — duration/easing tokens (or "static" if none)
9. **Audit notes** — what's been verified, what's flagged for follow-up
10. **Implementation reference** — file paths, key composables
11. **Component navigation** — prev/next links

Use the existing components in the doc as templates. ButtonGroupSection and HeroVideoCard are the most complete; copy their shape.

## Out-of-scope content is removed, not commented out

If a feature is in the code but not in the iteration scope:

- ✅ Remove it from the doc entirely.
- ❌ Don't leave it commented in HTML.
- ❌ Don't write "deferred for v2" — that's noise the agency doesn't need.

Example: the MenuDrawer prototype-toggle rows exist in `MenuDrawerOverlay.kt` but won't ship to the external agency. They were removed from the doc completely — states, behaviors, accessibility entries, wireframe rows, token references — even though the code retains them.

## When in doubt, discuss before editing

Some changes (renaming sections, removing existing wireframes, restructuring the TOC, bulk-replacing a class of content) are easy to over-shoot. Pattern: state the proposed delta as a bulleted plan, name the lines or sections affected, ask once, then execute. The doc has been over-deleted twice (greedy section-stripping scripts ate too much) — slow is faster than redo.

## Change-control summary

Every commit that touches the doc should be:

- **Small** — one component or one concern at a time.
- **Source-cited** — commit message references the `.kt:line` that drove the change, or the API engineer note that drove the change.
- **No invented values** — if you didn't find it in the code, you don't write it.

## Build + deploy

Source: `specs/080-create-a-plan/docs/handover.html` + sibling files.
Package: `packages/handover-doc/` (nginx static-site image, deployed via Coolify).
Trigger: push to the current handover branch → Coolify rebuilds → live URL updates.

To preview locally:

```bash
cd specs/080-create-a-plan/docs
python3 -m http.server 8080
# open http://localhost:8080/handover.html
```

To preview the full image:

```bash
docker build -f packages/handover-doc/Dockerfile -t handover-doc .
docker run --rm -p 8080:80 handover-doc
# open http://localhost:8080
```

