From aab83dacdcba9f54391ac3c54899b662f08afbe2 Mon Sep 17 00:00:00 2001 From: Oliver Rieger Date: Mon, 31 Aug 2026 13:22:16 +0200 Subject: [PATCH] The menu deals itself in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The splash's elements fly in from the left on the boot, 70ms apart, on the same easeOutBack overshoot the canvas modals have always used. It hangs off :not(.busy), so taking the loading state off IS the trigger and it never plays again — returning from a run should not make you wait for a flourish you have seen. Reduced motion turns it off. Co-Authored-By: Claude Fable 5 --- PROJECT.md | 6 ++++++ index.html | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/PROJECT.md b/PROJECT.md index 4907a42..6324b65 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -595,6 +595,12 @@ can add the class, and Oliver kept seeing `LEVEL 1 · LEAGUE 1` snap to the real is in the HTML now and main.js only ever takes it off. The keyboard-hint strip hides with it (`body:has(#splash.busy) #hint`), so the loading screen really is court, title and bar. +**THE MENU DEALS ITSELF IN.** The canvas modals have always arrived on an easeOutBack overshoot; +the splash now does the same once, on the boot: plaque, fixture, OTHER GAMES, the pair, the build +line fly in from the left 70ms apart (`@keyframes dealIn`, hung off `#splash:not(.busy)` so the +class removal IS the trigger). It fires on the boot only — coming back from a run must not make +you wait for a flourish you have already seen — and `prefers-reduced-motion` turns it off. + **EVERY SCREEN OPENS AT ITS TOP.** `#intro` is the one scroller the menus share, so a scroll earned on the stats screen outlived it — go back and the splash came up scrolled, buttons off screen (Oliver). `go()` resets `intro.scrollTop`, at the single funnel every stage change already passes diff --git a/index.html b/index.html index 8732dd5..f0884dd 100644 --- a/index.html +++ b/index.html @@ -519,6 +519,29 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat /* the splash's bottom band: no bar, no rule — the court art has to keep running under it */ .foot.bare { border: 0; background: none; } .foot.bare .btn.big { margin-bottom: 10px; } + /* THE DEAL-IN (2026-08-31, Oliver: "es wäre toll, wenn sich die buttons nach dem laden aufbauen + würden — arcade style"). The canvas modals have always arrived on an easeOutBack overshoot + rather than decelerating politely into place; this is that same snap, in CSS, for the one + moment the menu has an entrance to make. It fires ONCE, on the boot: the rule hangs off + :not(.busy), so it triggers when main.js takes the loading state off and never again — coming + back from a run must not make you wait for a flourish you have already seen. */ + @keyframes dealIn { + from { opacity: 0; transform: translateX(-56px); } + 60% { opacity: 1; } + to { opacity: 1; transform: translateX(0); } + } + #splash:not(.busy) #plaque, + #splash:not(.busy) .foot.bare > * { + animation: dealIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) backwards; } + #splash:not(.busy) #plaque { animation-delay: 0.02s; } + #splash:not(.busy) #mStart { animation-delay: 0.12s; } + #splash:not(.busy) #mOther { animation-delay: 0.19s; } + /* the pair of secondary buttons is a CLASS, so it needs the same weight as the shorthand above + to override its delay — the id selectors around it get there on their own */ + #splash:not(.busy) .foot.bare > .two { animation-delay: 0.26s; } + #splash:not(.busy) #build { animation-delay: 0.33s; } + @media (prefers-reduced-motion: reduce) { + #splash:not(.busy) #plaque, #splash:not(.busy) .foot.bare > * { animation: none; } } /* the boot bar wears the stats screen's kill bar, doing duty as a loading bar. While #splash is .busy it stands in the buttons' place — same band, same height, so nothing jumps when it goes. */ #boot { display: none; margin-bottom: 10px; }