Mayank

Dialog is for modals, popover is for everything else

<dialog> and popover are currently the only two ways1 to access the browser’s top layer. So what’s the difference between them and which one should you use when?

Dialog is for modals

This is the easy one. The <dialog> element has an implied dialog role.

When opened using dialog.showModal():

In practice, I find that the “inert”-ing feature of modal dialogs is over-zealous and gets in the way of other features like toasts and live regions. But I won’t get into that today; it works fine in most cases.

There is a dialog.show() method as well, but it doesn’t do any of the things listed above. This particular API is utterly useless and might as well not exist. The same also applies to the open attribute. Do not use <dialog> with the show() method or the open attribute.

Popover is for… everything else

The popover attribute can be used with three modes:

Most examples will nudge you towards using popover="auto" (or value-less popover="") with the declarative no-JS binding. That’s a good start. However, most of those fail to convey that the popover attribute alone is not enough if you want to produce semantically meaningful markup3.

So here’s my take: If you’re not sure which element to use with popover, then use a <dialog>, which maps to the dialog role (as we saw above). This is a safe choice, because dialogs are allowed to contain any arbitrary content.

Code snippet
<dialog popover>…</dialog>

When you do need more specialized roles (such as listbox or menu), you could even add those on top to override the dialog’s default role (or just use a generic <div> or custom element).

Auto popovers do a lot

With popover="auto you get a few nice things (similar to dialog.showModal):

Additionally, you get a few popover="auto"-exclusive features (described in way more detail in Hidde and Scott’s article on popover accessibility):

  1. The popover can be “light dismissed” by clicking outside it4.
  2. The button will have an implied aria-expanded state when the popover is open.
  3. If the button and popover are not adjacent elements, they will be connected to each other via aria-details. This helps with quick navigation for screen reader users.
  4. If necessary, the focus order will be adjusted to ensure that interactive elements within the popover come directly after the button that triggered the popover.

The last point is the one that really stands out to me. Browsers will adjust the focus order for you! All the other features are reasonably easy to implement, but focus order is one that is really tricky to get right. This, to me, is the killer feature of popover="auto". This feature makes it possible to use popovers in combination with techniques like portaling (which continue to be necessary in some advanced cases).

I have my gripes about what popover="auto" doesn’t do (or does weirdly), but let’s save that for another day.

Manual popovers are cool too

I want to create a clear distinction between the auto and manual modes. This distinction often gets lost when we talk about the value-less popover attribute (or popover=""). That’s why I’ve been careful to describe popover="auto" behaviors under a separate heading.

I almost consider popover="auto" to be a more specialized form of popover. In my mind,

  1. popover="manual" adds the base top-layer functionality (which is the whole point of using popover).
  2. And then popover="auto" adds the above mentioned nice features on top of that.

This makes sense to me, but maybe it doesn’t make sense to you, because popover="auto" is considered the default and popover="manual" is opt-in.

In practice, I’ve found myself using popover="manual" more often than popover="auto". Using popover="manual" makes it a lot easier to migrate legacy code that’s written using z-index. That’s because the legacy code often takes care of most of the considerations listed above, and it would be difficult to rip all that code out (especially when also supporting older browser versions). Maybe once popover becomes “baseline widely available”, I’ll change my mind. We shall see.

Footnotes

  1. Technically there’s also fullscreen, but that’s a bit different and not relevant for this conversation.

  2. It used to be that the dialog would only close when Esc was pressed. Recent advances have resulted in a more general “close” action, which includes things like the “Back” gesture on Android and “z” gesture on VoiceOver iOS. See CloseWatcher which allows custom (non-dialog) components to exhibit the same behavior.

  3. Browsers have started adding group role as a fallback, however this is not guaranteed to be exposed by assistive technology unless the element also has an accessible name.

  4. Technically, this isn’t exclusive to popover anymore. Modal dialogs can also be “light dismissed” with a new closedby="auto" attribute, though this isn’t cross-browser yet.