whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.13k stars 2.67k forks source link

Top layer research #4633

Open domenic opened 5 years ago

domenic commented 5 years ago

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:

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

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

giuseppeg commented 5 years ago

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.

yisibl commented 5 years ago

cc @upsuper

giuseppeg commented 5 years ago

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

upsuper commented 5 years ago

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...)

ghost commented 4 years ago

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.

Macil commented 4 years ago

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.

noamr commented 3 years ago

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.

idoros commented 3 years ago

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.

noamr commented 3 years ago

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.

idoros commented 3 years ago

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

noamr commented 3 years ago

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.

Westbrook commented 3 years ago

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...

noamr commented 3 years ago

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:

idoros commented 3 years ago

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

noamr commented 3 years ago

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

Yes, focus means focus-within

josepharhar commented 1 year ago

@domenic is this resolved now that we have the overlay property? https://github.com/whatwg/html/pull/9093

domenic commented 1 year ago

No, I don't think anyone has done what I asked in this issue, of surveying other platforms. (Not that anyone is obligated to!)

argyleink commented 2 months ago

can confirm, my tool VisBug maintains a concept of "layers of glass and priority" that are assigned to elements for complex layering.

i've also worked on a design tool, much like Figma, that had 4 layers of glass as divs, and these maintained the z order at a priority level, and internally had another z-index stack for children.

this same strategy could be used in the top-layer. "glass layer" divs could be popovers that are placed in the top layer, in the order that will never change. but, instead of putting more elements into the top layer, you insert elements into the glass layers as children. converts the top layer from being flat and trying to maintain 20+ elements in some hyper specific order, to one that creates a number of fixed layers and juggles their children.