Split bundle into lazy chunks; compositor-only sticky-header collapse#99
Merged
Split bundle into lazy chunks; compositor-only sticky-header collapse#99
Conversation
Load time: - Vite manualChunks separates achievements, scoreboard/integrity/postmortem, and challenges/scenarios; each non-en locale emits its own chunk via import.meta.glob. target=es2022, cssCodeSplit=true, modulePreload=false. - main.ts switches to dynamic import() for achievements (via AchStub that queues events until the real tracker attaches), scoreboard, integrity, challenges, and scenarios. loadLanguage is async; top-level await keeps first paint on the selected locale. - Small Vite plugin injects link rel=prefetch for integrity+meta chunks and an inline head script that adds link rel=modulepreload for the user's locale so non-en paint fetches it in parallel with the entry. Scroll: - .sideHeader h1 collapse moves from max-height/margin/padding to transform+opacity (no layout work per scroll frame). - .sideBody mask-image replaced with a sticky ::before gradient. - backdrop-filter kept only on .canvasHud--actions (behind prefers-reduced- motion and min-resolution gates); place/platform/backlog HUDs use a more opaque color-mix surface instead. - #ticketList uses one delegated click listener; renderTickets no longer re-attaches per-button handlers on every signature change. - Non-header sparklines skip push() when the Overview tab is inactive. - contain: content on .ticket/.card/.regionRow/.scenarioRow/.achItem. New unit: achievements-lazy queue-then-flush behaviour.
- New tests/e2e/perf-invariants.spec.ts: no afterFirstPaint chunk starts before first-contentful-paint (via PerformanceObserver); language switch fetches the target locale chunk and repaints labels; sticky-header collapse runs on transform+opacity, not layout-driven properties. - afterFirstPaint() now subscribes to the paint PerformanceObserver and schedules its idle callback only after FCP fires, with rAF+setTimeout fallbacks. rAF x2 alone was racing the actual paint event, letting the lazy ach chunk start before FCP in headless runs. - manualChunks: drop postmortem from the integrity chunk (sim.ts imports it statically, which was dragging integrity onto the critical path); drop the ach assignment entirely (let Rollup auto-chunk achievements, otherwise shared constants from types.ts got co-packed into ach and forced the entry to statically import it back). - Updated the existing parallax test to match the compositor-only contract (h1.offsetHeight stays constant across scroll; transform + opacity are what animate).
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
app-survival-android | 943d741 | Commit Preview URL Branch Preview URL |
Apr 22 2026, 11:06 PM |
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.
Load time
Vite
manualChunksgroups integrity + scoreboard into one lazy chunk and challenges + scenarios into another. Achievements is left for Rollup to auto-chunk (manually assigning it pulled shared constants fromtypes.tsinto the chunk, forcing the entry to statically re-import them). Each non-en locale is its own chunk viaimport.meta.glob.target=es2022,cssCodeSplit=true,modulePreload=false(we hand-pick what to warm).main.tsswitches to dynamicimport()for achievements (viaAchStubthat queues events and flushes on attach), scoreboard, integrity, challenges, scenarios.afterFirstPaint()subscribes toPerformanceObserveronpaintso the chained import only fires once FCP has actually landed.loadLanguage()is async; top-level await gets first paint on the selected locale.Small Vite plugin injects
rel=prefetchfor the integrity and meta chunks, plus an inline<head>script that addsrel=modulepreloadfor the user`s locale so non-en paint fetches it in parallel with the entry.Entry: 117 KB raw / 37 KB gzip. Lazy: achievements 13 KB raw / 3.5 KB gzip, meta 4.3 KB / 1.8 KB gzip, integrity 2 KB / 0.9 KB gzip, per-locale 0.3-6.4 KB, shared i18n 18 KB / 6.4 KB gzip.
Scroll
.sideHeader h1collapse moves frommax-height+margin+paddingtotransform+opacityso scroll frames no longer trigger layout..sideBodymask-imagereplaced with a sticky::beforegradient.backdrop-filterkept only on.canvasHud--actions(behindprefers-reduced-motionandmin-resolutiongates); place/platform/backlog HUDs use a more opaquecolor-mixsurface.#ticketListuses one delegated click listener;renderTicketsno longer re-attaches per-button handlers on every signature change. Non-header sparklines skippush()when Overview is inactive.contain: contenton.ticket/.card/.regionRow/.scenarioRow/.achItem.Test plan
npm run test:unit(143/143 pass; newachievements-lazyqueue-then-flush coverage)npm run build(clean)npm run test:e2e:ci(25/25 pass on desktop)h1.offsetHeightis invariant across scroll