The handoff stays on disk, not in the repo
The prototype ships a copy of thirty repo assets and the zip is a second copy again — a megabyte of duplicate art in the history for a document whose decisions now live in PROJECT.md and in the code. The folder is ignored, not deleted: it is still there to read. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
No files matched your search
@@ -13,3 +13,8 @@ promo/clips/
|
||||
# ios/.gitignore already keeps the copied web payload, Pods and Xcode's per-user junk out of it.
|
||||
node_modules/
|
||||
www/
|
||||
|
||||
# design hand-offs: read, built, and recorded in PROJECT.md — the prototype, its copy of the
|
||||
# assets and the zip do not belong in the image or the history (they are ~1 MB of duplicate art)
|
||||
design_handoff_*/
|
||||
*.zip
|
||||
@@ -1,242 +0,0 @@
|
||||
# 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.
|
||||
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 219 KiB |
|
Before Width: | Height: | Size: 714 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |