From 0904ad76d103bb0bbed615b566ed7f1253972be4 Mon Sep 17 00:00:00 2001 From: Evan Date: Mon, 15 Jun 2026 20:29:14 -0700 Subject: [PATCH] Fix per-frame render jank from toggle-input-card focus (#4300) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Enabling **Starting Gold** (or **Game Timer** / **Gold Multiplier**) in the single-player / host-lobby modal made the page spend several ms of "Render" time **every frame** — for as long as the toggle stayed enabled. Disabling the option made it stop. ## Root cause Each `toggle-input-card`, on enable, auto-focused and selected its number input so you could type immediately: ```ts input.focus(); input.select(); ``` A focused/selected editable inside the modal keeps the browser doing layout/paint work every frame for as long as it stays focused. It reproduces for any of the toggle-input cards because they all auto-focus on enable, which is why Starting Gold, Game Timer, and Gold Multiplier all triggered it. > **Note on the earlier revision of this PR:** the first attempt passed `{ preventScroll: true }` to `focus()`, on the theory that scroll-into-view was the cause. It successfully stopped the scroll (verified: modal scroll container `scrollTop 0 → 0`), but the per-frame render cost remained. That ruled out scroll-into-view and proved the focused editable itself — not the scroll — was the trigger. ## Fix Remove the auto-focus entirely. Enabling a toggle no longer focuses its number input, and the per-frame render cost is gone. ## Trade-off You no longer get type-to-replace on enable — click the field before typing the value. Worth it to eliminate the per-frame render. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) --- src/client/components/ToggleInputCard.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/client/components/ToggleInputCard.ts b/src/client/components/ToggleInputCard.ts index 58bd77f3f..2cceb5296 100644 --- a/src/client/components/ToggleInputCard.ts +++ b/src/client/components/ToggleInputCard.ts @@ -1,4 +1,4 @@ -import { LitElement, PropertyValues, html, nothing } from "lit"; +import { LitElement, html, nothing } from "lit"; import { customElement, property } from "lit/decorators.js"; import { translateText } from "../Utils"; import { CARD_LABEL_CLASS, INPUT_CLASS, cardClass } from "./InputCardStyles"; @@ -28,19 +28,6 @@ export class ToggleInputCard extends LitElement { createRenderRoot() { return this; } - - protected updated(changedProperties: PropertyValues) { - if (!changedProperties.has("checked")) return; - const previousChecked = changedProperties.get("checked"); - if (previousChecked === false && this.checked) { - const input = this.querySelector("input"); - if (input) { - input.focus(); - input.select(); - } - } - } - private toOptionalNumber( value: number | string | undefined, ): number | undefined {