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

Top layer research #4633

Open
domenic opened this issue May 16, 2019 · 15 comments
Open

Top layer research #4633

domenic opened this issue May 16, 2019 · 15 comments

Comments

@domenic
Copy link
Member

@domenic domenic commented May 16, 2019

This is a continuation of explorations in #897 and #4535 about the discussions on exposing the "top layer" primitive to web developers, instead of just to <dialog> and fullscreen. /cc @idoros @tabatkins @muan @alice as folks I've chatted with about this recently.

For this issue, let's focus on top-layer only, not on the focus/tab-trapping/inertness aspects of "blocking elements" that are explored in #897. We can continue the discussion about how those fit together in #897; I want to keep this more tightly scoped.

I suspect ultimately any solution here may end up in CSS, not HTML, but let's start some discussions focused on the research and problem space here.

My team at Chrome is interested in exploring this space, but we think there's a decent bit of research to do first. Some questions that come to mind in particular:

  • Native desktop platforms have very complicated layering: e.g. you can have dialogs, nested dialogs, modal dialogs, right-click menus, dropdown <select>-style menus, tooltips... All of these things are kind of competing for being "on top", and yet I've almost never noticed something going wrong and something appearing in an order that I as the user did not expect. How do they accomplish that? What programming model do they present to their app developers?
  • Native mobile platforms have less complexity, but still some interesting interactions: e.g. toasts, press-and-hold menus, action sheets, modals. Same question---what programming model enables this?
  • Some web toolkits, especially ones that are meant for use in a "pervasive" way that controls the entire page like AMP and Bootstrap, similarly coordinate among their various components to manage this. (They can do so successfully without a platform primitive because they control everything on the page.) Similarly, probably individual web apps have imposed a coordination hierarchy on the controls they use, even if it's just with pre-defined z-index values. What does that look like?

Some technologies that I can imagine being involved, but without research I have no evidence around, are:

  • Multiple "top" layers. E.g., one per modal dialog, to ensure that a modal dialog is above anything that appears below it, but that within the modal dialog, there can still be other top-ish things like right-click menus.
  • A fixed number of top layers (perhaps per dialog/window): e.g. one for menus, one for toasts, one for dropdowns.

Any help on this research, perhaps from people with a background in these technologies, would be appreciated.

@giuseppeg
Copy link

@giuseppeg giuseppeg commented Jul 16, 2019

fwiw this is how I have been managing layers in React for a while https://github.com/giuseppeg/react-layers-manager and it works well. A practical application can be found here.

Loading

@yisibl
Copy link

@yisibl yisibl commented Sep 19, 2019

Loading

@giuseppeg
Copy link

@giuseppeg giuseppeg commented Sep 19, 2019

this might be useful https://www.youtube.com/watch?v=2c-XLFSmn14 (similar to the layer managers thing of mine)

Loading

@upsuper
Copy link
Member

@upsuper upsuper commented Sep 24, 2019

yet I've almost never noticed something going wrong and something appearing in an order that I as the user did not expect.

I have a feeling that I saw that a lot, probably because I'm using some weird stuff...

Anyway, one data point is that macOS apparently uses fixed number of "level"s for different kinds, which can be seen in the document of NSWindow.Level.

That being said, I'm not totally sure whether exposing top layer as a primitive to developers is a good or bad idea. (I think I should have thought about this but have forgot since then...)

Loading

@ghost
Copy link

@ghost ghost commented Jan 9, 2020

What if we had a property style.inheritStackingContextOfElement = element; (making the name verbose to clarify its purpose) so that the element that has it would inherit the stacking context of the provided element, for example:

<body id="top">
  <outer-elements-creating-new-stacking-context>
      <custom-dialog></custom-dialog>
   </outer-elements-creating-new-stacking-context>
</body>
const dialogElement = document.querySelector("custom-dialog");
dialogElement.style.inheritStackingContextOfElement = document.getElementById("top");
dialogElement.blockingElement = true;

This then in combination with blockingElement/inert (for modal-like experiences) would make it much easier to implement modals and popups in a flexible way.

I'm mentioning flexibility here because there are cases where one wants to do "scoped" faux-modals that is modals that appear say inside a tab and only block the content of the tab and not any elements outside the tab set, one wouldn't be able to implement this with just the top layer exposed.

Loading

@Macil
Copy link

@Macil Macil commented Mar 4, 2020

Here's a few examples of libraries that handle this which I think have some useful desireable properties: React-Modal and React-Float-Anchor (disclaimer: I made this one). They both are libraries that provide a JSX element which can be put anywhere in the page which transparently puts its children into a new element at the end of body with position:fixed. This technique allows them to be used even from within a small element that uses overflow:hidden. In both libraries, if you nest the Modal/FloatAnchor element inside of another, then the nested one's contents will be rendered in a subsequent child of the body and therefore on top. In the Modal case, this means it just works if the content of a modal opens (renders) another modal, and similarly for FloatAnchors, this makes it natural to make submenus.

Loading

@noamr
Copy link
Contributor

@noamr noamr commented Nov 18, 2020

Note that sometimes stacking contexts are used as a form of content security.

For example, in wikipedia, the body of an article can use any HTML/css, but because it explicitly has a z-index of 0, it creates a stacking context, and if with pure CSS the internal content could override that context and appear in the top layer, a user-generated piece of content could occlude important navigation and banners. I am sure more places in the web that include user-generated content would incur that problem if stacking contexts were overridable from the inside.

Loading

@idoros
Copy link

@idoros idoros commented Nov 18, 2020

stacking contexts are used as a form of content security

That's very interesting, does anyone know if security was ever a use case for stacking context? or any other websites cases like this?

In the Wikipedia case the content can rise above it's context, but some parts of the website are designed to always be above any content that is nested under the main content (the content is not clipped). This requires the site to be intentionally designed so that known parts floats higher than others. This probably causes other issues because while they might want to prevent user content from overlapping with outside parts, legitimate popovers might want to take more space without links overlapping them and without JS manipulating the DOM outside of the content.

It would be useful if a website could be able to mark and sort layers somehow, so it can declare unknown layers to raise according to the top layer rules, while keeping specific elements drawn above any unknown layer, and allowing specific trusted layers to raise higher.

Loading

@noamr
Copy link
Contributor

@noamr noamr commented Nov 18, 2020

I'm thinking a solution to this would have to include some JavaScript, something along the lines of requestFullscreen. (Element.requestTopLayer() or Element.requestStackingParent(otherElement)) ?.

Trying to solve it in pure-CSS, considering the stacking context "contract" between the element's children and an element's surrounding may end up being over-complicated.

Loading

@idoros
Copy link

@idoros idoros commented Nov 18, 2020

Do you mean a request to the user, or to some other code controlled by the website?

Loading

@noamr
Copy link
Contributor

@noamr noamr commented Nov 18, 2020

Do you mean a request to the user, or to some other code controlled by the website?

It's a request to the page. It can be set rather than request, but I can imagine cases where it wouldn't be possible, e.g. in cases where a stacking context is created from something other than z-index, like opacity/overflow/transform.

Though it could be neat if setStackingParent would also transfer the tree of everything that would normally create a stacking context:
opacity, transform, mix-blend-mode etc.

Loading

@Westbrook
Copy link

@Westbrook Westbrook commented Nov 25, 2020

Yes, an approach to displaying content that exists "in tree/cascade/inheritance path" but appears visually "outside of tree/cascade/inheritance" (e.g. body > :last-child) would be huge!

I particularly like the "automatic" nature of @sfdciuie's suggestion of an element as not having to call an imperative API with element references might help to mitigate any issues around shadow DOM encapsulation that might come up here. However, a cue to the browser along the lines of content-visibility that instead of saying when to render an element said where to render the element would have the benefit of being able to apply to across any number of semantic elements instead of wrapping said elements.

To the OP's point, this "top" concept essentially opens an additional layers on top of the existing z-index layer, while this means the platform could establish specific groupings of z, is it possible that we create an API by which a developer could outline these for their own applications? I could see this manifesting a bit like the imperative slots API. The handrails likely leads to earlier success for a developer, but freedom likely opens the doors for qualities of success as yet unseen...

Loading

@noamr
Copy link
Contributor

@noamr noamr commented Nov 25, 2020

Do you mean a request to the user, or to some other code controlled by the website?

It's a request to the page. It can be set rather than request, but I can imagine cases where it wouldn't be possible, e.g. in cases where a stacking context is created from something other than z-index, like opacity/overflow/transform.

Though it could be neat if setStackingParent would also transfer the tree of everything that would normally create a stacking context:
opacity, transform, mix-blend-mode etc.

Actually I think there could be a way to circumvent the content-security thing without an imperative API,
by allowing the stacking context parent to limit the declarative API to elements that focused or hovered.

This would also make this capability less "general purpose" and more directed at the use case of fly-outs/popovers.

I'm thinking of an idea like this:

  • Extend the existing isolation CSS property, with new values
  • front or (popover or over or flyout) would represent an element that can overflow/stack above its stacking context
  • To protect from 3rd party content breaking isolation, the isolating element (the element currently creating the stacking context) can define isolation: isolate allow-focus allow-hover, allowing elements to break out from their stacking context when they're focused or hovered.
  • If the restriction is not necessary, the stacking-context element can define isolation: isolate allow-all or something like that.

Loading

@idoros
Copy link

@idoros idoros commented Nov 25, 2020

Would that work with focus-within to allow interactive popovers?

Loading

@noamr
Copy link
Contributor

@noamr noamr commented Nov 25, 2020

Would that work with focus-within to allow interactive popovers?

Yes, focus means focus-within

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
9 participants