5 min read

Exploring the Risks and Applications of the Top Layer Feature

Table of Contents

You’ll Never Know Until You Try

When you use the showModal() method to display an HTML5 <dialog> element, it automatically moves to the top of the stacking order, centers itself, and adds a black overlay. It’s clearly a better option than using the show() method or simply setting the open attribute to display a dialog.

With this in mind, I recently made significant changes to the dialog component in the UI lib, ensuring all dialogs were displayed using the showModal() method (achieved by overriding the open attribute).

At first, I was quite pleased with the results. However, when put into practice, a major issue arose: because the <dialog> element occupies the top layer, custom validation prompts, floating elements, toast notifications, and similar components became invisible. These components create their elements under the <body> element, but with the dialog taking precedence, their styles were completely overridden, leaving users unable to see them.

The only workaround was to append the toast elements and floating elements directly to the <dialog> element itself, which turned out to be very cumbersome.

In the end, I had to compromise: only alert and confirm dialog types would use the showModal() method, while other dialog types would revert to the traditional show() method with JavaScript managing the z-index.

This goes to show that features that look fantastic on paper don’t always work as well in practice.

It’s a bit like choosing a partner who looks stunning with a great figure—living together might not always be as comfortable as you imagined.


What does the top layer feature mean?

What exactly is the “top layer feature” that has been a topic of discussion for a while?

MDN has a dedicated document explaining this term, see: https://developer.mozilla.org/en-US/docs/Glossary/Top_layer

In simple terms, it refers to a separate layer created by the browser outside the document. This layer completely covers the browser’s viewport and is positioned above all page content.

If you open the console and inspect the elements, you’ll see the #top-layer label, for example:

caniuse

If you see this label, it means the page has a top layer element.

How to create a top layer element?

Currently, there are a few ways to create a top layer element:

  • You can use the Element.requestFullscreen() method to make an element fullscreen.
  • The <dialog> element, which is displayed using the showModal() method, as explained above.
  • The pop-up element displayed using the HTMLElement.showPopover() method.

Among them, requestFullscreen() allows any element to enter fullscreen mode, while showPopover() allows any HTML element to display as a pop-up.

As for CSS Anchor Positioning (CSS Anchor Positioning API), although it’s a native browser positioning effect, it won’t make the element appear as a top layer. Therefore, the current ways to create top layer elements are the three mentioned above.


Avoiding Hidden Risks

To make custom tip effects appear above a dialog with top-layer characteristics, the only solution is to make the custom tip element a top-layer element as well. Since top-layer elements follow the rule that later elements have a higher display layer, this method will ensure that the tip appears above the dialog box.

Ultimately, the best solution is to use the showPopover() method to display Tips or Toast effects.

As a result, the traditional display method using HTMLElement.style.display=“block” in components should no longer be used. Instead, set the popover attribute for the corresponding tip element and then call the showPopover() method.

Here’s a demonstration case. The interaction works as follows: when the button is clicked, a modal dialog appears. Inside the modal dialog, there’s another button to show a toast tip. After clicking it, the tip box is displayed. Note that the tip box is implemented using the showPopover() method.

Below is the complete HTML for the demo:

<button onclick="dialog.showModal();">click me to show modal</button>
<dialog id="dialog">
    <blockquote>
        <button onclick="toast.showPopover();">show toast</button>
        <button onclick="dialog.close();">hide</button>
    </blockquote>
</dialog>

<div id="toast" class="toast" popover>I'm content</div>

After clicking through the steps, the toast will appear above the dialog box!

Currently, there are two top-layer elements on the page, as shown in the screenshot below:

demo

The cost of modification is minimal, and technically it is completely feasible.

However, considering compatibility, Safari only started supporting it from version 17, released on September 26, 2023. A large number of users have not yet upgraded to this version, so the component update will need to be delayed for about another year. At that time, all elements in LuLu UI that require top-layer display will be implemented using the popover method.

For now, if encountering a situation where top-layer elements are covering, the only solution is to treat the tooltip element as a child of the top-layer element.