vuetifyjs / vuetifyjs/0

Evaluate Invoker Commands API (command/commandfor) for Dialog, AlertDialog, and a custom-command bridge

Open
#573 0 comments 0 reactions 1 assignee View on GitHub

@johnleider is already working on this.

Since Jul 12, 2026.

enhancement
Dominant language
TypeScript
Stars
735
Forks
10
Avg merge
10h 47m
Merged PRs (30d)
72

Description

Summary

The web platform's Invoker Commands API (command / commandfor HTML attributes + CommandEvent) lets a <button command="show-modal" commandfor="id"> declaratively open/close a dialog, toggle a popover, or dispatch a custom --command event to a target — replacing JS onClick wiring, with the UA managing aria-expanded / aria-controls. This issue parks an assessment of where (if anywhere) it earns a place in @vuetify/v0.

Parked for a future release — no milestone.

Counterargument first

Most of v0 already composes the native primitives this API wraps, so the blanket answer is "no":

  • Popover already emits popovertarget on its activator and popover on content — the UA toggles it with zero JS. command="toggle-popover" is functionally equivalent to the popovertargetaction behavior already in effect. Zero gain.
  • Custom commands duplicate what createContext + context methods (next(), select(), toggle()) already do — but type-safely and without ID coupling.
  • Constraints cut against v0's grain: the target must exist in the DOM with a resolvable id, and the invoker must be a real <button> (v0's as is polymorphic — as="a" would silently break it).

So this is polish, not a composable-layer differentiator. Two exceptions are genuinely worth it.

Ranked opportunities

Fit Where Native command Value
Strong AlertDialog request-close High — maps onto hand-rolled prevent-close logic
Good Dialog show-modal / close Medium — dedups JS onClick
Novel bet Carousel / Step / Tabs custom --next etc. Headless novelty (needs design decision)
Skip Popover toggle-popover ~Zero (already native)
Skip Tooltip, Snackbar, Collapsible, ExpansionPanel Wrong interaction model / no benefit
1. AlertDialog request-close (strongest case)

request-close fires a cancelable cancel event; if the consumer preventDefault()s it, the dialog stays open. That is exactly what AlertDialogContent.onCancel hand-rolls today (e.preventDefault(); return), and it's adjacent to the wait() / close() deferred-close pattern in AlertDialogAction. The platform now ships a primitive for the precise "are you sure? / unsaved changes" flow v0 built by hand.

2. Dialog show-modal / close
  • DialogActivator does onClick → context.open()command="show-modal" commandfor={contentId}
  • DialogClose does onClick → context.close()command="close"

The content id already exists on DialogContent, and PopoverActivator already proves the activator-reads-content-id pattern. Net: less JS, UA-managed aria-* on the invoker.

3. Custom-command → method bridge (novel, needs a design decision)

A headless primitive (working name useInvoker / createCommand) where a component root listens for CommandEvent and routes custom commands (--next, --prev, --select) to context methods — letting a consumer wire any button anywhere in the DOM to a Carousel/Step/Tabs without prop-drilling or shared context. On-brand with v0's "compose native platform APIs into novel composables" identity (cf. usePopover).

[!IMPORTANT]
PHILOSOPHY §2.5 caveat: composables never bind DOM events — components do. So this can't be a raw composable; it needs to be component-layer, or a sanctioned exception like the useEventListener / observer wrappers. Decide the boundary in a design doc before building.

Mechanics that keep this low-risk

  • Progressive enhancement: Atom forwards $attrs, and Button/Toggle/Activators use mergeProps — so command / commandfor pass through today without any v0 change. Adoption is additive.
  • Feature-detect to avoid double-firing (native open + JS open()): gate on 'command' in HTMLButtonElement.prototype; consider a SUPPORTS_INVOKER global alongside the existing SUPPORTS_* constants and drop the JS handler when true.
  • Browser support: Chrome/Edge, Safari, and Firefox all shipped command / commandfor during 2025 — "Baseline newly available," not yet "widely available." Fine for a headless lib doing progressive enhancement; premature to make it the only path.

Proposed tracks

  1. Additive, feature-detected: adopt request-close in AlertDialog and show-modal / close in Dialog behind SUPPORTS_INVOKER, keeping JS handlers as the fallback. Small, defensible.
  2. Design-doc first: the CommandEvent custom-command bridge — resolve the §2.5 boundary before implementing.

Skip Popover, Tooltip, Snackbar, Collapsible, and ExpansionPanel entirely.

References

  • MDN: Invoker Commands API
  • Relevant source: packages/0/src/components/{Dialog,AlertDialog,Popover}/, packages/0/src/composables/usePopover/

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.