Skip to content

Bound the command card to the screen so its buttons stay reachable - #157

Merged
Knorcedger merged 1 commit into
mainfrom
claude/mobile-hud-overlap-objectives-4hj5pv
Aug 23, 2026
Merged

Bound the command card to the screen so its buttons stay reachable#157
Knorcedger merged 1 commit into
mainfrom
claude/mobile-hud-overlap-objectives-4hj5pv

Conversation

@Knorcedger

Copy link
Copy Markdown
Owner

What changed?

The right-hand cluster is bottom-anchored with nothing bounding its height, so a card taller than the screen sent its excess off the top. Measured on an 844×390 landscape phone with a Town Center selected:

root height                 390
cluster / card            -147 .. 384   (531 tall)
first train button         -74 .. -30   ← above the viewport

The Q train button, both research buttons and the title were off-screen, nothing scrolled, and a player on that device could not train a villager or research anything from a Town Center at all.

The cluster now takes a measured max-height — from its own bottom up to whichever is lower, the top bar or the objectives panel's head — and the card scrolls inside it, top-anchored so the title and train row are what you see first. The bound is converted into the scaled stage's units, since the cluster lives inside the stage while every edge is measured on the unscaled root.

The queue's 140px reserve (26% of that card, empty) exists so chips appearing never shove the bottom-anchored card's train buttons under the player's thumb. A capped card scrolls, so its box cannot move and the reserve protects nothing — it drops to one row there.

Related issue

Closes #151

Why this approach?

Same contract as the two HUD fixes before it (#143, #144): each overlay publishes its real edges and the others size themselves from measurement. A breakpoint cannot answer this one either — the card's height depends on the selected building, the bar wraps, and the HUD scale setting moves both edges.

The bound reads the objectives head and never the panel's full height. The panel's list is already measured against this cluster, so feeding the list back in would close a layout loop; the head's height depends only on the objective text, which nothing here influences.

Dropping the reserve is judged against the card as it would be with the reserve restored. Measuring the shortened card would let a card that only just overflows un-cap itself, restore the reserve, overflow again, and flip every frame.

Alternatives considered: shrinking the 44px cells (the touch floor is not negotiable), and paginating the card (a redesign, not a fit). Scrolling keeps every control exactly where it is.

No simulation code is touched; this is all DOM HUD in packages/game.

Verification

  • npm run typecheck
  • npm test — 1391 passed, 11 skipped
  • npm run build (via npm run check, bundle budget included)
  • I added or updated tests where behavior changed
  • I included screenshots or a recording for visible changes

Driven in a browser on wallace-1 with a Town Center selected:

viewport cluster card reserve
844×390 106..384, capped at 278 434px, scrolls 44px
1280×800 263..794, natural 531 under a 688 bound fits 140px
390×844 309..840, natural 531 under a 692 bound fits 140px
844×390 @125% 118..383 scrolls 44px
844×390 @75% 104..385 scrolls 44px

offScreenPx: 0 and everyButtonReachable: true in all five; desktop is byte-for-byte the layout it had before.

A review of the first cut found five defects, each then confirmed fixed in the browser:

  • rebuildCard writes minHeight = '0px' rather than clearing it, so the "does this building queue?" test never matched — a villager got a phantom reserve, and every capped card was judged 96px taller than it was, defeating the hysteresis. It is a flag now (villagerReserve: 0px, still 0px across a viewport flip).
  • The card's scroll position was lost on rebuild, which the card key triggers on every train tap and every hp tick under attack — the just-tapped button moved out from under the thumb. Preserved now (60 → 60 with a chip added).
  • A classic scrollbar takes ~15px out of a 246px box holding a 236px grid of 44px buttons, clipping the fifth column. The bar is hidden; touch platforms overlay theirs anyway and a mouse still wheels (gridClipped: false, 5 columns).
  • The bound was applied from inside the cluster's own ResizeObserver, which is what produces "ResizeObserver loop completed with undelivered notifications". Its inputs each notify separately now (resizeObserverErrors: 0).

Provenance and AI assistance

Claude Code (Anthropic) wrote the implementation, tests and this description, driven and reviewed by the repository owner. No generated assets.

Contributor checklist

  • I searched issues and active pull requests before implementation and coordinated ownership in the linked issue
  • This pull request closes or references exactly one primary coordination issue
  • I read CONTRIBUTING.md and followed the deterministic simulation rules
  • I reviewed and understand every submitted change
  • I have the right to submit this code and any included assets
  • My commits are signed off under the Developer Certificate of Origin (git commit -s)
  • This PR contains no credentials, private player data, or unlicensed third-party material

Generated by Claude Code

The right-hand cluster is bottom-anchored with no bound of its own, so a
card taller than the screen sent its excess off the TOP. Measured on an
844x390 landscape phone with a town centre selected: a 531px card in a
390px root, its first train button at y -74..-30. The Q train button, both
research buttons and the title were above the viewport, nothing scrolled,
and a player on that device could not train a villager or research anything
from a town centre at all.

The cluster now takes a measured max-height — from its own bottom up to
whichever is lower, the top bar or the objectives panel's head — and the
card scrolls inside it, top-anchored so the title and train row are what
you see first. The bound is converted into the scaled stage's units, since
the cluster lives inside the stage while the edges are measured on the
unscaled root. It reads the objectives HEAD and never the panel's full
height: the panel's list is already measured against this cluster, so the
list would close a layout loop.

The queue's full 140px reserve exists so chips appearing never shove the
bottom-anchored card's train buttons under the player's thumb. A capped
card scrolls, so its box cannot move and the reserve protects nothing —
it drops to one row there, judged against the card as it would be with the
reserve restored so the two states cannot flip-flop.

Five fixes from review of the first cut, all confirmed in a browser:
rebuildCard writes '0px' rather than clearing minHeight, so the "does this
building queue?" test is a flag now instead of a style read that never
matched — a villager was getting a phantom reserve; the same broken test
was inflating every capped card by 96px and defeating the hysteresis; the
card's scroll position is preserved across a rebuild, which the card key
triggers on every train tap and every hp tick under attack; the scrollbar
is hidden, because a classic one takes 15px out of a 246px box holding a
236px grid of 44px buttons and clipped the fifth column; and the bound is
never applied from inside the cluster's own ResizeObserver.

Verified at 844x390, 1280x800 and 390x844, and at 75%/125% HUD scale:
nothing off-screen, every command button reachable, the desktop card
unchanged and still holding its full reserve, no ResizeObserver errors.

Closes #151

Signed-off-by: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
bannerfall Ready Ready Preview Aug 23, 2026 9:35am

Request Review

@Knorcedger
Knorcedger merged commit 3020939 into main Aug 23, 2026
3 checks passed
@Knorcedger
Knorcedger deleted the claude/mobile-hud-overlap-objectives-4hj5pv branch August 23, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Command card overflows the screen on a landscape phone: train and research buttons are unreachable

2 participants