Skip to content

Dialog: mouse clicks can't focus inputs (overlay pointerdown.preventDefault suppresses mousedown) #766

@netchampfaris

Description

@netchampfaris

Summary

In a Dialog, mouse clicks cannot focus text inputs/textareas/contenteditable. Keyboard Tab + typing works, and click-driven controls (Close/Submit buttons) work — only fields that rely on mouse focus are dead. Observed on the v1/espresso-tokens branch.

Root cause

Dialog.vue uses the scrollable layout where <DialogContent> is nested inside a scrollable <DialogOverlay> (overflow-y-auto) — the standard pattern for tall, scrollable dialogs.

However, the overlay element ends up with a pointerdown handler bound through Vue's .prevent modifier (confirmed live: the canceling listener's currentTarget is the overlay div, owner Primitive). Because content is nested under the overlay, a pointerdown on a dialog input bubbles up to the overlay and is preventDefault()'d.

Per the Pointer Events spec, cancelling pointerdown suppresses the compatibility mousedown — and mousedown's default action is what focuses an input + places the caret. Net effect:

  • pointerdown and click fire on the input, but mousedown and focusin never do.
  • Inputs/textareas/contenteditable can't be focused by mouse.
  • Buttons still work because they act on click.

Note: reproduces only with a trusted (real) mouse event. Synthetic el.click() / dispatchEvent bypasses the focus-on-mousedown machinery and will not reproduce it.

Open question

I could not locate where the overlay's pointerdown.prevent is bound — it is not in Dialog.vue's template, nor anywhere in reka-ui's Dialog/* source. It arrives on the overlay element via the $attrs forwarding chain (DialogOverlayDialogOverlayImplPrimitive). Tracking down and removing it at the source is the proper fix.

Current workaround (shipped)

@pointerdown.stop on <DialogContent> so content pointerdowns never reach the overlay's prevent handler. Verified: input focus, typing, Escape, buttons, and outside-click dismiss all work (reka's dismiss detection is capture-phase, unaffected by a bubble-phase .stop).

Proper fix options

  1. Find and remove/scope the stray pointerdown.prevent on the overlay (ideal — root cause).
  2. Keep the workaround @pointerdown.stop.
  3. Restructure to reka's centered sibling layout (DialogOverlay/DialogContent as siblings, content fixed-centered) — note this trades away in-overlay scrolling for tall dialogs, and a naive sibling scroll-wrapper would render even when closed (DialogPortal is a bare Teleport), so it needs its own Presence gating.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriagedSeen by baristaui

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions