More invoker commands, and more reasons not to use JavaScript please | pawelgrzybek.com

3 min read Original article ↗

The rule of least power on the web incentivised using HTML before reaching for CSS, CSS before JavaScript, and bashing it into the JS script as a last resort. Every time the web ships new features that let us shift the implementation left on the stack, I’m excited about it. In recent years, we have received a heap of CSS features that required not a trivial chunk of JS code a few years ago. The Invoker Commands API moved the implementation of button click handlers upstream to the HTML.

The Invoker Commands API recently landed on Safari, and now it is supported by all modern engines. This is usually the moment when I like to learn enough about the new feature so I will remember it when I need it. I probably won’t wait too long as it is crazy useful.

Baseline: Invoker commands is newly available

No need for event listeners

It is a declarative API that triggers common behaviours against other elements via the click of a button, like opening and closing a dialog or a popover. The command attribute accepts an action and the commandfor sets the target to invoke the command upon. A classic dialog example.

<button commandfor="d" command="show-modal">Open dialog</button>

<dialog id="d">
  <button commandfor="d" command="close">Close dialog</button>
  ♥️
</dialog>

See the Pen 2026-01-26-1 by Pawel Grzybek (@pawelgrzybek) on CodePen.

Currently the list of commands predefined by the browsers is limited to: toggle-popover, show-popover, hide-popover, show-modal, close and request-close. The difference between close and request-close is subtle, but the latter one, before invoking the close event, also invokes the cancel event, which you can block using preventDefault.

Custom commands

Registering a custom commands is what makes it interesting, and to make one you need to prefix it with a double dash, for example --change-bg. This one invokes a custom command of the CommandEvent interface on the target element. Here is a simple example (worth noting that the value defined on the button can be read from the source property). Contrary to the title of this post, this one needs a tiny, tiny snippet of JavaScript.

<button commandfor="d" command="--change-bg" value="salmon">Salmon</button>
<button commandfor="d" command="--change-bg" value="lime">Lime</button>
<button commandfor="d" command="--change-bg" value="hotpink">Hot pink</button>

<div id="d">♥️</div>
const d = document.getElementById("d");

d.addEventListener("command", (e) => {
  if (event.command === "--change-bg") {
    d.style.backgroundColor = e.source.value;
  }
});

See the Pen 2026-01-26-2 by Pawel Grzybek (@pawelgrzybek) on CodePen.

The future of the invoker commands

Although the list of predefined commands looks slim at the moment, I learned from from the “Everything you need to know about Invoker Commands” by Keith Cirkel talk that there are discussions to add more commands. Media control, toggling the summary view of the details element, full screen view, copy, share and controlling form elements are all great candidates and they are all under discussions. OpenUI “Invoker Commands Future (Explainer)” page goes a lot more in depth about future possibilities.

Resources