Skip to content

refactor(ui): show pill navbar only on root pages#474

Open
sahilc0 wants to merge 7 commits intomasterfrom
wt-navbar-root-only-20260325
Open

refactor(ui): show pill navbar only on root pages#474
sahilc0 wants to merge 7 commits intomasterfrom
wt-navbar-root-only-20260325

Conversation

@sahilc0
Copy link
Copy Markdown
Contributor

@sahilc0 sahilc0 commented Mar 25, 2026

Summary

  • Show navbar only on Wallet home, Apps home, and Settings menu — sub-pages (Send, Receive, Settings options, etc.) no longer show the navbar
  • Hide navbar on unlock and loading screens — the page === screen guard ensures the navbar is hidden whenever the rendered page is overridden (unlock, loading hold)
  • Add "Sounds good" button to success screens — since the navbar is hidden on sub-pages, Send/Receive/Notes success screens now have a primary action button to navigate back to wallet home
  • Accessibility: inert on hidden navbar — when the navbar is hidden, it's removed from the accessibility tree and tab order
  • Spring animation (Framer Motion) for navbar show/hide transitions — bounces down on exit, springs back up on enter
  • Remove all manual padding workarounds — footer clearance CSS, conditional 4rem padding in ButtonsOnBottom, manual pill-navbar-clearance padding in Delegates.tsx
  • Bonus: cherry-picked VITE_DEV_NSEC auto-init feature (separate commit) for faster local dev iteration

Why

The pill navbar was a fixed overlay (position: fixed, portaled to document.body) that required every component with bottom-positioned elements to manually account for its height. This was brittle — three CSS variables, automatic padding on IonContent, margin on IonFooter, conditional padding in ButtonsOnBottom, and manual padding in Delegates.tsx.

Since all action buttons exist on sub-pages and never on root pages, hiding the navbar on sub-pages eliminates the conflict entirely.

The navbar was also appearing on the Unlock screen because the visibility check used screen from NavigationContext (which could still be Pages.Wallet) instead of the actual rendered page. Adding page === screen as a guard fixes this.

Success screens (send, receive, notes) became dead ends after hiding the navbar on sub-pages — no navbar, no back button, no way out. Added a "Sounds good" ButtonsOnBottom button on each that navigates back to wallet home, following the same pattern as Init/Success.tsx.

Note for reviewers

The isSettingsRoot check in App.tsx couples navbar visibility to OptionsContext.option. This means if someone refactors Settings sub-navigation, they need to be aware it affects navbar visibility. Worth considering whether this should live in the navigation system instead (e.g., expose isRootPage from NavigationContext). @pietro909 thoughts?

Test plan

  • Wallet home: navbar visible, content scrolls with fade effect
  • Tap Send/Receive: navbar bounces out, action buttons at bottom work (especially Skip on Receive)
  • Go back: navbar springs back in
  • Apps home: navbar visible, tap any app → navbar hides
  • Settings menu: navbar visible, tap any option → navbar hides, buttons at correct position
  • Back to Wallet/Apps: navbar reappears with spring animation
  • Unlock screen: navbar NOT visible
  • Loading screen: navbar NOT visible
  • Send success: "Sounds good" button visible, tapping it returns to wallet home
  • Receive success: "Sounds good" button visible, tapping it returns to wallet home
  • Notes success: "Sounds good" button visible, tapping it returns to wallet home
  • Dark mode: navbar styling correct
  • Safe area: bottom spacing correct on notch devices

Summary by CodeRabbit

  • Style

    • Navbar now appears conditionally based on current screen, with improved show/hide behavior and smoother animations that respect reduced-motion preferences.
    • Refined layout spacing in the delegates view and adjusted pill-navbar interactivity when hidden.
  • New Features

    • Added a consistent "Sounds good" bottom action on various success screens that returns users to the wallet.
  • Tests

    • Added tests covering navbar visibility across authentication and navigation states.

sahilc0 added 2 commits March 25, 2026 13:56
When VITE_DEV_NSEC is set in .env.local, automatically initializes the
wallet on dev server startup, bypassing onboarding and unlock screens.
The pill navbar was a fixed overlay requiring manual padding on every
component with bottom-positioned elements. Since all action buttons
live on sub-pages (Send, Receive, Settings options), hiding the navbar
on sub-pages eliminates the conflict entirely.

- Show navbar only on Wallet home, Apps home, and Settings menu
- Hide with spring animation (Framer Motion) on sub-page navigation
- Remove footer clearance CSS hacks and manual padding workarounds
  in ButtonsOnBottom and Delegates
- Disable pointer-events on hidden navbar to prevent click blocking

Files: App.tsx, PillNavbarOverlay.tsx, ButtonsOnBottom.tsx,
       Delegates.tsx, index.css, ionic.css
@sahilc0 sahilc0 requested review from bordalix and pietro909 March 25, 2026 12:59
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0e6e614e-b4cd-494f-9727-1d2ace587c49

📥 Commits

Reviewing files that changed from the base of the PR and between 2fe4100 and 9018fcb.

📒 Files selected for processing (3)
  • src/screens/Wallet/Notes/Success.tsx
  • src/screens/Wallet/Receive/Success.tsx
  • src/screens/Wallet/Send/Success.tsx

Walkthrough

App now computes navbar visibility from navigation + settings option and passes a new visible prop to PillNavbarOverlay; the overlay uses framer-motion for show/hide animations and CSS was adjusted; tests and a few success screens add bottom buttons navigating back to Wallet.

Changes

Cohort / File(s) Summary
App / Navbar wiring
src/App.tsx
Read option from OptionsContext; added isSettingsRoot and showNavbar; set has-pill-navbar based on showNavbar; pass visible={showNavbar} to PillNavbarOverlay.
Pill navbar component
src/components/PillNavbarOverlay.tsx
Added required visible: boolean prop; replaced static layer with motion.div; animate opacity/y based on visible; respect useReducedMotion(); apply inert when hidden.
Styles
src/index.css, src/ionic.css
Added rule to disable pointer-events when layer is --hidden; removed ion-footer.buttons-on-bottom margin-bottom offset for has-pill-navbar.
Screens — spacing & buttons
src/screens/Settings/Delegates.tsx, src/screens/Wallet/.../Success.tsx
Delegates: replaced bottom padding calc with fixed 24px. Wallet Receive/Send/Notes Success: added bottom ButtonsOnBottom with a "Sounds good" button that navigates to Pages.Wallet.
Tests / mocks
src/test/App.test.tsx
Updated IonApp test mock to accept className; extended renderApp helper for screen/tab; stubbed env in tests; added navbar visibility test suite asserting has-pill-navbar presence.

Sequence Diagram

sequenceDiagram
    participant App as App.tsx
    participant Nav as PillNavbarOverlay
    participant Motion as Framer Motion

    App->>App: compute isSettingsRoot & showNavbar (screen + option)
    App->>Nav: render with visible = showNavbar

    alt visible = true
        Nav->>Motion: animate to {opacity: 1, y: 0}
        Motion-->>Nav: mount visible layer
        Nav->>Nav: ensure interactive (no inert / pointer-events)
    else visible = false
        Nav->>Motion: animate to {opacity: 0, y: 24}
        Motion-->>Nav: collapsed/hidden
        Nav->>Nav: apply inert and hidden class (disable pointer-events)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • pietro909
  • bordalix
  • tiero
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: restricting pill navbar visibility to root pages (Wallet home, Apps home, Settings menu) rather than showing it on all pages.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wt-navbar-root-only-20260325

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 25, 2026

Deploying wallet-bitcoin with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9018fcb
Status: ✅  Deploy successful!
Preview URL: https://d838a397.wallet-bitcoin.pages.dev
Branch Preview URL: https://wt-navbar-root-only-20260325.wallet-bitcoin.pages.dev

View logs

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 25, 2026

Deploying wallet-mutinynet with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9018fcb
Status: ✅  Deploy successful!
Preview URL: https://77abdacf.arkade-wallet.pages.dev
Branch Preview URL: https://wt-navbar-root-only-20260325.arkade-wallet.pages.dev

View logs

sahilc0 added 2 commits March 25, 2026 14:03
- Keep master's authState/unlockWallet additions in App.tsx and wallet.tsx
- Keep master's .scroll-fade opt-in selector for scroll mask
- Remove footer clearance rule (our change) that master re-added
@arkanaai
Copy link
Copy Markdown

arkanaai bot commented Mar 25, 2026

🔍 Arkana PR Review — wallet#474

refactor(ui): show pill navbar only on root pages by @sahilc0

Overview

Clean refactor that conditionally hides the pill navbar on sub-pages, replacing fragile manual padding workarounds with a simple visibility flag + spring animation. +22/-9 — tight and focused.

✅ What Looks Good

  • Solid approach: Hiding navbar on sub-pages where action buttons live eliminates the conflict entirely — much better than fighting CSS layering.
  • useReducedMotion respect — good accessibility practice with the Framer Motion transitions.
  • pointer-events: none on hidden state — prevents ghost taps during exit animation.
  • Cleanup of manual padding in Delegates.tsx, ionic.css, and ButtonsOnBottom — less special-casing = fewer future bugs.

⚠️ Items to Consider

  1. isSettingsRoot coupling (as noted in PR): The option === SettingsOptions.Menu check in App.tsx couples navbar visibility to OptionsContext. The PR body already flags this — worth resolving before merge or at minimum tracking as a follow-up issue. A NavigationContext.isRootPage approach would be more robust.

  2. Animation on initial mount: initial={false} on the motion.div skips the entry animation on first render. This is correct for the common case (app loads on Wallet home), but if the app deep-links directly to a sub-page and then navigates back to a root page, verify the navbar animates in correctly rather than appearing instantly.

  3. CSS class toggle vs. animation state: The pill-navbar-layer--hidden class is applied immediately, but the Framer Motion animate runs over duration: 0.5. There is a brief window where pointer-events: none is set but the element is still partially visible/interactive. Consider syncing pointer-events to onAnimationComplete instead, or accepting the current tradeoff (minor).

  4. Safe area on notch devices: The test plan mentions this — since pill-navbar-clearance CSS variable removal changes the bottom spacing model, this needs careful testing on iOS Safari with home indicator.

Security

No security concerns — UI-only change, no data handling.

Verdict

Clean, well-scoped refactor. The isSettingsRoot coupling is the main thing to resolve (even as a tracked TODO). Otherwise looks good to merge.

sahilc0 added 2 commits March 26, 2026 13:45
The navbar visibility check used `screen` from NavigationContext, which
lags behind the actual rendered page. When the app is locked, `screen`
stays as Pages.Wallet while `page` is overridden to Pages.Unlock,
causing the navbar to appear on the unlock screen.

Add `page === screen` guard so the navbar hides whenever the rendered
page is overridden (unlock, loading). Also add `inert` to the hidden
navbar overlay for accessibility, and stub VITE_DEV_NSEC in tests to
prevent env-dependent flakiness.
@arkanaai
Copy link
Copy Markdown

arkanaai bot commented Mar 26, 2026

Iterative review36e846e + 2fe4100 (fix navbar on unlock/loading + test formatting)

Good improvement. The key change in App.tsx:

const isSettingsRoot = screen === Pages.Settings && option === SettingsOptions.Menu
const showNavbar = page === screen && (screen === Pages.Wallet || screen === Pages.Apps || isSettingsRoot)

The page === screen guard is the critical fix — when the rendered page is overridden (unlock screen, loading hold), the navbar correctly hides even if the navigation context still has Tabs.Wallet set. This was a real UX bug where the navbar would flash on lock/loading screens.

New tests cover all three cases (unlock, loading, authenticated root) — solid coverage.

Minor: the PillNavbarOverlay now accepts visible and uses Framer Motion spring animation with inert for a11y. Clean approach — inert removes it from tab order when hidden, and useReducedMotion respects user preferences.

LGTM ✅

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/test/App.test.tsx (1)

193-235: LGTM!

The new test suite provides solid coverage of the navbar visibility logic:

  • Tests the page !== screen guard (unlock overrides wallet screen).
  • Tests the loading hold scenario.
  • Tests the happy path for authenticated users on root pages.

Consider adding tests for Apps and Settings root pages if not covered elsewhere, to ensure parity with the Wallet root test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/App.test.tsx` around lines 193 - 235, Add two new tests mirroring
the Wallet root test to cover Apps and Settings roots: use renderApp with
authState: 'authenticated' and initialized: true and set screen/tab to
Pages.Apps/Tabs.Apps and Pages.Settings/Tabs.Settings respectively; then await
screen.findByTestId('ion-app') and assert ionApp.className contains
'has-pill-navbar'. Place them alongside the existing Navbar visibility tests and
reuse the same test setup (beforeEach stubbing detectJSCapabilities, matchMedia,
vi.stubEnv) so renderApp, Pages, and Tabs symbols are referenced exactly as in
the current suite.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/test/App.test.tsx`:
- Around line 193-235: Add two new tests mirroring the Wallet root test to cover
Apps and Settings roots: use renderApp with authState: 'authenticated' and
initialized: true and set screen/tab to Pages.Apps/Tabs.Apps and
Pages.Settings/Tabs.Settings respectively; then await
screen.findByTestId('ion-app') and assert ionApp.className contains
'has-pill-navbar'. Place them alongside the existing Navbar visibility tests and
reuse the same test setup (beforeEach stubbing detectJSCapabilities, matchMedia,
vi.stubEnv) so renderApp, Pages, and Tabs symbols are referenced exactly as in
the current suite.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e8c4b040-9af2-4fe7-a4b1-f929222d99a2

📥 Commits

Reviewing files that changed from the base of the PR and between cf486c7 and 2fe4100.

📒 Files selected for processing (5)
  • src/App.tsx
  • src/components/PillNavbarOverlay.tsx
  • src/index.css
  • src/ionic.css
  • src/test/App.test.tsx
💤 Files with no reviewable changes (1)
  • src/ionic.css
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/PillNavbarOverlay.tsx

With the navbar hidden on sub-pages, payment success screens
(send, receive, notes) were dead ends with no way to navigate home.
Adds a ButtonsOnBottom "Sounds good" button that navigates to wallet.
@arkanaai
Copy link
Copy Markdown

arkanaai bot commented Mar 26, 2026

Iterative review9018fcb1 (add "Sounds good" button to success screens)

Straightforward fix for the dead-end problem: Send, Receive, and Notes success screens now have a ButtonsOnBottomButton that calls navigate(Pages.Wallet). Same pattern as Init/Success.tsx.

Consistent across all three screens, correct imports. No concerns — this was the expected follow-up from hiding the navbar on sub-pages.

LGTM ✅

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.

1 participant