[Proposal] “position: viewport;” scheme


#1

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?