Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Top Layer Stack Management #10370

Closed
Link2Twenty opened this issue May 26, 2024 · 11 comments
Closed

Feature Request: Top Layer Stack Management #10370

Link2Twenty opened this issue May 26, 2024 · 11 comments
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest

Comments

@Link2Twenty
Copy link

What problem are you trying to solve?

Inconsistent Stacking: Right now, elements in the top layer are added on top of each other based solely on the order they are added. This can cause issues for UI elements that need to stay on top, such as toast notifications or live chat. A new element added later might unintentionally cover them.

What solutions exist today?

Manual Removal and Re-addition: A developer can remove the element they want on top from the top layer temporarily using JavaScript. Then, after adding the new item, they can re-add the desired item back to the top layer. There currently is no way to detect items being added to the top layer stack you must track this yourself as you promote items.

This manual tracking will become impractical as more and more libraries use top layer (via popover and dialog).

How would you solve it?

We'd need a few things added to be able to do this nicely,

  • An event that lets us know when a new item has been added to the top layer stack. I'd assume this event would be on the window, document or body unless a new window.topLayer property was added.
  • A way to check what is in the top layer stack and in what order. Again I'm uncertain of how this would look in practice unless it was added to the afore mentioned window.topLayer property.
  • The ability to change the order of an element on the stack. I can't think of a reason to fully reorder the stack so perhaps just being able to move a specific element back to the top would be enough. I would imagine something like element.sendToTop() or window.topLayer.bringToTop(element).

I think the first two points can be solved by making window.topLayer return an Element that can be observed with a mutation observer. I'm not sure how practical this approach would be but here is a snippet of how I'd imagine it working.

const el = document.querySelector('#important-element')

const callback = (list) => {
 if(list.every(({ type }) => type !== 'childList')) return;

 const childArray = Array.from(window.topLayer.children);
 const childPosition = childArray.indexOf(el);
 
 // stop if the element is not in the top layer or if the element is already on top
 if (childPosition === -1 || childPosition === childArray.length - 1) return;

 window.topLayer.bringToFront(el);
}

const observer = new MutationObserver(callback);

observer.observe(window.topLayer, { childList: true });

Anything else?

Whilst this is not a major issue, I'm sure it will start being a thorn in the side of developers as popover and dialog become more prevalent.

This issue is on the toastify and shows that it is already becoming something noticed by the community (even if slowly).

Also here is an exchange I had on twitter/x with @bramus looking for an existing solution.

@Link2Twenty Link2Twenty added addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest labels May 26, 2024
@bramus
Copy link

bramus commented May 27, 2024

Another workaround I’ve seen authors do is the creation of predefined swimlanes (or layers) of empty divs with pointer-events set to none. These swimlands get shown in a single (manual) popover that is constantly active on the page.

To show something in the top layer, instead of triggering showPopover() on an Element, they move the targeted element to the correct swimlane. See https://codepen.io/bramus/pen/MWdbyNG for an example.

I don’t think this workaround is very practical to work and kinda defeats the whole purpose.

A practical use case where an author used this was to keep tooltips visible on top of toasts, while keeping the toasts on top of a dialog that gets shown later in the page’s lifetime.

@Link2Twenty
Copy link
Author

An aspect I missed, thanks to @lcoronelp for pointing it out, is that non-modal items moved above a modal dialog will still be inert even if visually they are on top.

See https://jsfiddle.net/link2twenty/75xnL2ap/

For toast specifically this can be an issue if the toast contains a call to action or a dismiss button. A solution would be to not open any modals and handle inert manually, though this feels prone to accident.

@annevk
Copy link
Member

annevk commented May 28, 2024

This is tracked in #9075.

@annevk annevk closed this as not planned Won't fix, can't repro, duplicate, stale May 28, 2024
@Link2Twenty
Copy link
Author

I don't believe #9075 lines up exactly with what I'm trying to solve, though does cover part.

API to get top layer elements

  • Expose an API that allows checking which element is on top.

Top Layer Stack Management

  • Expose an API to see entire top layer stack in render order.
  • Add method for placing element already in the top layer back on top.
  • Event or Mutation for tracking top layer changes.

What is the correct protocol here? Should I make more specific issues with no overlap, take my comments over to 9075 or just perhaps I've misunderstood and all the comments are covered?

@annevk
Copy link
Member

annevk commented May 29, 2024

What part do you think is not covered? Pretty sure it touches on all of that (though not all in OP).

@Link2Twenty
Copy link
Author

I've just reread the other issue, maybe I'm missing something but it doesn't seem to have;

@OliverJAsh
Copy link

OliverJAsh commented Jun 22, 2024

Inconsistent Stacking: Right now, elements in the top layer are added on top of each other based solely on the order they are added. This can cause issues for UI elements that need to stay on top, such as toast notifications or live chat. A new element added later might unintentionally cover them.

At Unsplash this issue is preventing us from migrating from react-modal to native modal <dialog>. We need the ability to show multiple different elements above the modal dialog:

  • "Say thanks!" dialog/prompt that appears after downloading a photo
  • Flash messages that appear when there's an error
  • Global progress bar when we're loading data

All of these elements may appear before or after the modal dialog has been opened, but they should always sit on top of the modal dialog, and they should not be inert when the modal dialog is open.

If we implemented these components as manual popovers then I believe that—based on the discussion above—we would have the following issues:

  • They would be inert because of the modal dialog.
  • When the modal dialog is opened, they would appear behind, so we would need to re-insert them. This feels like a hack.

It feels like we need a way to control the order of elements in the top layer (like z-index but relative to a different stacking context). Then, any elements stacked above modal dialogs would not be inert—only elements stacked behind would be inert.

image

@keithamus
Copy link
Contributor

If you’re able to, you can insert those elements within the modal dialog which will ensure they’re not inert and they’ll layer correctly.

@OliverJAsh
Copy link

If you’re able to, you can insert those elements within the modal dialog which will ensure they’re not inert and they’ll layer correctly.

I considered doing this but it means we'd have to render the elements both inside and outside the modal dialog, so they appear even when the modal dialog is closed. It gets even more complicated when you consider that we have nested modal dialogs, so we'd have to render them inside there as well! 🤯

@Link2Twenty
Copy link
Author

Link2Twenty commented Jul 1, 2024

I have the beginning of a musing

https://codesandbox.io/p/sandbox/modal-change-xpskc7

still feels a little hacky but perhaps may help.


The basic premise is to use a MutationObserver to listen for attribute changes over the entire body, we're listening for the open attribute and filtering the results down to just modal dialog elements. Then we can move the popover element into the newly opened dialog and trigger showPopover again.

I know this example is in React but I'm fairly certain it would be quite easy to implement in vanilla using appendChild this method also only works for dialogs not other popover elements and I'm dubious of how safe it actually is.

Again I'd feel much happier with an event on the window or the ability to watch for mutations in the top layer specifically.

@raythurnvoid
Copy link

raythurnvoid commented Sep 30, 2024

I have the beginning of a musing

https://codesandbox.io/p/sandbox/modal-change-xpskc7

This is the symptom that this api, is insufficient, we need the ability to control what element should show on top similarly to how we do it with z-index.

The modal element cannot arbitrarily decide to go on top of everything else. It can be z-index or it can be the order of the element in the DOM hierarchy, but we really need a way to control this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest
Development

No branches or pull requests

6 participants