-
Notifications
You must be signed in to change notification settings - Fork 746
Description
As a custom element author, it's not uncommon to create an element that utilizes some sort of dynamic "popover." There are many examples of this, but a few common ones include:
- Dialogs
- Dropdowns
- Combo boxes
- Tooltips
In most cases, such components require a "panel" element that "pops over" the page content. The problem is the panel is subject to containment by its ancestors. For example, a containing <div>
with overflow: hidden
will [correctly] cause unwanted clipping.
Traditional workarounds for this problem include:
- "Hoisting" the panel element to another position in the DOM (e.g. before
</body>
) - Using a fixed position strategy to "break out" of the containing element
Neither of these solutions are elegant, nor do they guarantee the panel will be positioned as intended. The former relies on DOM modifications and arbitrary z-indexes while the latter requires that no ancestors have transform
, perspective
, or filter
properties:
It is positioned relative to the initial containing block established by the viewport, except when one of its ancestors has a transform, perspective, or filter property set to something other than none (see the CSS Transforms Spec), in which case that ancestor behaves as the containing block. Source
In the world of web components, particularly when a shadow root is attached, the panel is commonly contained in the shadow root. As a result, it cannot be "hoisted" to another location in the DOM because its styles are encapsulated and slotted content will no longer work. Hoisting also makes accessibility difficult since id
attributes can no longer be referenced once they're removed from the shadow root (not to mention potential conflicts with ids in the global scope). This leaves us with only one option — the fixed position strategy.
With the fixed position strategy, there's no way to guarantee a panel's position. You can try to identify its containing block by traversing the DOM and checking for relevant properties, but that's arduous. And if the containing block isn't viewport, "fixing it" will involve altering properties that may cause visible changes to unrelated ancestors.
The Proposal
I suspect this use case will become more and more common as web components become ubiquitous. After many weeks of experimentation, I've come to the conclusion that this could better be solved at the CSS level. Therefore, I would like to propose a new position property:
.my-panel {
position: popover;
}
Just like position: fixed
, an element with position: popover
will be removed from the normal document flow, and no space is created for the element in the page layout. Unlike fixed
, the element will be positioned relative to the viewport, not its containing block. This makes sense since popovers are pseudo-modal and seldom appear off-screen.
And since naming is hard, a few alternatives might be position: overlay
, position: anchored
, or position: viewport
.