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>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-31 14:41:25 +02:00
1 parent 4c0ede33db
commit a331a1ae37
44 files changed
+3840 -187

No files matched your search

+11 -15
View File
@@ -7,7 +7,7 @@
// (beats <-> gameplay and beats <-> zombies are runtime-only import cycles, same pattern as
// input <-> gameplay: the imported bindings are only touched inside functions, never at load.)
import { TUNE, UI, bossArt, matchFoe, BENCH, ZFAMS, COURT, HOME, racketStage, leagueOf, cupRoundsFor, foeLevel } from './config.js';
import { TUNE, UI, bossArt, matchFoe, BENCH, ZFAMS, COURT, HOME, racketStage, leagueOf, cupRoundsFor, cupFixture, foeLevel } from './config.js';
import { endRun, MODE_MATCH } from './stats.js';
import { award, prof, save as profSave, push as profPush, setPromo, levelAt, recordCup } from './profile.js';
import { G, P, O, ball, score, setupPoint, sendBoy, other } from './state.js';
@@ -85,8 +85,8 @@ export function startDuel() {
showModal({ title: 'SUDDEN DEATH', color: UI.hot,
body: ["IT'S TENNIS AGAIN — ONE POINT", 'WIN IT AND THEY HAND YOU A WEAPON'],
// both stakes named (the old card only sold the win): a lost duel is a real cost
rules: [{ lead: "IT'S TENNIS AGAIN — ONE POINT", sub: 'WIN: THEY HAND YOU A WEAPON' },
{ lead: 'LOSE: THE DEAD TAKE A GAME', sub: 'AND FIVE MORE CLIMB THE FENCE', hot: true }] });
rules: ['WIN IT AND THEY HAND YOU A WEAPON',
{ lead: 'LOSE IT AND FIVE MORE CLIMB THE FENCE', hot: true }] });
} else G.flash = { text: 'SUDDEN DEATH', t: 1.6 };
}
@@ -394,17 +394,15 @@ export function startMatch(cup = false, draw = null, train = 0) {
showModal({ title: 'THE TRAINING COURT', color: UI.hot,
body: ['ONE BODY, ONE GAME AT A TIME',
'FELL IT — OR BLEED OUT — AND IT COMES BACK', 'NO RUN IS BANKED — REPS PAY QUARTER XP'],
rules: [{ lead: 'ONE BODY, ONE GAME AT A TIME', sub: 'FELL IT OR BLEED OUT — IT COMES BACK' },
{ lead: 'NO RUN IS BANKED', sub: 'REPS PAY QUARTER XP' }] });
rules: ['FELL IT OR BLEED OUT — IT COMES BACK', 'NOTHING IS BANKED · REPS PAY ¼ XP'] });
} else if (cup && !G.seen.cup) {
G.seen.cup = true;
showModal({ title: 'THE TOURNAMENT', color: UI.hot,
showModal({ title: 'THE TOURNAMENT', color: UI.hot, strap: cupFixture(G.league, prof.ch).sub,
// plain words (Oliver: "dead hierarchy" said nothing) — what you play, what winning buys
body: ['YOUR LEAGUE, ROUND BY ROUND — WIN IT ALL, MOVE UP A LEAGUE',
'ITS POINTS DRAW BLOOD — ' + TUNE.matchPlayerHp + ' AND YOU ARE EATEN'],
rules: [{ lead: 'YOUR LEAGUE, ROUND BY ROUND', sub: 'WIN IT ALL — MOVE UP A LEAGUE' },
{ lead: 'ITS POINTS DRAW BLOOD', sub: 'AT ZERO YOU ARE EATEN', hot: true,
pips: TUNE.matchPlayerHp }] });
rules: ['WIN IT ALL — MOVE UP A LEAGUE',
{ lead: 'POINTS DRAW BLOOD', hot: true, pips: TUNE.matchPlayerHp }] });
} else if (!cup && !train && !G.seen.match) {
G.seen.match = true;
// Oliver's copy direction: name the stake, count the blood. TUNE.matchPlayerHp is the real
@@ -416,15 +414,13 @@ export function startMatch(cup = false, draw = null, train = 0) {
? { title: 'DEATH MATCH', color: UI.hot,
body: ['WIN THE POINT — WOUND YOUR FRIEND',
'ITS POINTS DRAW BLOOD — ' + TUNE.matchPlayerHp + ' AND YOU ARE EATEN'],
rules: [{ lead: 'WIN THE POINT', sub: 'WOUND YOUR FRIEND' },
{ lead: 'ITS POINTS DRAW BLOOD', sub: 'AT ZERO YOU ARE EATEN', hot: true,
pips: TUNE.matchPlayerHp }] }
rules: ['WIN THE POINT — WOUND YOUR FRIEND',
{ lead: 'POINTS DRAW BLOOD', hot: true, pips: TUNE.matchPlayerHp }] }
: { title: 'THE CLIMB', color: UI.hot,
body: ['THE WHOLE CLUB, BOTTOM UP — ONE GAME PER BODY',
'ITS POINTS DRAW BLOOD — ' + TUNE.matchPlayerHp + ' AND YOU ARE EATEN'],
rules: [{ lead: 'THE WHOLE CLUB, BOTTOM UP', sub: 'ONE GAME PER BODY — HOW FAR CAN YOU GET?' },
{ lead: 'ITS POINTS DRAW BLOOD', sub: 'AT ZERO YOU ARE EATEN', hot: true,
pips: TUNE.matchPlayerHp }] });
rules: ['ONE GAME PER BODY — HOW FAR CAN YOU GET?',
{ lead: 'POINTS DRAW BLOOD', hot: true, pips: TUNE.matchPlayerHp }] });
}
matchOpponent();
matchReady();