Skip to content

perf(startup): paint a static frame before the JavaScript arrives - #593

Open
yhabib wants to merge 1 commit into
mainfrom
perf/static-startup-frame
Open

yhabib wants to merge 1 commit into
mainfrom
perf/static-startup-frame

Conversation

@yhabib

@yhabib yhabib commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator

Motivation

A visitor sees an empty page until the JavaScript (470 to 560 KB gzip) arrives. Now the first paint needs only the HTML and the stylesheet, so the frame of the app shows about 2.9 s sooner.

Production build, local HTTP/2, slow 4G, 4x CPU, no cache, median of 5 runs at 360 px: FCP 4436 ms to 1572 ms, CLS 0.002 to 0.002. At 1280 px (3 runs): FCP 4424 ms to 1580 ms, CLS 0 to 0. LCP does not change (about 5.9 s), because the hero still needs the JavaScript.

Changes

  • Added a static frame to index.html: background and logo when logged out, sidebar and bottom nav when signed in.
  • Added public/startup-v1.js, which sets the theme and picks the frame from nns-theme, ic-session-state, and nns-advanced-features.
  • Added removeStartupFrameOnFirstCommit, which removes the frame in the same task as the first React commit.
  • Kept the login fade-up. The logo stays in place when React takes over, and the rest fades in as before.

Screenshots

Each screenshot shows the page before the JavaScript arrives.

Before After
Logged out at 360 px before Logged out at 360 px after
Signed in at 360 px before Signed in at 360 px after
Logged out at 1280 px, dark, before Logged out at 1280 px, dark, after
Signed in at 1280 px, dark, before Signed in at 1280 px, dark, after

The first paint now needs only the HTML and the stylesheet.
index.html holds a logged-out frame (background and logo) and a
signed-in frame (sidebar and bottom nav). public/startup-v1.js sets
the theme and picks the frame. React removes the frame on its first
commit, in the same task, so no blank page shows.

On a production build with slow 4G and 4x CPU at 360 px, FCP goes
from 4436 ms to 1572 ms. CLS stays at 0.002.
@yhabib
yhabib requested a review from a team as a code owner November 7, 2026 07:41
Copilot AI balanced review requested due to automatic review settings November 7, 2026 07:41
@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown

📊 Build Bundle Stats

The latest build generated the following assets:

dist/index.html                                          21.97 kB │ gzip:   5.59 kB
dist/assets/index-DHR8rj0Y.css                          150.06 kB │ gzip:  24.02 kB
dist/assets/icpLedger-Smqc3gZy.js                         0.23 kB │ gzip:   0.21 kB
dist/assets/useNowInSeconds-BEf6ZKyA.js                   0.43 kB │ gzip:   0.31 kB
dist/assets/Skeleton-DTNImVaY.js                          0.44 kB │ gzip:   0.32 kB
dist/assets/webhid-D0yio6wG.js                            0.53 kB │ gzip:   0.32 kB
dist/assets/advancedFeatures-DIvC9mk5.js                  0.53 kB │ gzip:   0.27 kB
dist/assets/useTvlValue-DPiNGTgg.js                       0.60 kB │ gzip:   0.41 kB
dist/assets/webVitals-DAQunrNq.js                         0.63 kB │ gzip:   0.46 kB
dist/assets/nnsDapp-DktCgKUB.js                           0.68 kB │ gzip:   0.25 kB
dist/assets/SkeletonPageHeader-TR6WniHc.js                0.76 kB │ gzip:   0.47 kB
dist/assets/PageHeader-jXzaR6sL.js                        0.77 kB │ gzip:   0.45 kB
dist/assets/CertifiedBadge-CalO5dxh.js                    0.81 kB │ gzip:   0.48 kB
dist/assets/SkeletonStatCard-D4V5-6nv.js                  0.81 kB │ gzip:   0.50 kB
dist/assets/Separator-SKt00d4v.js                         0.83 kB │ gzip:   0.50 kB
dist/assets/useForecastPeriod-crDdoIGy.js                 1.00 kB │ gzip:   0.60 kB
dist/assets/utils-DC9VwknB.js                             1.02 kB │ gzip:   0.59 kB
dist/assets/useIcpIndex-DUUb3vcn.js                       1.09 kB │ gzip:   0.64 kB
dist/assets/useSubaccountsMetadata-B2C7IU3l.js            1.10 kB │ gzip:   0.62 kB
dist/assets/numbers-Dc5JnHpd.js                           1.15 kB │ gzip:   0.57 kB
dist/assets/routeLoaders-BykauB21.js                      1.19 kB │ gzip:   0.56 kB
dist/assets/rolldown-runtime-DAXXjFlN.js                  1.26 kB │ gzip:   0.70 kB
dist/assets/useHardwareWalletsMetadata-CZeNZrtn.js        1.39 kB │ gzip:   0.69 kB
dist/assets/events-NOfvdv7_.js                            1.66 kB │ gzip:   0.86 kB
dist/assets/SkeletonListRows-COgAv2XF.js                  1.69 kB │ gzip:   0.53 kB
dist/assets/useLedgerNeuronCall-NgH1x_eu.js               1.90 kB │ gzip:   1.04 kB
dist/assets/errors-C_oENL0S.js                            1.95 kB │ gzip:   0.97 kB
dist/assets/AnimatedNumber-De2q0V82.js                    1.98 kB │ gzip:   1.13 kB
dist/assets/useCommandPaletteSettings-DXfHeH-0.js         2.12 kB │ gzip:   1.06 kB
dist/assets/addressBook-CfbQt3I7.js                       2.14 kB │ gzip:   1.20 kB
dist/assets/useIcpIndexTransactionsPolling-C_oiwUCk.js    2.17 kB │ gzip:   1.16 kB
dist/assets/useGovernanceAppCanister-CjkP8qCt.js          2.41 kB │ gzip:   1.07 kB
dist/assets/useShortcutSettings-BAsP1lRh.js               2.41 kB │ gzip:   1.19 kB
dist/assets/useAddHotkey-Camb__Ni.js                      2.61 kB │ gzip:   1.34 kB
dist/assets/governance-BlRffTdU.js                        2.99 kB │ gzip:   1.15 kB
dist/assets/ToggleGroup-BK11SisU.js                       3.09 kB │ gzip:   1.41 kB
dist/assets/useClaimPendingDeviceStakes-CjVNNgBi.js       3.12 kB │ gzip:   1.43 kB
dist/assets/SkeletonText-SnjaiyTf.js                      3.71 kB │ gzip:   1.30 kB
dist/assets/useAccounts-BrN8DLqT.js                       4.98 kB │ gzip:   2.21 kB
dist/assets/tickers-CaQqEUiV.js                           6.54 kB │ gzip:   2.73 kB
dist/assets/AlertDialog-BDhbfpZ3.js                       7.73 kB │ gzip:   2.72 kB
dist/assets/DeviceActionDetail-BI_1yvEK.js                8.45 kB │ gzip:   3.03 kB
dist/assets/TopicFollowingAccordion-BT9Jv2CU.js           8.55 kB │ gzip:   3.42 kB
dist/assets/routes-Ca2BBYpD.js                            8.96 kB │ gzip:   3.08 kB
dist/assets/ledgerIdentity-CbGbEww2.js                   10.16 kB │ gzip:   3.70 kB
dist/assets/MutationDialog-iSpxxodg.js                   11.55 kB │ gzip:   4.03 kB
dist/assets/spamFilter-DWn3arri.js                       11.77 kB │ gzip:   5.02 kB
dist/assets/ResponsiveDialog-BRSKq-s4.js                 14.56 kB │ gzip:   4.06 kB
dist/assets/_id-BNWPJFt9.js                              16.61 kB │ gzip:   5.32 kB
dist/assets/AmountInput-Ddxvl2Mr.js                      16.96 kB │ gzip:   6.77 kB
dist/assets/_auth-C5W_W8vq.js                            22.49 kB │ gzip:   8.29 kB
dist/assets/HideBalancesToggle-BKtzTG4o.js               22.99 kB │ gzip:   8.58 kB
dist/assets/vendor-buffer-EX-AqChq.js                    26.95 kB │ gzip:   8.18 kB
dist/assets/accounts-CwqceFda.js                         32.08 kB │ gzip:  10.90 kB
dist/assets/SendICPButton-BHHPmJqU.js                    37.11 kB │ gzip:  13.54 kB
dist/assets/settings-1XBZh4Xc.js                         54.47 kB │ gzip:  15.55 kB
dist/assets/dashboard-CMs8dHPP.js                        57.76 kB │ gzip:  19.10 kB
dist/assets/voting-Do_0uPI8.js                           69.29 kB │ gzip:  21.73 kB
dist/assets/index-BRwzzAlq.js                            69.75 kB │ gzip:  20.64 kB
dist/assets/vendor-md-DQa12sMs.js                        73.29 kB │ gzip:  19.98 kB
dist/assets/button-BCCED5Yq.js                           86.42 kB │ gzip:  29.03 kB
dist/assets/neurons-Cmy-SqjP.js                          90.51 kB │ gzip:  27.15 kB
dist/assets/DisburseModal-CnOYNe5P.js                   101.47 kB │ gzip:  29.63 kB
dist/assets/vendor-tanstack-D12Yy7ks.js                 120.02 kB │ gzip:  39.22 kB
dist/assets/vendor-jsqr-CjiBYtfG.js                     129.98 kB │ gzip:  47.50 kB
dist/assets/vendor-core-react-bRRjyX41.js               189.81 kB │ gzip:  59.76 kB
dist/assets/vendor-recharts-DQ8kALIQ.js                 280.26 kB │ gzip:  76.83 kB
dist/assets/vendor-ledger-Bz6feAP8.js                   403.26 kB │ gzip:  98.84 kB
dist/assets/vendor-icp-e5O_ocwA.js                      468.31 kB │ gzip: 117.84 kB
dist/assets/vendor-libs-BjQATLf3.js                     587.62 kB │ gzip: 189.56 kB

@zeropath-ai

zeropath-ai Bot commented Nov 7, 2026

Copy link
Copy Markdown

✅ No security or compliance issues detected. Reviewed everything up to 7d3ab15.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► src/governance-app-frontend/index.html
    Add startup frame script and static frame styles
► src/governance-app-frontend/public/startup-v1.js
    Add startup frame logic script
► src/governance-app-frontend/src/common/utils/startupFrame.ts
    Add removeStartupFrameOnFirstCommit utility
► src/governance-app-frontend/src/common/utils/startupFrame.test.ts
    Add tests for startup frame removal on first commit
► src/governance-app-frontend/src/main.tsx
    Invoke startup frame removal on mount
► src/governance-app-frontend/src/main.tsx
    Import and use removeStartupFrameOnFirstCommit

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

There are no active review issues for the static startup frame change.

No open issues 🎉

What changed in this PR

Adds a static startup frame so the governance app can display its background and navigation before the main JavaScript bundle loads, then hand over to React.

Changes:

  • Adds logged-out and signed-in frames with responsive navigation.
  • Selects the initial theme and frame from stored preferences and session expiry.
  • Removes the static frame when React first adds an element, with tests for the removal behavior.
File Description
src/​governance-app-frontend/​src/​main.tsx Registers frame removal before rendering React.
src/​governance-app-frontend/​src/​common/​utils/​startupFrame.ts Observes the root and removes the static frame on its first element insertion.
src/​governance-app-frontend/​src/​common/​utils/​startupFrame.test.ts Tests frame retention, removal, and missing-frame handling.
src/​governance-app-frontend/​public/​startup-v1.js Initializes theme, session-based frame selection, and navigation state.
src/​governance-app-frontend/​index.html Adds startup markup, styles, icons, and the initialization script.

Review effort: Balanced

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.

2 participants