Skip to content

feat(live): make picker chrome modal-host friendly (Radix, Headless UI, vaul)#116

Merged
pbakaus merged 1 commit intomainfrom
feat/live-modal-host-friendliness
Apr 27, 2026
Merged

feat(live): make picker chrome modal-host friendly (Radix, Headless UI, vaul)#116
pbakaus merged 1 commit intomainfrom
feat/live-modal-host-friendliness

Conversation

@pbakaus
Copy link
Copy Markdown
Owner

@pbakaus pbakaus commented Apr 27, 2026

Summary

Closes #113. The live picker became unclickable inside Radix Dialog portals, and clicking it dismissed the host dialog. Fixes that, plus three adjacent issues that surfaced during manual verification on the new fixture.

What's in here

  • Modal-aware chrome — new defangOutsideHandlers helper applied to bar, action picker, params panel, annotation overlay, global bar, and design panel host. Sets pointer-events: auto !important so Radix's modal scroll-lock can't silence our UI, and stops pointerdown / mousedown / focusin propagation at the chrome boundary so DismissableLayer / FocusScope outside-handlers never fire for our clicks. Click events still bubble normally.
  • detectPageTheme correctness — the prior regex read rgba(0,0,0,0) as black, mislabelling every default-background page as dark. Now honors alpha, walks body → html, and falls back to prefers-color-scheme only when both are transparent.
  • Exit X visibility against host CSS resets — every other chrome button set padding inline; the exit X did not, so a host like button { padding: 0.5rem 1rem } inflated the 24×24 button into 56×40 and pushed the SVG out of view. Pinned padding: 0 + box-sizing: border-box, dropped to a 14 / stroke 1.5 spec that matches the other toggle icons in perceived weight.
  • Toast stacks above the bar — the "No PRODUCT.md found" toast (and any toast fired while the global bar is up) used to sit at bottom: 16px, overlapping the bar's bottom: 14px. Now reads the bar's actual rect and parks itself ~12 px above its top edge.

Test plan

  • bun run test — 180/180 unit + static fixture checks pass
  • bun run test:live-e2e — 20/20 fixtures pass (19 prior + new vite8-react-radix-dialog)
  • Manually verified on vite8-react-radix-dialog: pick element inside a real @radix-ui/react-dialog portal → bar appears above the lightbox, every chip / Go / cycle / Accept click works, dialog stays open through the full session.
  • Manually verified theme detection picks the dark bar on the default-white fixture.
  • Manually verified exit X is visible at rest (textDim) and brightens on hover (text); padding fix holds against the fixture's button { padding: 0.5rem 1rem }.
  • Manually verified the "No PRODUCT.md found" toast on reload no longer obscures the bar.

🤖 Generated with Claude Code


Note

Medium Risk
Changes global event propagation and pointer-events behavior for multiple floating UI roots, which could subtly affect interactions/focus behavior on some host pages. Scope is limited to live-picker chrome and fixture/docs updates with no data/security impact.

Overview
Improves live-picker robustness when used inside modal dialogs/portals (e.g., Radix, Headless UI, vaul). Adds defangOutsideHandlers and applies it to the bar, action picker, params panel, annotation overlay, global bar, and design panel host to force pointer-events: auto (where appropriate) and stop early pointer/focus event propagation so host “outside click/focus” dismiss logic doesn’t interfere.

Also fixes a few adjacent UI issues: toast positioning now avoids overlapping the global bar, detectPageTheme now ignores transparent rgba(...) backgrounds and falls back more safely, and the exit “X” button is styled to resist host CSS resets. Adds a new vite8-react-radix-dialog framework fixture for e2e coverage and updates the craft reference name (NeonNeo Mirai).

Reviewed by Cursor Bugbot for commit 630e586. Bugbot is set up for automated code reviews on this repo. Configure here.

…I, vaul)

Closes #113.

Picker chrome could become unclickable inside Radix Dialog portals, and
clicking it dismissed the host dialog. Three orthogonal issues surfaced
during manual verification:

1. Modal-aware chrome
   - Add `defangOutsideHandlers` and apply it to bar, picker, params
     panel, annotation overlay, global bar, and design panel host.
   - Sets `pointer-events: auto !important` on interactive chrome so
     Radix's `body { pointer-events: none }` modal scroll-lock can't
     silence our UI.
   - Stops `pointerdown` / `mousedown` / `focusin` propagation at the
     chrome boundary so DismissableLayer / FocusScope outside-handlers
     never fire for clicks that land on us.

2. detectPageTheme: misread transparent body as black
   - `getComputedStyle(body).backgroundColor` returns `rgba(0,0,0,0)`
     when no bg is set; the prior regex captured (0,0,0) and ignored
     alpha, calling every default-bg page "dark."
   - Honor alpha, walk body → html, fall back to
     `prefers-color-scheme` only when both are transparent.

3. Exit X invisible on host pages with `button { padding: ... }`
   - Every other chrome button sets padding inline; exitBtn didn't.
     Host resets like `button { padding: 0.5rem 1rem }` (in the new
     fixture, common in the wild) inflated the 24x24 button into 56x40
     and pushed the SVG into a non-rendering region — DevTools showed
     the right styles, the X just didn't paint.
   - Pin `padding: 0` + `box-sizing: border-box`, match the toggle
     icon spec (14 / stroke 1.5 / textDim → text on hover).

4. Toast no longer obscures the global bar
   - Position the toast above globalBarEl's actual rect instead of a
     fixed bottom: 16px that overlapped the bar's bottom: 14px.

Test coverage: new `vite8-react-radix-dialog` fixture exercises the
full pick → Go → cycle → Accept loop with `@radix-ui/react-dialog`
+ `Portal` + `Overlay` + `Content`. Without the fix, clicking Go
dismisses the dialog and unmounts the picked element. All 20 live
e2e fixtures pass; all 180 unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying impeccable with  Cloudflare Pages  Cloudflare Pages

Latest commit: 630e586
Status: ✅  Deploy successful!
Preview URL: https://c59e501c.impeccable-2rv.pages.dev
Branch Preview URL: https://feat-live-modal-host-friendl.impeccable-2rv.pages.dev

View logs

@pbakaus pbakaus merged commit 62ce35a into main Apr 27, 2026
3 checks passed
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.

bug: live picker is not clickable inside Radix portal dialogs

1 participant