A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

[Proposal] “position: viewport;” scheme

platosha
2019-02-11

Consider that an author wants to implement a reusable component with that allows to toggle an overlay displayed in place. For example, a <select>-like dropdown menu, that shows a list with items after the user clicks to open the menu.

Let’s assume this HTML implementation:

<main>
  <p>Some content outside the menu</p>

  <nav class="menu">
    <button>Show menu</button>
    <ul hidden class="overlay">
      <li><a href="...">Option one</a></li>
      <li><a href="...">Option two</a></li>
    </ul>
  </nav>

  <p>Other content outside the menu</p>
</main>

When shown, the overlay is expected to:

  1. Show in front of everything else on the page, i. e., not be covered with other non-overlay content. For the example above, the paragraphs outside the menu should never cover the menu.

  2. Not be trapped in ancestor box, even if ancestors have overflow other that visible or a clip region. E. g., main {overflow: hidden;} should not affect the overlay.

  3. Inherit the styles from the parent, e. g., font settings, colors, etc., as usual.

  4. Be targetable with styles for the menu ancestor, e. g., the .menu .overlay selector targets the the overlay.

Currently, .overlay { position: fixed; } provides a way close to meet all the requirements above. However, there are issues with that.

  • Expectation 1 is prone to break: the overlay can get covered with other content under certain conditions. For example, when there are different stacking contexts defined for .menu and its siblings: .main > * { position: relative; z-index: 1; }, then the overlay will always be covered with the next paragraph, regardless of the overlay’s own z-index.
  • Expectation 2 is breaking too, for example, when an ancestor is using a transform rule.

Authors could avoid these issues by moving the overlay away from the .menu parent, for example, by attaching it in the end of <body>, so that it is never affected with stacking contexts and transform rules on ancestors, and such. But the downside of this approach is breaking the styling (expectations 3 and 4), because the content is moved, the regular CSS targeting and inheritance do not apply anymore.

The issues with fixed could be tolerable if an author can keep all the styles, including those for content outside the menu under control. However, in case of authoring a reusable component, the author is not in control of the styles outside the component, so avoiding issues is harder and requires some effort from the component consumers too.

I would like to propose a new positioning scheme viewport, that would act pretty much like fixed, with an exception of ignoring any ancestor transforms, stacking contexts, overflows, and clipping. Any element with position: viewport:

  • is positioned relatively to the viewport,
  • and also participates directly in the top-level stacking context.

Hopefully that would allow the component authors to display overlays meeting all the expectations above, without maintaining complex workarounds for them.

Any thoughts?

platosha
2019-02-22

@fantasai @tabatkins could you please have a look?

Now that I try again how that works in Blink, having an ancestor with a transform rule basically breaks fixed descendants. In that case, not only the stacking context is generated, but also the positioning is messed up, the fixed works like absolute in that case: it positions with respect to the ancestor with a transform rule and scrolls together with it.

Would it make sense to define another positioning scheme, that would be, essentially, same as fixed but more reliable?

Malvoz
2019-06-26

Not trying to take away from your proposal, but this seems related:

And this because you probably don’t want the UA’s UI to visually interefere: