Skip to content

ux(loading): make every loading state match the content it replaces - #447

Open
yhabib wants to merge 1 commit into
mainfrom
ux/skeleton-loading-states
Open

ux(loading): make every loading state match the content it replaces#447
yhabib wants to merge 1 commit into
mainfrom
ux/skeleton-loading-states

Conversation

@yhabib

@yhabib yhabib commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Motivation

The app had six ways to show a load. Three of them showed the same three grey bars whatever was behind them: the /voting route, the proposal page, and the transaction list. /dashboard, /neurons, /accounts and /settings had no pending state at all, so they showed a blank frame. The anti flicker delay only ran inside QueryStates, so the 28 files with inline skeletons flashed on every fast query.

Changes

  • Added a skeletons/ set of content shaped shells, one per route plus the list, card, and row shapes they share, and wired them as pendingComponent on all six routes.
  • Moved the 120ms anti flicker delay into the .skeleton class, so every skeleton holds its space from the first frame and reveals itself late. Removed useDelayedFlag and MultipleSkeletons.
  • Fixed the skeletons that hid data already in hand or changed the card height when they left: CapitalCard, IcpPriceCard, TotalStakedCard, DailyRewardsCard, and the account and stakes cards.
  • Marked skeleton bars aria-hidden and added one role="status" per region, so a screen reader hears "loading" once instead of nothing.
  • Switched the 13 raw Loader2 and Loader icons to the shared Spinner, and the two processing halos to one ProcessingSpinner.
  • Turned off animate-pulse under prefers-reduced-motion, and left spinners turning.

The app had six ways to show a load. Three of them showed generic grey
bars that did not match the page behind them.

Replace them with one set of content shaped shells, move the anti flicker
delay into CSS so every skeleton gets it, and route the raw spinner icons
through the shared `Spinner`.
@yhabib
yhabib requested a review from a team as a code owner August 25, 2026 05:56
Copilot AI lite review requested due to automatic review settings August 25, 2026 05:56
@zeropath-ai

zeropath-ai Bot commented Aug 25, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to 134b698.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► src/governance-app-frontend/src/common/components/ProcessingSpinner.tsx
    Implement centralized processing spinner used during mutations
► src/governance-app-frontend/src/common/components/MutationDialog.tsx
    Replace AnimatedSpinner with ProcessingSpinner in loading state
► src/governance-app-frontend/src/common/components/QueryStates.tsx
    Switch loading placeholder to SkeletonText instead of MultipleSkeletons
► src/governance-app-frontend/src/common/components/skeletons/Skeleton.tsx updated to integrate new Skeleton styling conventions (via CSS classes)
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/SkeletonScreen.tsx
    New SkeletonScreen wrapper component for loading regions
► src/governance-app-frontend/src/common/components/skeletons/SkeletonPageHeader.tsx
    New SkeletonPageHeader matching PageHeader shape
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/SkeletonText.tsx
    New SkeletonText component to render placeholder text lines
► src/governance-app-frontend/src/common/components/skeletons/SkeletonStatCard.tsx
    New SkeletonStatCard for stat-like placeholders
► src/governance-app-frontend/src/common/components/skeletons/SkeletonAccountCard.tsx
    New SkeletonAccountCard for account list placeholders
► src/governance-app-frontend/src/common/components/skeletons/SkeletonTransactionList.tsx
    New SkeletonTransactionList and SkeletonTransactionRows for transaction placeholders
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/VotingSkeleton.tsx
    Add skeleton structure for voting page including ProposalListSkeleton
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/ProposalListSkeleton.tsx
    New skeleton for proposal list items
► src/governance-app-frontend/src/common/components/skeletons/NeuronsSkeleton.tsx
    New skeletons for Neurons page including content and header
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/DashboardSkeleton.tsx
    New comprehensive skeleton layout for dashboard matching sections (stat cards, accounts, staked, etc.)
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/AccountsSkeleton.tsx
    New skeleton to mirror Accounts page layout including header, cards, and activity
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/ProposalDetailSkeleton.tsx
    New skeleton for proposal detail page (summary and body)
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/ProposalListSkeleton.tsx
    New skeleton for list of proposals with rows including badges and vote bars
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/SettingsSkeleton.tsx
    New skeletons mirroring settings sections layout
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/Skeleton.tsx
    Refactor of skeleton base to support new skeleton classes and usage
Enhancement ► src/governance-app-frontend/src/common/constants/extra.ts
    Remove LOADING_INDICATOR_DELAY constant (loading delays handled via new skeleton system)
Bug Fix ► src/governance-app-frontend/src/features/account/components/AccountCard.tsx
    Adjust skeleton height from h-4 to h-5 during loading
Bug Fix ► src/governance-app-frontend/src/features/accounts/components/AccountsCard.tsx
    Adjust skeleton heights for loading placeholders (h-5, etc.) and consistency with new Skeleton components
Bug Fix ► src/governance-app-frontend/src/features/accounts/components/AccountsList.tsx
    Use SkeletonAccountCard and SkeletonScreen when accountsState is null (loading state)
Bug Fix ► src/governance-app-frontend/src/features/accounts/components/AccountsListItem.tsx
    Adjust balance skeleton height from h-4 to h-5 for alignment
Other Deleted file: src/governance-app-frontend/src/common/components/MultipleSkeletons.tsx
 - Removed in favor of new skeleton components and approach
Other Refactor/Replacement

@github-actions

Copy link
Copy Markdown

📊 Build Bundle Stats

The latest build generated the following assets:

dist/index.html                                           2.47 kB │ gzip:   0.85 kB
dist/assets/index-CoEvrqTO.css                          132.23 kB │ gzip:  21.57 kB
dist/assets/externalServices-CK4eoRia.js                  0.19 kB │ gzip:   0.16 kB
dist/assets/address-BGHb0VKO.js                           0.20 kB │ gzip:   0.15 kB
dist/assets/icpLedger-DNGzHrUv.js                         0.23 kB │ gzip:   0.21 kB
dist/assets/Skeleton-B2j6kc12.js                          0.43 kB │ gzip:   0.32 kB
dist/assets/useTvlValue-DNzhBTW7.js                       0.57 kB │ gzip:   0.39 kB
dist/assets/numbers-Dp6_OJhU.js                           0.58 kB │ gzip:   0.36 kB
dist/assets/service-CtvEAMRI.js                           0.59 kB │ gzip:   0.38 kB
dist/assets/PageHeader-Czh9Jiub.js                        0.77 kB │ gzip:   0.45 kB
dist/assets/CertifiedBadge-cYUjtBaQ.js                    0.81 kB │ gzip:   0.48 kB
dist/assets/useIcpIndex-CczyCsJO.js                       1.07 kB │ gzip:   0.63 kB
dist/assets/rolldown-runtime-CMxvf4Kt.js                  1.21 kB │ gzip:   0.67 kB
dist/assets/EmptyActionState-BH79D_XZ.js                  1.61 kB │ gzip:   0.83 kB
dist/assets/addressBook-vHRmCmc1.js                       1.66 kB │ gzip:   0.99 kB
dist/assets/useGovernanceAppCanister-D0_0fGFk.js          1.91 kB │ gzip:   0.96 kB
dist/assets/AnimatedNumber-DP-M_hkf.js                    1.96 kB │ gzip:   1.12 kB
dist/assets/useIcpIndexTransactionsPolling-CN323aoZ.js    2.03 kB │ gzip:   1.09 kB
dist/assets/advancedFeatures-DL21xLNc.js                  2.29 kB │ gzip:   1.08 kB
dist/assets/tickers-XjM2bOLg.js                           3.10 kB │ gzip:   1.48 kB
dist/assets/Switch-l-vqXXyi.js                            3.22 kB │ gzip:   1.44 kB
dist/assets/ToggleGroup-BB3wxsrn.js                       4.70 kB │ gzip:   1.76 kB
dist/assets/badge-DgZST727.js                             6.42 kB │ gzip:   2.22 kB
dist/assets/SensitiveValue-DGAeLvHc.js                    7.33 kB │ gzip:   3.29 kB
dist/assets/routes-ntW8ZrFY.js                            7.42 kB │ gzip:   2.45 kB
dist/assets/spamFilter-CAgVB7of.js                        8.54 kB │ gzip:   3.81 kB
dist/assets/MutationDialog-CWDB85yY.js                    9.54 kB │ gzip:   3.46 kB
dist/assets/events-Cd17XOIQ.js                           10.38 kB │ gzip:   4.11 kB
dist/assets/_id-D9d9YL_m.js                              14.41 kB │ gzip:   4.33 kB
dist/assets/_auth-Bt7UrzF6.js                            17.75 kB │ gzip:   6.53 kB
dist/assets/accounts-BUQQ4O9U.js                         23.31 kB │ gzip:   8.13 kB
dist/assets/settings-ClFAC-qI.js                         41.59 kB │ gzip:  11.91 kB
dist/assets/GetTokens-DrWVo-0W.js                        42.76 kB │ gzip:  15.21 kB
dist/assets/dashboard-EFBbCgOz.js                        50.44 kB │ gzip:  15.09 kB
dist/assets/button-CVTIMWeR.js                           58.44 kB │ gzip:  20.06 kB
dist/assets/neurons-X2vAl4Jb.js                          67.76 kB │ gzip:  20.32 kB
dist/assets/voting-BX7gB7Qn.js                           68.49 kB │ gzip:  21.18 kB
dist/assets/vendor-md-DEU2WssF.js                        73.29 kB │ gzip:  19.98 kB
dist/assets/neuronDetail-CXsxQQwi.js                     77.02 kB │ gzip:  22.87 kB
dist/assets/index-CSfaeAcy.js                            97.95 kB │ gzip:  28.10 kB
dist/assets/vendor-tanstack-2VpBmtA9.js                 161.18 kB │ gzip:  49.35 kB
dist/assets/vendor-core-react-qLCIanUQ.js               189.81 kB │ gzip:  59.76 kB
dist/assets/vendor-recharts-BhBmbfUy.js                 223.45 kB │ gzip:  64.00 kB
dist/assets/vendor-icp-D3_RTz5f.js                      411.38 kB │ gzip: 104.09 kB
dist/assets/vendor-libs-BWFEgkgW.js                     554.54 kB │ gzip: 181.01 kB

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.

Pull request overview

This PR standardizes loading/processing UI across the governance app frontend by replacing ad-hoc skeletons and loader icons with route-aligned skeleton screens, shared list/card/row skeleton primitives, and shared spinner components—while also improving accessibility (single loading announcement per region) and reducing flicker via a delayed skeleton reveal in CSS.

Changes:

  • Introduces route-shaped skeleton screens (and shared skeleton primitives) and wires them as pendingComponent / loadingComponent across major authenticated routes and dialogs.
  • Moves the anti-flicker delay from React logic into the .skeleton CSS utility; removes useDelayedFlag and MultipleSkeletons, and updates QueryStates defaults accordingly.
  • Replaces various Loader/Loader2 usages with the shared Spinner and consolidates mutation “processing” UI into ProcessingSpinner; adds reduced-motion behavior for skeleton pulsing.

Reviewed changes

Copilot reviewed 58 out of 58 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/governance-app-frontend/src/routes/_auth/voting/proposals/$id/index.tsx Uses proposal-detail skeletons for route pending + query loading state.
src/governance-app-frontend/src/routes/_auth/voting/index.tsx Uses voting and proposal-list skeletons for route pending + list loading/infinite-scroll loading.
src/governance-app-frontend/src/routes/_auth/settings/index.tsx Adds a route-level settings pending skeleton.
src/governance-app-frontend/src/routes/_auth/neurons/index.tsx Adds a route-level neurons pending skeleton and query loading skeleton.
src/governance-app-frontend/src/routes/_auth/dashboard/index.tsx Adds a route-level dashboard pending skeleton.
src/governance-app-frontend/src/routes/_auth/accounts/index.tsx Adds a route-level accounts pending skeleton.
src/governance-app-frontend/src/features/voting/components/VotingOverviewSimple.tsx Updates loading UI to use shared skeleton screen + page header skeleton.
src/governance-app-frontend/src/features/voting/components/VotingOverviewAdvanced.tsx Updates overview loading UI to use shared skeleton screen + page header skeleton; refines row skeleton sizing.
src/governance-app-frontend/src/features/voting/components/SimpleFollowingModal.tsx Replaces inline picker-row skeletons with shared SkeletonPickerRows.
src/governance-app-frontend/src/features/voting/components/KnownNeuronCard.tsx Replaces Loader2 with shared Spinner for loading state.
src/governance-app-frontend/src/features/voting/components/FolloweePicker.tsx Replaces inline picker-row skeletons with shared SkeletonPickerRows.
src/governance-app-frontend/src/features/voting/components/FollowedNeuronCard.tsx Adjusts skeleton width for loading label.
src/governance-app-frontend/src/features/voting/components/AdvancedFollowingModal.tsx Replaces inline topic-row skeletons with SkeletonTopicRows and swaps Loader2 for Spinner in the action button.
src/governance-app-frontend/src/features/transactions/components/TransactionListDialog.tsx Replaces generic skeletons with a transaction-list-shaped skeleton component.
src/governance-app-frontend/src/features/stakes/components/summaryCards/EngagementCard.tsx Adjusts skeleton sizing/spacing to match final content height more closely.
src/governance-app-frontend/src/features/stakes/components/summaryCards/EarningsCard.tsx Adjusts skeleton height to better match loaded typography.
src/governance-app-frontend/src/features/stakes/components/summaryCards/CapitalCard.tsx Prevents hiding already-available data; shows only USD line as skeleton while ticker loads.
src/governance-app-frontend/src/features/stakes/components/stakingWizard/StakingWizardStepConfirmation.tsx Replaces custom animated loader with shared ProcessingSpinner.
src/governance-app-frontend/src/features/stakes/components/StakedCard.tsx Adjusts skeleton heights to reduce layout shift.
src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailMaturityModeView.tsx Replaces Loader2 with shared Spinner during mutation pending state.
src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailIncreaseStakeView.tsx Replaces Loader2 with shared Spinner during mutation pending state.
src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailIncreaseDelayView.tsx Replaces Loader2 with shared Spinner during mutation pending state.
src/governance-app-frontend/src/features/stakes/components/neuronDetail/NeuronDetailDissolveView.tsx Replaces Loader2 with shared Spinner during mutation pending state.
src/governance-app-frontend/src/features/stakes/components/FollowingStatusAlert.tsx Replaces Loader2 with shared Spinner during mutation pending state.
src/governance-app-frontend/src/features/proposals/components/ProposalListItem.tsx Replaces Loader2 with shared Spinner for in-progress vote actions.
src/governance-app-frontend/src/features/proposals/components/ProposalDetailsVoting.tsx Replaces Loader2 with shared Spinner for in-progress vote actions.
src/governance-app-frontend/src/features/dashboard/components/TotalStakedCard.tsx Keeps card height stable by separating skeleton/value from the caption line.
src/governance-app-frontend/src/features/dashboard/components/IcpPriceCard.tsx Keeps card height stable while ticker change row loads; refines skeleton placement.
src/governance-app-frontend/src/features/dashboard/components/DailyRewardsCard.tsx Keeps card height stable while last reward row loads.
src/governance-app-frontend/src/features/addressBook/components/AddressBookModal.tsx Uses shared address-book row skeletons for the loading state.
src/governance-app-frontend/src/features/accounts/components/RecentTransactions.tsx Uses shared compact transaction-row skeletons for loading.
src/governance-app-frontend/src/features/accounts/components/AccountsTotalCard.tsx Adjusts skeleton heights to better match loaded typography.
src/governance-app-frontend/src/features/accounts/components/AccountsListItem.tsx Adjusts skeleton height for balance subtitle line.
src/governance-app-frontend/src/features/accounts/components/AccountsList.tsx Switches to shared account-card skeletons and wraps in SkeletonScreen.
src/governance-app-frontend/src/features/accounts/components/AccountsCard.tsx Tweaks skeleton sizing/consistency for account summary card.
src/governance-app-frontend/src/features/account/components/AccountCard.tsx Adjusts skeleton height for balance subtitle line.
src/governance-app-frontend/src/common/hooks/useDelayedFlag.ts Removes hook (anti-flicker now handled via CSS delayed skeleton reveal).
src/governance-app-frontend/src/common/constants/extra.ts Removes LOADING_INDICATOR_DELAY constant.
src/governance-app-frontend/src/common/components/skeletons/VotingSkeleton.tsx Adds route-shaped voting skeleton composition.
src/governance-app-frontend/src/common/components/skeletons/SkeletonTransactionList.tsx Adds list + compact-row transaction skeletons.
src/governance-app-frontend/src/common/components/skeletons/SkeletonText.tsx Adds reusable paragraph-like skeleton text blocks.
src/governance-app-frontend/src/common/components/skeletons/SkeletonStatCard.tsx Adds reusable stat-card skeleton used by dashboard/neurons.
src/governance-app-frontend/src/common/components/skeletons/SkeletonScreen.tsx Adds a single “loading” status region wrapper for skeleton groups.
src/governance-app-frontend/src/common/components/skeletons/SkeletonPageHeader.tsx Adds page-header skeleton matching PageHeader layout.
src/governance-app-frontend/src/common/components/skeletons/SkeletonListRows.tsx Adds reusable skeleton row sets for address book, pickers, and topic rows.
src/governance-app-frontend/src/common/components/skeletons/SkeletonAccountCard.tsx Adds account-card-shaped skeleton to match account list items.
src/governance-app-frontend/src/common/components/skeletons/SettingsSkeleton.tsx Adds route-shaped settings skeleton composition.
src/governance-app-frontend/src/common/components/skeletons/ProposalListSkeleton.tsx Adds proposal-list skeleton that matches proposal card layout.
src/governance-app-frontend/src/common/components/skeletons/ProposalDetailSkeleton.tsx Adds proposal-detail skeleton composition (page + content variants).
src/governance-app-frontend/src/common/components/skeletons/NeuronsSkeleton.tsx Adds neurons page + content skeleton compositions.
src/governance-app-frontend/src/common/components/skeletons/DashboardSkeleton.tsx Adds dashboard page skeleton composition.
src/governance-app-frontend/src/common/components/skeletons/AccountsSkeleton.tsx Adds accounts page skeleton composition.
src/governance-app-frontend/src/common/components/Skeleton.tsx Updates skeleton bars to use .skeleton styling and marks them aria-hidden.
src/governance-app-frontend/src/common/components/QueryStates.tsx Removes delayed-flag logic; relies on CSS-delayed skeleton reveal and updates default loading fallback.
src/governance-app-frontend/src/common/components/ProcessingSpinner.tsx Introduces shared mutation “processing” spinner halo component.
src/governance-app-frontend/src/common/components/MutationDialog.tsx Replaces dialog-specific animated spinner with shared ProcessingSpinner.
src/governance-app-frontend/src/common/components/MultipleSkeletons.tsx Removes old generic multi-bar skeleton helper.
src/governance-app-frontend/src/app/styles/main.css Adds skeleton color tokens and delayed reveal/pulse animations; disables pulse under reduced motion.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 16 to 21
<div
data-slot="skeleton"
className={cn('animate-pulse rounded-md bg-accent dark:bg-muted-foreground/20', className)}
aria-hidden={true}
className={cn('skeleton rounded-md', className)}
{...props}
/>
Comment on lines 44 to 49
if (q.isLoading) {
// Hold the frame empty until the delay elapses rather than flashing a skeleton.
return showLoading ? loadingComponent || <MultipleSkeletons count={3} /> : null;
// The skeleton holds the space at once and reveals itself after a short
// delay, so a fast query needs no hold-back here. See `.skeleton` in
// `main.css`. Pass a `loadingComponent` built from `Skeleton` to keep that.
return loadingComponent || <SkeletonText lines={3} />;
}
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