Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ These mirror `CONTRIBUTING.md`. Enforce them in your own writing and when review
4. **British English.** "colour", "behaviour", "internationalisation", "licence" (noun).
5. **Section structure.** `## What it is`, `## Why it matters`, `## How to implement`, `## Common mistakes`, `## Verification`. Last two are optional if they would not add value.
6. **Length.** 250–500 words of body content. Be useful, not padded.
7. **Inline cross-links must carry the right category.** A spec page's URL is `/spec/<category>/<slug>/`, and the category is the directory the `.md` lives in — _not_ always the category of the page you're linking from. When you hand-write an inline link to another spec page, confirm its directory (`ls src/content/spec/*/<slug>.md`) before writing the path; do not assume the target shares the current page's category. (`relatedSlugs` is safe — the route resolver looks up each slug's real category — but raw Markdown links are not checked at authoring time, only by the **Internal links** CI job, which 404s on a wrong-category path.)

## Architecture quick reference

Expand Down
Binary file modified public/og-default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/og/checklist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/og/spec.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/og/spec/foundations.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/og/spec/foundations/invoker-commands.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/content/changelog/2026-06-30-invoker-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: New page on invoker commands
date: "2026-06-30"
type: added
relatedSlugs: [invoker-commands]
---

Added a page on [invoker commands](/spec/foundations/invoker-commands/) — the `command` and `commandfor` button attributes that declaratively open a popover, control a `<dialog>`, or fire a custom `CommandEvent`, with the browser handling keyboard activation and accessibility. Documented as `recommended`: it reached Baseline across major browsers at the end of 2025 (Safari 26.2 completing the set), so it is a sound progressive enhancement with a scripted fallback for older versions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ summary: "Prefer native HTML interactive elements — <button>, <a>, <details>/<
status: recommended
order: 155
appliesTo: [all]
relatedSlugs: [semantic-html, aria-usage, keyboard-navigation, hidden-until-found, inert-attribute]
relatedSlugs: [semantic-html, aria-usage, keyboard-navigation, hidden-until-found, inert-attribute, invoker-commands]
updated: "2026-05-29T16:40:22.000Z"
sources:
- title: "WHATWG HTML — The details element"
Expand Down
80 changes: 80 additions & 0 deletions src/content/spec/foundations/invoker-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
title: "Invoker commands"
slug: invoker-commands
category: foundations
summary: "Wire a button to open a popover, close a dialog, or run a custom action declaratively with command and commandfor — no click handler, no ARIA plumbing."
status: recommended
order: 170
appliesTo: [all]
relatedSlugs: [popover-api, native-interactive-elements, semantic-html, inert-attribute, keyboard-navigation]
updated: "2026-06-30T00:00:00.000Z"
sources:
- title: "HTML Standard — The button element (command, commandfor)"
url: "https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-command"
publisher: "WHATWG"
- title: "HTML Standard — CommandEvent"
url: "https://html.spec.whatwg.org/multipage/interaction.html#commandevent"
publisher: "WHATWG"
- title: "MDN — Invoker Commands API"
url: "https://developer.mozilla.org/en-US/docs/Web/API/Invoker_Commands_API"
publisher: "MDN"
- title: "Open UI — Invokers explainer"
url: "https://open-ui.org/components/invokers.explainer/"
publisher: "Open UI"
---

## What it is

Invoker commands let a `<button>` control another element declaratively, through two HTML attributes:

- `commandfor` references the `id` of the element to act on.
- `command` names the action to perform.

The browser wires the click and keyboard activation, dispatches a `command` event on the target, and runs the built-in behaviour — no `addEventListener`, no `aria-controls`, no `aria-expanded` to keep in sync.

```html
<button commandfor="menu" command="toggle-popover">Open menu</button>

<div id="menu" popover>
<button commandfor="menu" command="hide-popover">Close</button>
</div>
```

The built-in commands cover the common overlay patterns: `show-popover`, `hide-popover`, `toggle-popover` for [popovers](/spec/foundations/popover-api/), and `show-modal`, `close`, `request-close` for `<dialog>`. Any value prefixed with two dashes (for example `command="--rotate"`) is a **custom command**: the browser fires a `CommandEvent` on the target carrying that name, and your script decides what to do — invoker wiring without a built-in action.

## Why it matters

- **Less JavaScript for common patterns.** Opening a popover or closing a dialog no longer needs a click handler. The relationship lives in markup the browser understands.
- **Accessibility comes for free.** The control is a real `<button>`, so keyboard activation, focus, and the exposed relationship are handled natively. There is no ARIA to add or to drift out of sync.
- **Custom commands are properly delegated.** A `--name` command dispatches a single typed event from a real button, replacing ad-hoc `onclick` wiring with one consistent mechanism — and you can read `event.source` to know which button invoked it.
- **It composes with the platform.** Invoker commands pair directly with the Popover API, `<dialog>`, and [native interactive elements](/spec/accessibility/native-interactive-elements/) rather than re-implementing their behaviour in script.

## How to implement

Reach for invoker commands whenever a button's job is to drive another element. Use the built-in popover and dialog commands for overlays; use a `--`-prefixed custom command when you want the declarative button-to-target wiring but a behaviour the platform does not ship.

```html
<button commandfor="confirm" command="show-modal">Delete…</button>

<dialog id="confirm">
<form method="dialog">
<p>Delete this item?</p>
<button command="close" commandfor="confirm">Cancel</button>
<button value="delete">Delete</button>
</form>
</dialog>
```

Treat it as progressive enhancement where you still support older browsers: a button with a JavaScript fallback degrades to a normal control.

## Common mistakes

- **Putting `command`/`commandfor` on a non-button.** They only work on `<button>`. A `<div>` with these attributes does nothing and is not keyboard-operable.
- **Custom command without a listener.** A `--name` command does nothing on its own; you must handle the `command` event on the target.
- **Forgetting `request-close`.** For dialogs that should run cancel behaviour and fire the `cancel` event, prefer `request-close` over `close`.

## Verification

- Activate the button with the keyboard (Enter/Space): the target popover or dialog responds without any script.
- In DevTools, confirm the `<button>` exposes the command relationship and that a `command` event fires on the target.
- Baseline: invoker commands became newly available across major browsers at the end of 2025 — keep a scripted fallback if you still support older versions.
2 changes: 1 addition & 1 deletion src/content/spec/foundations/popover-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ summary: "Replace ARIA-puzzled JavaScript modals, menus, and tooltips with a nat
status: recommended
order: 130
appliesTo: [all]
relatedSlugs: [semantic-html, aria-usage, keyboard-navigation, focus-indicators, anchor-positioning]
relatedSlugs: [semantic-html, aria-usage, keyboard-navigation, focus-indicators, anchor-positioning, invoker-commands]
updated: "2026-06-08T00:00:00.000Z"
sources:
- title: "HTML Standard — Popover"
Expand Down
Loading