yiekheng 99fd2584e4 fix(web): drop <button>-wrapping-<Card> — div inside button is invalid HTML
Root cause of the hydration mismatch:

  <button type="button">       ← React 19 server output
    <Card>                     ← <div> from shadcn Card
      <CardContent>...

`<div>` is flow content and is NOT allowed inside `<button>` per the
HTML spec. Browsers auto-close the outer `<button>` when they hit the
nested `<div>`, while React's SSR doesn't — the server tree and the
post-parse client tree disagree, and React 19 throws Hydration failed.

Fix: stop nesting Card inside button-shaped triggers. Three sites
touched, all on the account list / detail pages:

- Accounts list — Delete card per row
- Account detail — Unpair card
- Account detail — Delete card

For these the trigger is a Dialog. Radix's DialogTrigger asChild
forwards click handling to whatever element you give it, so we now
pass the Card directly with role="button" / tabIndex / aria-label.
The Card stays a <div>, no invalid nesting.

- Account detail — Pair / Re-pair card

This one wraps a server action `<form>`, which still requires a real
`<button type="submit">`. Solution: keep the Card as a sibling of an
absolute-positioned transparent submit button covering the card's
surface — the whole card surface still triggers submit, but the
visible Card never lives inside the button, and HTML stays valid.

Updated `accounts-list-view.test.tsx` to match: the delete card's
trigger is now a `<div role="button" tabIndex="0">` instead of a
real button.

92/92 tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:11:35 +08:00
..