Dock the start-failure banner above the footer, not inside it - #2366
Merged
Conversation
…e it Closes #2365. Pressing Start with no players joined rendered "Cannot start" as a narrow column between the two footer buttons, wrapping mid-word — `beitrete` / `n`, `scanne` / `n`. Reported from a phone on v4.3.1-rc1. The banner was fine; the anchor was wrong. `showBanner` inserts its node *before the anchor, inside the anchor's parent*, so handing it the Start button put the banner **into** `.home-cta-bar`. That bar is a flex row and `.home-cta-start` claims the free space with `flex: 1`, leaving the banner at its min-content width — one word for a sentence, one character for a long word. "Above the button" and "before the button in a horizontal row" are the same DOM operation and two very different layouts. `showBanner`'s own docstring already said what was meant: "docks directly above the primary action". Three changes: * `bannerAnchorFor()` in admin/util.js resolves the Start button to its footer row, falling back to the button when there is no row and to null when there is no button (showSetupError reads that null to fall back to a toast). Extracted rather than inlined so the decision that caused the bug is unit-testable. * A guard on `.beatify-banner`: `flex-basis: 100%`. It has no effect outside a flex parent, so it costs nothing normally; should a future call site anchor a banner inside a flex row again, it claims its own line instead of being squeezed. * `flex-wrap: wrap` on `.home-cta-bar`, without which that guard cannot take effect. It also lets the buttons themselves wrap on a narrow phone rather than squeeze. Only one production call site reaches showBanner (showSetupError), and all four of its callers go through it, so this is the whole exposure. Tests: four in banner-anchor-2365.test.js. Verified against the broken behaviour — with the anchor returning the button, the first test fails and the three guard tests stay green, which is what they are for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012W9Pj7vqs7Yv31j19B4vRt
mholzi
added a commit
that referenced
this pull request
Aug 23, 2026
Bumps the manifest to 4.3.1-rc2, adds the changelog entry for what landed since the previous build, and adds the pre-release notes. Two changes since the last tag: the Tidal name fallback (#2364) and the start-failure banner layout fix (#2365 / #2366). The dead YouTube links repaired in #2362 are catalogue maintenance and stay out of the user-facing notes, as pre-release notes always do. The notes file is force-added — `docs/` is gitignored and every previous release-notes file was added the same way. Claude-Session: https://claude.ai/code/session_012W9Pj7vqs7Yv31j19B4vRt Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2365.
The bug
Pressing Start with no players joined rendered "Cannot start" as a narrow column between the two footer buttons, wrapping mid-word —
beitrete/n,scanne/n. Reported from a phone on v4.3.1-rc1.The cause
The banner was fine. The anchor was wrong.
showBannerinserts its node before the anchor, inside the anchor's parent. Handing it the Start button therefore did not put the banner above the button — it put it into the button's row:The banner became a third flex item, the Start button claimed the free space, and the banner was left at its min-content width — one word for a sentence, one character for a long word.
"Above the button" and "before the button in a horizontal row" are the same DOM operation and two very different layouts.
showBanner's own docstring already said what was meant: "docks directly above the primary action".The change
1.
bannerAnchorFor()inadmin/util.jsresolves the Start button to its footer row. Falls back to the button when there is no row, and tonullwhen there is no button —showSetupErrorreads thatnullto fall back to a toast, so it has to survive. Extracted rather than inlined so the decision that caused the bug is unit-testable; the file's existing helpers set that precedent.2. A guard on
.beatify-banner:flex-basis: 100%. No effect outside a flex parent, so it costs nothing in the normal case. Should a future call site anchor a banner inside a flex row again, it claims its own line rather than being squeezed.3.
flex-wrap: wrapon.home-cta-bar— without it the guard above cannot take effect, because a 100% basis still shares a line in a non-wrapping row. It also lets the buttons themselves wrap on a narrow phone rather than squeeze.Scope of the exposure
Checked, as asked: there is exactly one production call site for
showBanner—showSetupErrorinadmin.js— and all four of its callers funnel through it. The test files call it directly, which is fine. So this is the whole exposure, not a first instance.Tests
Four in
banner-anchor-2365.test.js. Verified against the broken behaviour rather than only the fixed one: with the anchor returning the button, the first test fails and the three guard tests stay green — which is exactly what they are for.Full suites: vitest 645 in 66 files, pytest 2083 passed / 2 skipped, eslint 0 errors.
npm run buildran (the bundles are in the diff).🤖 Generated with Claude Code
https://claude.ai/code/session_012W9Pj7vqs7Yv31j19B4vRt