DEV Community

Emmanuel Elikwu
Emmanuel Elikwu

Posted on

HTML Can Open Dialogs Without JavaScript- Here's How command and commandfor Work

What if I told you that you could open a modal, close it, and control a popover menu using HTML alone?

No addEventListener().

No onclick handlers.

No JavaScript functions just to open and close things.

Modern HTML supports Invoker Commands, using two attributes:

  • commandfor
  • command

And they're surprisingly powerful.

In this tutorial, we'll explore them by building interactive features inside a real luxury real estate website called HAVEN Estates.

Instead of demonstrating everything with empty boxes, we'll use actual property cards, detailed property dialogs, a navigation popover, and a private-viewing form.

Prefer watching rather than reading? Watch the full step-by-step YouTube tutorial.

If you want to go deeper and build full responsive layouts faster, check out my guide: Build Any Responsive Layout with CSS Grid & Flexbox. It walks through building real pages component by component.

Let's get started.

1. The old way: JavaScript event handlers

Imagine you're building a website with a button that opens a modal.

Traditionally, you might write:

<button id="open-dialog">
  View Property
</button>

<dialog id="property-dialog">
  <h2>Villa Sereno</h2>
  <p>Explore this luxury residence.</p>
</dialog>
Enter fullscreen mode Exit fullscreen mode

Then use JavaScript:

const openButton = document.querySelector("#open-dialog");
const dialog = document.querySelector("#property-dialog");

openButton.addEventListener("click", () => {
  dialog.showModal();
});
Enter fullscreen mode Exit fullscreen mode

This works, and JavaScript is still essential when you need custom application logic. But for a simple built-in interaction like opening a dialog, we no longer need to write this event handler in supporting browsers.

2. Meet commandfor and command

Here's the HTML-only alternative:

<button
  type="button"
  commandfor="property-dialog"
  command="show-modal"
>
  View Property
</button>

<dialog id="property-dialog">
  <h2>Villa Sereno</h2>
  <p>Explore this luxury residence.</p>
</dialog>
Enter fullscreen mode Exit fullscreen mode

That's it. The browser knows which dialog to open and what action to perform.

commandfor = WHERE

The commandfor attribute identifies the target element:

commandfor="property-dialog"
Enter fullscreen mode Exit fullscreen mode

This connects the button to the element with the matching ID:

<dialog id="property-dialog">
Enter fullscreen mode Exit fullscreen mode

Think of it as answering: Which element should this button control?

command = WHAT

The command attribute tells the browser what action to perform:

command="show-modal"
Enter fullscreen mode Exit fullscreen mode

Think of it as answering: What should happen to that element?

The easiest way to remember the difference:

commandfor = WHERE

command = WHAT

That's the foundation of HTML Invoker Commands.

3. The commands you need to know

For built-in dialogs and popovers, common command values include:

Command Purpose
show-modal Open a dialog as a modal
close Close a dialog
request-close Request a dialog to close
show-popover Show a popover
hide-popover Hide a popover
toggle-popover Toggle a popover

Some values target <dialog> elements, while others target elements with the popover attribute. That distinction is important.

Let's use them in our actual project.

4. Build a property dialog in HAVEN Estates

HAVEN Estates is a premium property website featuring luxury residential listings. Each property card contains a quick-view button.

Our goal is simple: when a visitor clicks a property button, a dialog should open with more information.

Let's use one of the properties: Villa Sereno.

Step 1: Create the button

Inside our property card, we have a button similar to this:

<button
  class="image-action"
  type="button"
  commandfor="property-sereno"
  command="show-modal"
  aria-label="Quick view of Villa Sereno"
>
  ↗
</button>
Enter fullscreen mode Exit fullscreen mode

Notice three important things:

  1. It's a real <button>.
  2. The button points to property-sereno.
  3. The command is show-modal.

Now we need to create the target.

Step 2: Add the dialog

<dialog
  class="modal property-dialog"
  id="property-sereno"
  aria-labelledby="dialog-title-sereno"
>
  <div class="modal-body">
    <h2 id="dialog-title-sereno">
      Villa Sereno
    </h2>

    <p>
      A luxury Mediterranean residence
      designed for exceptional coastal living.
    </p>

    <button
      type="button"
      commandfor="property-sereno"
      command="close"
    >
      Close
    </button>
  </div>
</dialog>
Enter fullscreen mode Exit fullscreen mode

The id matches the button's commandfor value. That's what makes the connection work.

Click the quick-view button, and the browser opens the dialog—without a custom click handler or a JavaScript function to call showModal().

Step 3: Close the dialog

Look at our close button:

<button
  type="button"
  commandfor="property-sereno"
  command="close"
>
  Close
</button>
Enter fullscreen mode Exit fullscreen mode

We target the same dialog, but change the command from show-modal to close.

What about request-close?

Our HAVEN project also uses this pattern for the close icon:

<button
  type="button"
  commandfor="property-sereno"
  command="request-close"
  aria-label="Close property details"
>
  ×
</button>
Enter fullscreen mode Exit fullscreen mode

The difference matters:

  • close directly closes the dialog.
  • request-close requests closure through the dialog's cancelable close-request behavior.

That can be useful when an application needs to intercept a closing request, such as warning about unsaved changes. For our simple property preview, both normally result in a closed dialog.

5. Style the dialog with CSS

Native HTML controls don't mean we have to accept boring designs.

The HAVEN Estates dialogs use custom styling, property imagery, elegant typography, and a backdrop. Here's a simplified example:

.modal {
  width: min(90vw, 760px);
  max-height: 90dvh;
  padding: 0;
  border: 0;
  border-radius: 18px;
  overflow: auto;
  background: #fff;
  color: #24211d;
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.25);
}

.modal::backdrop {
  background: rgba(10, 14, 18, 0.65);
  backdrop-filter: blur(6px);
}
Enter fullscreen mode Exit fullscreen mode

The ::backdrop pseudo-element styles the layer behind a modal dialog. And max-height: 90dvh limits the dialog to the dynamic viewport height, with scrolling available for longer content.

Native behavior and custom design can work together.

6. Create a navigation menu with the Popover API

Now let's move to another real feature in HAVEN Estates: the navigation button.

Clicking it reveals an additional panel containing links such as:

  • Discover properties
  • Our philosophy
  • Exceptional amenities
  • Client stories
  • Contact us

The panel also has a Book a private viewing button.

We can build this interaction with HTML commands and the Popover API.

Step 1: Create the trigger

<button
  class="menu-trigger"
  type="button"
  commandfor="site-menu"
  command="toggle-popover"
  aria-label="Open navigation"
>
  ☰
</button>
Enter fullscreen mode Exit fullscreen mode

This time our command is toggle-popover, and the target is site-menu.

Step 2: Create the popover

<nav
  id="site-menu"
  class="site-menu"
  popover
  aria-label="Additional navigation"
>
  <div class="menu-top">
    <strong>Explore HAVEN</strong>

    <button
      type="button"
      commandfor="site-menu"
      command="hide-popover"
      aria-label="Close menu"
    >
      ×
    </button>
  </div>

  <a href="#properties">Discover properties</a>
  <a href="#story">Our philosophy</a>
  <a href="#amenities">Exceptional amenities</a>
  <a href="#testimonials">Client stories</a>
  <a href="#contact">Contact us</a>
</nav>
Enter fullscreen mode Exit fullscreen mode

The important addition is the popover attribute.

Because we're using the default automatic popover behavior, the browser provides useful built-in interactions:

  • The command button can toggle the popover.
  • Escape can dismiss the open popover.
  • Clicking outside can dismiss it.
  • The open popover is placed in the top layer.

All without custom JavaScript for those behaviors.

However, a popover isn't automatically a modal dialog: it does not make the rest of the page inert or trap keyboard focus.

7. Add a viewing form without an open-modal handler

Another feature in HAVEN Estates is the private-viewing form. When a visitor clicks Schedule a viewing, a modal dialog opens.

The trigger uses the same pattern:

<button
  type="button"
  commandfor="viewing-dialog"
  command="show-modal"
>
  Schedule a viewing
</button>
Enter fullscreen mode Exit fullscreen mode

And the target is:

<dialog id="viewing-dialog">
  <h2>Schedule a viewing</h2>

  <form method="dialog">
    <label for="full-name">Full name</label>
    <input id="full-name" name="name" required>

    <label for="email">Email address</label>
    <input id="email" name="email" type="email" required>

    <button type="submit">Close form preview</button>
  </form>
</dialog>
Enter fullscreen mode Exit fullscreen mode

Here's another useful native HTML feature:

<form method="dialog">
Enter fullscreen mode Exit fullscreen mode

When submitted through its submit button, this form can close its containing dialog rather than sending a normal network request. Do not confuse this behavior with saving a booking.

Important: In HAVEN Estates, this is only a frontend demonstration. The form does not actually send or store a viewing request. A production booking system would need server-side handling, validation, data storage, and user feedback.

8. Dialog vs. Popover: When should you use each?

Both are useful, but they solve different problems.

Feature Modal dialog Auto popover
Typical use Important focused interaction Dismissible supplementary UI
Makes background inert Yes, when opened modally No
Escape dismissal Supported Supported
Outside-click dismissal Not automatic by default Supported
Top-layer rendering Yes, when modal Yes, when shown
Example in HAVEN Property preview, viewing form Additional navigation

For HAVEN Estates, we use modal dialogs for detailed property previews and the viewing form, and a popover for additional navigation.

Choosing the right element improves the interaction before we even start styling it.

9. Common mistakes to avoid

Mistake 1: The IDs don't match

Incorrect:

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

<dialog id="another-dialog"></dialog>
Enter fullscreen mode Exit fullscreen mode

The button's target doesn't exist under that ID.

Correct:

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

<dialog id="my-dialog"></dialog>
Enter fullscreen mode Exit fullscreen mode

Mistake 2: Using the wrong command

These commands are not interchangeable:

command="show-modal"
Enter fullscreen mode Exit fullscreen mode

is intended for a dialog, while:

command="toggle-popover"
Enter fullscreen mode Exit fullscreen mode

is intended for a popover.

Mistake 3: Forgetting accessible names

A button containing only an icon can be difficult to understand without an accessible name.

Instead of:

<button commandfor="property-sereno" command="show-modal">
  ↗
</button>
Enter fullscreen mode Exit fullscreen mode

Use:

<button
  commandfor="property-sereno"
  command="show-modal"
  aria-label="Quick view of Villa Sereno"
>
  ↗
</button>
Enter fullscreen mode Exit fullscreen mode

Now assistive technologies can communicate its purpose.

Mistake 4: Assuming modern HTML works in every browser

Invoker Commands are modern platform features. Before using them in production, verify the browser versions your audience needs. Plan a fallback if unsupported browsers are part of your requirements.

Mistake 5: Confusing UI behavior with application functionality

Opening a booking form without JavaScript is not the same as implementing a booking system. Real business processes may still require JavaScript, a backend, authentication, persistence, and validation.

Watch the complete HAVEN Estates project

In the full video tutorial, I demonstrate:

  1. The completed luxury real estate interface.
  2. The starter project before commands are added.
  3. How to connect buttons to property dialogs.
  4. How to open and close dialogs without custom JavaScript handlers.
  5. How to build the navigation popover.
  6. How to control the viewing dialog.
  7. How to test the finished interactions.

Watch the complete YouTube tutorial:

Watch the HAVEN Estates HTML Commands tutorial

Improve your responsive CSS skills

If you want to become better at building polished layouts like HAVEN Estates, check out my guide:

Build Any Responsive Layout with CSS Grid & Flexbox

Final thoughts

Modern HTML keeps getting more powerful.

The command and commandfor attributes show how browsers can handle common interface interactions without requiring custom event listeners for every button.

That doesn't mean JavaScript is obsolete. It means we can use the browser's native capabilities first and add JavaScript when our application genuinely needs it.

Have you tried HTML Invoker Commands yet?

Would you use them for dialogs and popovers in your next project?

Let me know in the comments.

Happy coding!

— CodeWithDivine

Top comments (0)