Files
splatter-tennis/design_handoff_menu_text_reduction/README.md
T
wuwiandClaude Fable 5 a331a1ae37 The menu text reduction: the design handoff, built
Sixty words on the wall became a dozen. The handoff's own summary of the rule:
Jersey 15 NAMES (screen titles, rooms, cards), Bangers CALLS exactly once per
screen (the fixture, the primary button, a modal headline), Silkscreen INFORMS
(numbers, labels, chips) — and a sentence survives only where it carries a
decision. Everything else moved into a bar, a pip or the modal that opens with
the mode.

  · Jersey 15 bundled subset (9 KB) and Bungee retired after one afternoon
  · SPLASH: the plaque is one row, the goal line is gone (the fixture button
    below said it already), the two doors carry a number each
  · PICK A GAME: rooms are one word, posters are their names, the fixture is a
    hero with the only second line — the whole wall fits one screen now
  · MATCH SETUP: unnumbered blocks, the meters' legend once instead of a
    description per court, no summary footer
  · DIFFICULTY: three pips instead of three sentences
  · TRAINING: the revanche is a full-width row, rungs are numbers, the list
    ends on the league that opens the rest of the ladder
  · VS: three paragraphs became YOU / HOST / JOIN
  · MODALS: one rule per row (the sub was the lead again in other words), the
    blood pool as pips on that same line, and the walk-on's two levels in ONE
    face at ONE size — a comparison drawn at two sizes is a headline with a
    footnote (Oliver)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 14:41:25 +02:00

243 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Handoff: Menüführung — Text reduzieren (Splatter Tennis)
## Overview
Die funktionalen Screens von Splatter Tennis (Splash, PICK A GAME, MATCH SETUP, DIFFICULTY,
TRAINING, VS A FRIEND und die In-Game-Modals) tragen heute mehr Text als eine Entscheidung
braucht — auf `PICK A GAME` rund 60 Wörter, verteilt auf drei Rubriken mit Straps plus sechs
Poster mit je einer Regelzeile. Das Ergebnis dieser Session: eine reduzierte Fassung aller
funktionalen Screens (~12–18 Wörter auf der Wand) und ein Wechsel der aufrechten Display-Schrift
von **Bungee** auf **Jersey 15**.
Gameplay, Stats-Screens und Canvas-HUD sind bewusst NICHT Teil dieses Pakets.
## About the Design Files
`Menues.dc.html` ist eine **Design-Referenz in HTML** — ein Prototyp, der Aussehen und Struktur
zeigt, kein Produktionscode zum Kopieren. Splatter Tennis ist Vanilla JS ohne Build-Step
(`CLAUDE.md`, Punkt 5: „Zero dependencies, no build step, no framework"), also wird die Fassung
in der bestehenden Umgebung nachgebaut: DOM-Menüs in `index.html` + `src/main.js`, Canvas-Modals
in `src/render.js`, Texte in `src/config.js`. Kein neues Framework, keine neuen Dateien ausser
der Schriftdatei.
Die Datei enthält mehrere Runden untereinander, neueste oben. Verbindlich ist **Runde 5**
(„GESETZT — ALLE SCREENS IN JERSEY 15"). Darunter liegen als Historie: Runde 4 (Schrift-Kandidaten
Bebas Neue / Oswald / Jersey 15), Runde 3a (derselbe Satz in Bebas), Runde 2 (Bungee vs. Bangers),
Runde 1 (die zwei Grundrichtungen) und ganz unten der **IST-Stand**, pixelgenau aus dem heutigen
`index.html` nachgebaut — nützlich als Vor/Nach-Vergleich.
## Fidelity
**High-fidelity.** Palette, Masse, Schriftgrössen und Copy sind aus dem Repo übernommen
(`index.html` CSS, `config.MODES` / `MENU` / `COURTS` / `LEAGUES` / `OPPONENTS`, `beats.js`
Modal-Payloads). Alle Sprites/Poster im Prototyp sind echte Repo-Assets. Übernahme darf 1:1
erfolgen.
## Die drei Register (die eigentliche Entscheidung)
| Rolle | heute | neu |
|---|---|---|
| benennen — Screen-Titel, Rubriken, Karten- und Poster-Namen | Bungee | **Jersey 15** |
| rufen — einmal pro Screen: Fixture, Primär-Button, Modal-Headline | Bangers | Bangers (unverändert) |
| informieren — Zahlen, Labels, Chips | Silkscreen | Silkscreen (unverändert) |
Begründung Bungee → Jersey 15: Bungee ist Single-Weight und sehr fett/blockig; Jersey 15 sitzt
auf demselben Pixelraster wie Silkscreen (wirkt als Schriftfamilie mit der Sprite-Art), läuft
proportional und deutlich leichter. Beide sind SIL OFL.
**Grössen-Umrechnung Bungee → Jersey 15: ca. +40 %** (Jersey sitzt tiefer im Kasten und läuft
leichter). Konkrete Werte im Abschnitt „Design Tokens".
## Schrift einbauen
1. `Jersey15-Regular.ttf` von Google Fonts holen, wie Bungee subsetten (`pyftsubset`, Befehl
steht in `PROJECT.md`) → `assets/fonts/Jersey15-Subset.ttf`.
2. In `index.html` das `@font-face` von Bungee ersetzen (`?v=` mitziehen wie bei den anderen drei):
```css
@font-face { font-family: 'Jersey 15'; font-weight: 400; font-display: block;
src: url('assets/fonts/Jersey15-Subset.ttf?v=87') format('truetype'); }
```
3. Alle `Bungee`-Vorkommen in `index.html` auf `'Jersey 15'` umstellen (Liste unten),
`Bungee-Subset.ttf` und `Bungee-OFL.txt` löschen, `Jersey15-OFL.txt` daneben legen.
4. `main.js?v=N` in `index.html` bumpen (`CLAUDE.md`, Punkt 4).
## Screens / Views
### 1 — Splash (`#splash` in index.html)
**Zweck:** Eintritt; die Plakette zeigt wer du bist, der Primär-Button das nächste Fixture.
Änderungen:
- **Plakette (`#plaque`)** wird eine Zeile statt vier Grid-Areas: Wappen links (50px), rechts
Name + `LEVEL 12` in einer Baseline-Zeile, darunter der XP-Balken mit der Ziel-Zahl rechts.
**`#plGoal` entfällt komplett** — „WIN THE CUP TO REACH LEAGUE 4" sagt der Fixture-Button
darunter schon. Grid-Areas `name/lv/bar/goal` werden zu einem Flex-Row mit zwei Spalten.
- Name: Jersey 15 21px, `#ccd7ca`, `letter-spacing:0.5px` (war Silkscreen 700 13px)
- `LEVEL 12`: Silkscreen 700 10px, `#bcd455` (war Bangers 26px als `#plLv` — der Ruf gehört
dem Button unten, nicht der Plakette)
- Balken unverändert (6px, `#2c4238` Rahmen, Gradient `#3f9560 → #bcd455 55% → #e8b23a`)
- **Fixture-Button (`#mStart`)** bleibt Bangers 26px; die zweite Zeile (`#msLine`) verliert das
Rundenpräfix: `ROUND ONE: THE STRAWBERRY SELLER` statt
`4 ROUNDS · ROUND ONE: THE STRAWBERRY SELLER`. → in `config.cupFixture` die `line` kürzen
(`rounds.length + ' ROUNDS · '` fällt weg; die Rundenzahl steht im Modal).
- **`#mOther`** verliert seinen Strap (`<i>CLIMB · TRAINING · GORE · A FRIEND</i>` löschen),
Label auf Jersey 15 19px.
- **`#mStats` / `#mRanks`**: Straps ersetzt durch je eine Zahl aus lokalem Stand —
`STATS / 4 812` (Kill-Count, wie heute) und `BOARD / BEST 612` (eigener Bestwert; **kein**
Weltrang, der Splash lädt `/data/scores.json` nicht). Label Jersey 15 16px, Zahl
Silkscreen 700 10px `#bcd455`.
- Logo, Slogan (`Fifteen. Thirty. Forty. Dead.`), `#build` unverändert.
### 2 — PICK A GAME (`#stmode` / `#modeList`, gebaut in `main.buildTops`)
**Zweck:** einen Modus wählen. Das ist der Screen, der „erschlägt".
- **Rubriken** (`#modeList .mgroup > header`) verlieren ihren Strap (`<i>`) und werden ein Wort:
`THE SEASON → SEASON`, `THE TRAINING → TRAINING`, `OUT OF COMPETITION → CASUAL`.
Jersey 15 17px, `#bcd455`, `letter-spacing:2px`. → `config.MENU`: `sub` der drei `group`-Rows
löschen, `group`-Text kürzen; in `main.buildTops` das `<i>` aus dem `header`-Template nehmen.
- **Poster** tragen nur noch den Namen — `<i>${card.sub}</i>` fällt in `main.banner()` weg.
Damit werden die `sub`-Felder in `config.MODES` für die Wand irrelevant (Aufbewahren lohnt:
die Modals nutzen dieselben Sätze).
- **Hero-Poster** (das Fixture, `mode:'cup'`) ist der einzige mit einer zweiten Zeile und dem
einzigen Ruf: Name in **Bangers 32px**, darunter die Fixture-Zeile in Silkscreen 10px
`#f0cfc4`. Höhe 142px. `.way`-Ribbon („THE WAY UP") unverändert.
- **Übrige Poster** in Jersey 15, Höhe gestaffelt statt einheitlich 148px:
- TRAINING-Raum: zwei Poster **nebeneinander**, je `flex:1`, Höhe 106px, Name 27px
- CASUAL-Raum: drei Poster gestapelt, Höhe 84px, Name 30px
- **Regeln, die wirklich anders sind, bleiben als Chip** (`.last`-Optik, Silkscreen 8px, acid,
`rgba(0,0,0,0.72)` Grund): `18 RUNGS` auf TRAINING, `LAST` auf dem zuletzt gespielten.
- Ergebnis: der ganze Wall passt in 844px (heute zwei Bildschirme). Gerechnet mit
Header 64 + Padding 14 + 142/106/84 + Gaps 17/8.
### 3 — MATCH SETUP (`#setup`)
- `<h3 id="s01">01 · PLAYER</h3>` / `02 · COURT` → Rubrik-Zeile wie auf der Wand:
Jersey 15 17px acid, Text `PLAYER` / `COURT`. Die Nummerierung entfällt (es sind zwei Blöcke
auf einem Screen, keine Schritte).
- Über der Platzliste steht die Legende **einmal**: rechts in der Rubrik-Zeile
`BOUNCE · PACE` (Silkscreen 8px, dim).
- **Platz-Zeilen** (`#courtList button.court`) verlieren `<i>${court.sub}</i>` — Name
(Jersey 15 19px) plus die zwei Meter, die dasselbe wortlos sagen. Bildhöhe 66 → 60px.
- **Gesperrte Plätze** (`.court.lock`) behalten Bild und Namen, statt einer Erklärung nur
`LEAGUE 4` in `#cf5a4e`, Silkscreen 9px.
- **Tour-Tiles**: `MEN'S SINGLES` / `WOMEN'S SINGLES` → `MEN'S` / `WOMEN'S`, Jersey 15 16px.
- **`#sSum`** (die Zusammenfassungszeile über SERVE UP) entfällt — alle drei genannten Werte
sind auf dem Screen sichtbar markiert.
- `SERVE UP` bleibt Bangers 26px auf `#a4231f`.
### 4 — DIFFICULTY (`#stsub` / `#modeSub button.chip`, aus `config.DIFFS`)
- Chip wird zweispaltig: Name links (Jersey 15 30px), **drei Pips** rechts (je 10×10px, kein
Radius) — 1/2/3 gefüllt. EASY/MEDIUM acid `#bcd455`, HARD `#cf5a4e`; leere Pips
`rgba(255,255,255,0.12)`.
- Kein Erklärtext. Padding 26px/16px, `gap:10px`.
- `LAST`-Chip bleibt oben rechts im gewählten.
### 5 — TRAINING (`#train` / `#trainList`, gebaut in `main.buildTrain`)
- `<h3>PICK YOUR OPPONENT<span>ONE GAME · GO AGAIN</span></h3>` entfällt — der Screen-Titel
`TRAINING` genügt.
- **Revanche-Tile** (`profile.prof.out`) wird eine **breite Zeile über dem Raster**: Kopf 52px
links, Name (Jersey 15 24px), darunter `PUT YOU OUT` (statt `PUT YOU OUT OF THE CUP`) in
`#e08a7c`, Chevron rechts. Rahmen `#a4231f`, Grund `rgba(164,35,31,0.16)`.
- **Übrige Rungs** bleiben das 2-spaltige Raster: Kopf 44px, Name Jersey 15 17px zentriert,
Rangnummer zweistellig (`01`, `02`, …) statt `No. 1` — Silkscreen 8px dim.
- **Letzte Kachel** ist ein Ziel, keine Liste: gestrichelter Rahmen `#2c4238`, `LEAGUE 4` und
`13 — 15` in `#4a5a52`. (Heute endet die Liste einfach am Band-Cap.)
### 6 — VS A FRIEND (`#vs` / `#vsPick`)
- Die drei `<h3>` mit Straps (`01 · YOUR NAME / ON THEIR SCOREBOARD`, `02 · HOST A MATCH /
THEY COME TO YOU`, `OR JOIN THEIRS / WITH THEIR CODE`) werden drei Wörter:
**`YOU` / `HOST` / `JOIN`**, Jersey 15 17px acid.
- Host-Buttons: `CLASSIC TENNIS` / `DEATH MATCH` → `CLASSIC` / `DEATH MATCH` nebeneinander,
Jersey 15 19px, Padding 20px.
- Join-Zeile: Code-Feld plus Button `GO` (statt `JOIN` — daneben steht die Rubrik JOIN),
Feld-Letterspacing 6px.
- `#vsErr`-Meldungen unverändert (`THE CODE IS FOUR LETTERS`).
### 7 — In-Game-Modals (`render.drawModal`, Payloads aus `beats.js`)
Die Box-Sprache aus dem 2026-08-31-Pass bleibt (`MODAL_PAD 14`, `MODAL_GAP 13`, `MODAL_BTN 30`,
`MODAL_FOOT 8`, Breite `min(W-44, 244)`, Fill `rgba(8,22,16,0.93)`, Edge `rgba(120,150,130,0.45)`,
Rules `rgba(120,150,130,0.22)`). Reduziert wird der **Inhalt einer Row**:
- **Eine Regel pro Row, keine Unterzeile.** Heute rendert `modalRows` pro `m.rules`-Eintrag
`lead` + `sub`; der `sub` formuliert in fast allen Fällen den `lead` nur um
(`YOUR LEAGUE, ROUND BY ROUND` / `WIN IT ALL — MOVE UP A LEAGUE`). Neu: **eine** Zeile, die
das Ergebnis nennt — `WIN IT ALL — MOVE UP A LEAGUE`. Also in `beats.js` die `rules`-Arrays
auf je einen String reduzieren und `modalRows` entsprechend vereinfachen; `m.body` bleibt als
Fallback unverändert.
- **Blut ist Pips, kein Satz**: Row `POINTS DRAW BLOOD` (gekürzt von `ITS POINTS DRAW BLOOD`)
plus `TUNE.matchPlayerHp` Pips **in derselben Zeile rechts**, statt Zeile + Unterzeile
(`AT ZERO YOU ARE EATEN`) + Pips darunter.
- **Masthead**: Titel Bangers 27px in der Modal-Farbe, darunter EINE Silkscreen-10px-Zeile.
Beim Rules-Modal ist das die Sachzeile (`LEAGUE 3 · 4 ROUNDS`) — hier landet die Rundenzahl,
die aus `cupFixture.line` genommen wurde. Beim Walk-on bleibt es `foe.line`
(`CREAM WITH THAT?`).
- **Walk-on** (Gegner): Kopf-Sprite 56px, Name, `foe.line`, dann EINE Row mit den zwei Leveln
gegenüber (`LEVEL 14` hot links, `YOURS 12` dim rechts) — statt zwei Textzeilen plus
Split-Figur.
- Footer-Button unverändert (`WALK ON` / `GOT IT` / `PLAY ON`, `m.btn` überschreibt).
### 8 — Quit-Modal (`#qAsk` / `.qBox`)
Unverändert übernehmen — es ist schon lean. Nur `LEAVE THE MATCH?` von Silkscreen 12px auf
**Bangers 20px** (es ist eine Frage, die ruft), `PLAY ON` auf Jersey 15 17px.
## Interactions & Behavior
Keine Änderung an Navigation oder Zustandslogik. Alles Bestehende bleibt:
- `go(stage)` / `#intro[data-stage]`, ein Overlay, vier Panels
- Tap auf ein Poster antwortet UND geht weiter (`main.buildTops`), `pickMode` schreibt
`localStorage.mode`
- Die Deal-in-Animation (`#intro.deal`, `dealIn` 0.28s `cubic-bezier(0.34,1.56,0.64,1)`,
Delays 0.02/0.09/0.16/0.23/0.30s, `.mgroup`-Staffel 0.09/0.17/0.25s) gilt weiter — die
Rubriken bleiben `<section class="mgroup">`, damit sie greift
- `prefers-reduced-motion: reduce` schaltet sie weiter ab
- Modal-Dismiss-Regeln unverändert (`m.in < 1` schluckt die Gesture, Tap überall entlässt,
`G.seen.<key>` gated)
- Swipe-links-zurück im Shell bleibt unberührt
## State Management
Nichts Neues. Zwei Werte werden neu GELESEN, beide existieren schon:
- `profile.prof.out` (der Rung, der dich aus dem Cup geworfen hat) → Revanche-Zeile; wird
bereits von `buildTrain` genutzt
- der eigene Bestwert für die BOARD-Kachel → aus `stats` (lokal), **nicht** vom Server
## Design Tokens
Unverändert aus `index.html`:
```
--ink #0e1714 --panel rgba(42,62,54,0.42) --line #2c4238
--acid #bcd455 --cream #ccd7ca --dim #6c7f74
--red #a4231f --bad #cf5a4e --solid #16211d
Poster-Veil: linear-gradient(0deg, rgba(6,10,8,0.94) 0%, rgba(6,10,8,0.86) 22%,
rgba(6,10,8,0.5) 58%, rgba(6,10,8,0) 100%) — Höhe 52–62 % je Kachelhöhe
Modal: fill rgba(8,22,16,0.93) · edge rgba(120,150,130,0.45) · rule rgba(120,150,130,0.22)
Rahmen: 2px überall in den Menüs, 1px im Canvas-Modal
```
Typografie neu (Bungee-Wert → Jersey-15-Wert):
```
Screen-Titel (.hdr h2) 23px → 33px/1 letter-spacing 1px
Rubrik / Raum-Label 15px → 17px letter-spacing 2px
Poster-Name, Hero — → Bangers 32px/0.95 (der eine Ruf)
Poster-Name, TRAINING-Paar 17px → 27px/1
Poster-Name, CASUAL-Stapel 19px → 30px/1
Karten-/Platz-Name 15px → 22px
Difficulty-Chip 22px → 30px
Training-Tile-Name 12px → 17px/1
Sekundär-Button 13px → 18px
Zahlen, Labels, Chips Silkscreen 8–10px (unverändert)
Primär-Button Bangers 26px (unverändert)
```
## Assets
Alle Assets im Prototyp stammen aus dem Repo, unverändert — nichts muss neu gezeichnet werden:
`assets/logo.png`, `menu_bg.png`, `mode_cup|arcade|train|gore|classic|vs.png`,
`court_clay|hard|grass|gloom.png`, `badge_l3.png`, `z_{sh,bb,sp,gk,rm,st,ma,ru,sb,sg,lj,cu}head.png`,
`fonts/{Silkscreen-Regular,Silkscreen-Bold,Bangers-Regular}.ttf`.
NEU nötig: `fonts/Jersey15-Subset.ttf` (Google Fonts, SIL OFL) — ersetzt `Bungee-Subset.ttf`.
## Files
- `Menues.dc.html` — der Prototyp. Runde 5 oben ist verbindlich; darunter Runde 4 (Schriftwahl),
3a (Bebas-Fassung), 2 (Bungee vs. Bangers), 1 (Grundrichtungen), IST-Stand.
- `support.js` — Runtime des Prototyps, für die Implementierung irrelevant.
- `assets/` — die Repo-Assets, die der Prototyp lädt.
Betroffene Repo-Dateien: `index.html` (CSS + Markup der Panels), `src/main.js`
(`banner`, `buildTops`, `buildSub`, `buildTrain`, `refreshSplash`), `src/config.js`
(`MENU`-Gruppen, `cupFixture.line`, ggf. `MODES.sub`), `src/render.js` (`modalRows`,
`modalLayout`), `src/beats.js` (die `rules`-Payloads).
## Vor dem Commit
`node test.js` muss grün sein (`CLAUDE.md`, Punkt 2) — die Fixture-Deskriptor-Tests hängen an
`cupFixture`, also die `line`-Kürzung dort mit anpassen. `main.js?v=N` bumpen.