diff --git a/hell/adventcalendar/2025/1/index.md b/hell/adventcalendar/2025/1/index.md index 0ed23d56..d4e754fd 100644 --- a/hell/adventcalendar/2025/1/index.md +++ b/hell/adventcalendar/2025/1/index.md @@ -1,35 +1,108 @@ --- title: "Top layer troubles: popover vs. dialog" -author: "Your Name" -author_bio: "Your short bio" +author: "Stephanie Eckles" +author_bio: "Stephanie Eckles is a Senior Design Engineer for Adobe Spectrum CSS, a member of the CSSWG, and the author of ModernCSS.dev. Steph has 15+ years of webdev experience that she enjoys sharing as an author, workshop instructor, and conference speaker. She's an advocate for accessibility, scalable CSS, and web standards. Offline, she's mom to two girls and enjoys baking and watercolor painting." date: 2025-12-01 author_links: - - label: "Site" - url: "https://linktoyourblog123.com" - link_label: "linktoyourblog123.com" -intro: "
Short introductory text
" + - label: "Blog" + url: "https://moderncss.dev" + link_label: "ModernCSS.dev" + - label: "Mastodon" + url: "https://front-end.social/@5t3ph" + link_label: "@5t3ph" +intro: "A sneaky accessibility conflict can arise when trying to use modal dialogs with the popover API. Learn about the conflict and how to resolve it.
" image: "advent25_1" --- -Some text. -Some text. -Some text. Some text. +Have you ever tried to set `z-index: 9999` to solve element layering issues? If so, you’ve been fighting a fundamental CSS concept of _stacking contexts_. -## Heading +The stacking context defines the order things are placed in the third dimension, or the “z” axis. Think of the z-axis as layers of DOM elements between the root of the stacking context within the viewport, and you, the user, looking through the browser viewport. -Some text. + -## Heading +An element can only be re-layered within the same stacking context. While `z-index` is the tool to do it, the failure happens due to a change in stacking contexts. This can happen in a few ways, such as a fixed or sticky positioned element, or combining position absolute or relative along with a z-index, among others [as listed on MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Stacking_context). -Some text. +A modern web feature is the “[top layer](https://developer.mozilla.org/en-US/docs/Glossary/Top_layer),” which is guaranteed to be the top-most layer above any other stacking context. It spans the entire viewport, although elements existing in the top layer may have smaller visible dimensions. +Promoting an element to the top layer breaks it free of any stacking context it may otherwise exist within. -Note: Some text.
+Although the top layer directly addresses a CSS-related issue, there is currently no property available to promote an element to the top layer. Instead, certain elements and conditions gain access to the top layer, including native dialogs via `showModal()` and elements designated as popovers. -```html -+See the Pen +conflicting by Stephanie Eckles (@5t3ph) +on CodePen. +
+ +If you are able to click on the popover, you may think it’s at least working with that method. But shortly we’ll learn that thing aren’t always as they seem. + +## Why the toast popover is unreachable + +While top layer allows beating out standard stacking contexts, items that exist within the top layer create their own layering order. The most recently added top layer item appears over previous top layer items. This is why the toast was visually appearing over the dialog’s backdrop. + +So if the popover is _visually_ available, why is it unreachable via keyboard or a screen reader’s virtual cursor? + +The reason has to do with the popover competing with a _modal_ dialog. When the native HTML dialog is launched via `showModal()`, the page outside of the dialog becomes _inert_. The state of _inert_ is a necessary accessibility behavior, which results in isolating the dialog contents, and prevents both tab and virtual cursor access to the background page. + +The bug is due to the toast popover being part of the background page’s DOM. This means it has been made inert due to being outside of the DOM boundary of the dialog. + +But, due to top layer order, since it was created after the dialog was opened, it is confusingly sitting visually over the dialog. + +If you thought clicking the popover was working, it’s in fact not, even though the popover does dismiss. What is really happening is that you are triggering the _light dismiss_ behavior of the popover. This means it is closing because you are technically clicking outside of it’s boundaries, since the dialog is capturing the click instead. + +So, while the popover was dismissed, the “Retry” button was _not_ actually clicked, meaning any associated event listener was not triggered. + +Even if you created an automated test to check your alert functionality specifically when a dialog was open, the automated test may have had a false positive because it triggered a programmatic click on the toast button. That pseudo click was falsely getting around the issue created from the dialog causing the page to be inert. + +## Regaining popover access + +The solve is two-fold: + +1. move the popover physically inside the DOM of the dialog +2. ensure use of `popover="manual"` to prevent clicks inside the dialog from prematurely triggering light dismiss on the popover + +Once we’ve done both of those things, the popover is now both visually available and fully interactive via any method. + ++See the Pen +conflicting by Stephanie Eckles (@5t3ph) +on CodePen. +
+ +## Learnings and additional considerations + +What we’ve learned is that you will need to work out a mechanism to launch popovers from within dialogs if your website or app has the possibility of both displaying at once, and they have independent timelines. + +Alternatively, you could opt to supress background page popovers until the dialog closes. This may not be ideal if notifications require timely interaction, or if the dialog contents have the potential to trigger a toast. + +Another issue you may need to handle for, besides visibility and interactivity, is if the popover needs to outlive the dialog. As in, it needs to remain open - perhaps to keep waiting for the user to take action - once the dialog is closed. + +While I am a huge proponent of using native platform features, and I think popover in particular is an incredible feature, sometimes conflict points can’t be entirely avoided. In fact, you may have already had to contend with a similar conflict against the inert behavior of a modal dialog. So, this article may mostly be a warning to not entirely rip out your previous custom popover architecture _if_ you have this potential issue of displaying background popovers and modal dialogs simultaneously. + +If this is an issue that currently, or may in the future affect your work, keep an eye on [this HTML issue where solutions are being discussed](https://github.com/whatwg/html/issues/9936). + + diff --git a/hell/images/advent2025/top-layer/stacking-context.jpg b/hell/images/advent2025/top-layer/stacking-context.jpg new file mode 100644 index 00000000..75cf44b9 Binary files /dev/null and b/hell/images/advent2025/top-layer/stacking-context.jpg differ