Top Layer Popup Controller
Et2TopLayerPopupController
Overview
Keeps hoisted Shoelace popups (select dropdowns, tooltips, date pickers, …) anchored to their widget when they open inside a CSS container query container.
A layout that can collapse its own columns has to know how wide it is, so it declares itself a query
container with container-type: inline-size. That is where the trouble starts. By the letter of
the spec a query container applies layout containment, which makes the element a containing block for
position: fixed descendants - but no browser actually does that part. Measured on Chrome 152
and Firefox 142, a fixed descendant of a query container still resolves against the viewport, while
contain: layout and transform on the same element both do capture it. Floating UI,
which places every Shoelace popup, believes the spec: it takes the first
container-type ancestor as the containing block and hands the popup coordinates measured from
that element’s scrolled padding box. The browser then reads the very same numbers as viewport coordinates,
so the popup lands container.scrollTop - container.top away from the field it belongs to -
already wrong when nothing is scrolled, and drifting further with every pixel scrolled. Floating UI also
treats the container as a clipping ancestor, squeezing a dropdown in a short tab panel down to a few rows.
Promoting the popup into the top layer settles the argument instead of taking a side: Floating UI
short-circuits its containing block search for a top layer element and measures from the viewport, which is
exactly where the browser paints it. Clipping ancestors drop out at the same time, so a dropdown may again
overflow the panel it lives in. Crucially the container itself is left alone - forcing real containment on
it (contain: layout) would fix these popups by breaking everything else that uses
position: fixed to escape a scrolling panel, the rich text editor’s menus among them.
Only popups that asked to escape are touched: hoist gives a popup position: fixed,
and a popup without it is deliberately placed against an offset parent inside the container and has to stay
there. A popup with no query container above it is left alone too, since it is already placed correctly and
clipped the way its widget intended.