Skip to content

feat: replace the account popover with two plain wallet buttons - #136

Merged
gabitoesmiapodo merged 14 commits into
mainfrom
feat/135-account-button
Sep 3, 2026
Merged

feat: replace the account popover with two plain wallet buttons#136
gabitoesmiapodo merged 14 commits into
mainfrom
feat/135-account-button

Conversation

@gabitoesmiapodo

@gabitoesmiapodo gabitoesmiapodo commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #135

The kit's ConnectButton owned a hardcoded account popover, so a consumer could not swap it for a drawer, restyle it, or reduce it to a plain disconnect button. The trigger's onClick belonged to the popover and the panel was internal.

Review guide

Worth your time:

  • WalletButton: a chooser over the two faces, with no caller in this repo
  • TopBar/AccountMenu and useDismissable: hand-rolled dismissal, focus and ARIA
  • TopBar's face rule: party or lock, not isConnected

Skim:

  • The footer and its six icons: a visual port
  • canton-theme: a rename plus a selector hoist
  • Docs

Changes

  • /connect ships ConnectButton, DisconnectButton and WalletButton, a chooser that follows the session
  • children replace the label on both faces, pending copy included
  • preventDefault() in a consumer onClick cancels connect and disconnect
  • The account popover, mode, ConnectButtonMode and @zag-js/popover are gone
  • The header's connected face is the app's own dropdown: copyable party id, network, disconnect
  • A locked wallet replaces that dropdown with a disabled unlock button
  • useDismissable carries the blur-and-Escape dismissal both app dropdowns share

Deviations

  • AccountButton ships as DisconnectButton; it shows no party, so it is named for its action
  • Neither face renders null in the other state; WalletButton picks between them, so either face can go anywhere
  • That choice follows the session, not the party: a lock keeps the session and clears the party
  • The theme class is .cnc-disconnect-button, following the rename
  • Extra scope: a ported dappbooster.dev footer replaces AppShell's hardcoded status strip
  • Extra scope: the published-docs URL in the canton-connect README, and a stale EmptyState comment

Acceptance criteria

  • <AccountButton> reads the party from useParty() and takes no partyId prop
  • <ConnectButton> and <AccountButton> each render null in the other session state
  • mode and ConnectButtonMode are gone from the /connect barrel
  • children replace the label on both buttons, pending copy included
  • preventDefault() in a consumer onClick cancels connect and disconnect respectively
  • AccountPopover.tsx, popoverAnatomy and states.mode are deleted
  • @zag-js/popover, LogoutIcon and ChevronDownIcon are removed from the kit
  • .cnc-account-popover* rules leave canton-theme, shared reset and focus lists included
  • .cnc-account-button is styled, so pnpm check:anatomy passes
  • dapp/frontend rebuilds the dropdown in TopBar/AccountMenu.tsx with no new dependency
  • ConnectPrompt no longer passes mode
  • pnpm lint, test, typecheck, build, knip, docs:check and check:anatomy pass

Test plan

Automated tests

  1. Run pnpm install
  2. From the repo root run pnpm lint, pnpm typecheck, pnpm test, pnpm build, pnpm knip, pnpm docs:check and pnpm run check:anatomy
  3. Expect all seven green

Manual verification

  1. Bring the local stack up per the root README, then run pnpm app:dev
  2. Open http://localhost:3012 with no session and expect "Connect wallet" in the header and on the page
  3. Connect through the wallet and expect the header to show your truncated party id
  4. Click it and expect a panel with the copyable party id, "Connected · <networkId>" and Disconnect
  5. Press Escape and expect the panel to close with focus back on the trigger
  6. Lock the wallet and expect a disabled "Unlock your wallet"; unlock, then Disconnect, and expect "Connect wallet" back

Breaking changes

/connect drops mode and avatar and adds two exports. The package is private and unpublished, and its only consumer is updated here.

Checklist

  • Self-reviewed my own diff
  • Tests added or updated
  • Docs updated (if applicable)
  • No unrelated changes bundled in

Screenshots

Should look mostly the same as it did before.

`ConnectButton` was one component that swapped between a connect trigger and a
Zag popover holding the account, with a `mode` prop to pin it to one. A host
that wants a dropdown of its own had to take ours or take nothing.

`WalletButton/` replaces it with three exports from `/connect`: `ConnectButton`
and `DisconnectButton`, each a plain button that renders whatever the session
says and calls one method, and `WalletButton`, a chooser that follows
`useWalletStatus().isConnected` and hands its props to the face it picks. The
session and not the party decides that face, because a locked wallet keeps the
session and clears the party.

The popover goes with it, and `@zag-js/popover` with the popover: no component
here needs a popper any more. `LogoutIcon` and `ChevronDownIcon` were rendered
only inside it.
`.cnc-disconnect-button` shares the connect button's rules, since they are the
same object with different words. The popover's own block goes with the
component, taking the second `z-index` value and the only `[hidden]` rule in
the file with it.

Two rules the file was already following go into CLAUDE.md while there is a
case to point at: hoist a shared trailing part into `:is()` where every entry
weighs the same, and never nest with `&`, which hides a class from the regex
`check:anatomy` harvests selectors with.
The kit's account popover was the whole of the connected face, so the app could
show the party id and nothing else. `TopBar/AccountMenu` replaces it with a
dropdown the app owns: a copyable party id, the network the session is on, and
the kit's `DisconnectButton`.

The top bar picks the face rather than mounting the kit's `WalletButton`, on the
party or the lock and not on `isConnected` alone. A standing session reports no
party while the account read is in flight and again after it fails, and the
connect face answers both — pending copy for the first, a retry for the second.
A lock is the one state that clears the party for good, and there the dropdown
is replaced whole by a disabled button. `useParty` reports that lock beside the
party, so nothing else in the app reaches for a canton-connect hook.

Dismissal is `hooks/useDismissable`, shared with `RoleSelect`: the focusout test
against the root, the Escape close, the focus returned to the trigger, and the
mousedown guard without which Safari unmounts the panel before the click lands.
The shell ended in a hardcoded status strip. `Footer` ports dappbooster.dev's:
the BootNode credit, the four social links and the two resource links, over one
outbound anchor and six new icons.
`ConnectButton` was one component in every doc that named it, and its popover
was the example for how Zag earns its place and how depth is decided. Both
seams move: the kit's is three components behind `/connect` that require a
provider ancestor, the app's is a top bar picking a face and an account menu
holding the session.

The shared pieces the app now has one of each of are listed with them, and the
published reference is at its own domain rather than the Vercel one.
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
demo.canton-dappbooster Ready Ready Preview Sep 3, 2026 4:50pm UTC
docs.canton-dappbooster Ready Ready Preview Sep 3, 2026 4:50pm UTC

Request Review

Copilot AI left a comment

Copy link
Copy Markdown

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 replaces the kit's hardcoded, @zag-js/popover-based account popover inside ConnectButton with three plain, composable buttons on the /connect sub-path: ConnectButton, DisconnectButton, and a WalletButton chooser that follows the session. This lets a consumer build its own dropdown/drawer by wrapping the buttons and opting out of the default action with preventDefault(). The app's dapp/frontend is the sole consumer and is updated to rebuild the connected-account dropdown itself in TopBar/AccountMenu, backed by a new shared useDismissable hook (also adopted by RoleSelect). The change also ports a richer footer and renames the theme's .cnc-account-* rules to .cnc-disconnect-button.

Changes:

  • /connect now ships ConnectButton, DisconnectButton, and WalletButton; mode, ConnectButtonMode, avatar, the popover, and @zag-js/popover are removed.
  • The app rebuilds the account dropdown in TopBar/AccountMenu (copyable party id, network, disconnect; disabled "Unlock your wallet" when locked) and extracts blur/Escape dismissal into hooks/useDismissable.
  • Supporting changes: useParty now surfaces isLocked and networkId; a new Footer with six icons replaces the status strip; theme selectors are hoisted into :is() and renamed; docs/URLs updated.

Reviewed changes

Copilot reviewed 42 out of 44 changed files in this pull request and generated no comments.

Show a summary per file
File Description
canton-dappbooster/src/components/WalletButton/{index,ConnectButton,DisconnectButton}.tsx New session-following chooser plus the two plain button faces.
canton-dappbooster/src/components/WalletButton/{anatomy,composeAction}.ts Split connect/disconnect anatomies and the shared preventDefault-aware action composer.
canton-dappbooster/src/components/WalletButton/*.test.tsx Tests for the chooser, connect and disconnect faces.
canton-dappbooster/src/components/ConnectButton/* (deleted) Old popover-based button, anatomy, tests, and AccountPopover removed.
canton-dappbooster/src/connect.ts Barrel now exports the three wallet buttons and their prop types.
canton-dappbooster/src/icons/{ChevronDownIcon,LogoutIcon}.tsx + index.ts Removed now-unused kit icons.
canton-dappbooster/{package.json,doc-fixtures.d.ts,README.md,CLAUDE.md,architecture.md} Drop @zag-js/popover, add toggleMenu fixture, update docs.
canton-theme/src/default.css, canton-theme/CLAUDE.md Rename to .cnc-disconnect-button, hoist selectors into :is(), document specificity/z-index rules.
dapp/frontend/src/components/TopBar/{index,AccountMenu}.tsx Rebuild the connected dropdown and pick the face on party/lock.
dapp/frontend/src/hooks/{useDismissable,useParty}.ts, components/RoleSelect.tsx Shared dismissal hook; useParty adds isLocked/networkId; RoleSelect adopts the hook.
dapp/frontend/src/components/{Footer,AppShell,ConnectPrompt,EmptyState}.tsx + src/icons/* New footer with social/resource icons replacing the status strip; drop mode.
canton-connect/README.md, pnpm-lock.yaml Docs URL update; lockfile drops popover/popper.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

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

@fernandomg fernandomg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

some tiny things that need fixing, an open question regarding AccountMenu

Comment thread canton-connect/README.md Outdated
Comment thread canton-theme/src/default.css
Comment thread canton-dappbooster/README.md Outdated
Comment thread dapp/frontend/src/components/TopBar/AccountMenu.tsx Outdated
A wallet reports a lock and its own disconnect as the same push, so the
header cannot know which it is. The disabled button also disagreed with
the refresh path, which lands on the connect face for the same wallet
state.
Comment thread dapp/frontend/src/components/TopBar/index.tsx Outdated
TopBar already picks between the two faces on party, so the menu
re-reading the hook only to return null was a second gate on the same
condition.
Comment thread dapp/frontend/src/components/TopBar/AccountMenu.tsx Outdated
The parent decides between the menu and the connect button, so the menu
always renders.
@gabitoesmiapodo
gabitoesmiapodo merged commit a9fc774 into main Sep 3, 2026
8 checks passed
@gabitoesmiapodo
gabitoesmiapodo deleted the feat/135-account-button branch September 3, 2026 16:56
@github-project-automation github-project-automation Bot moved this from In review to Done in Canton - dAppBooster (#390) Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

ConnectButton's account dropdown cannot be replaced, restyled or repurposed

3 participants