w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.49k stars 660 forks source link

[css-positioning] position: fixed within a stacking context #400

Open valdrinkoshi opened 8 years ago

valdrinkoshi commented 8 years ago

The spec of position: fixed specifies that:

Fixed positioning is similar to absolute positioning. The only difference is that for a fixed positioned box, the containing block is established by the viewport.

https://www.w3.org/TR/css-position-3/#fixed-pos https://www.w3.org/TR/css-position-3/#containing-block

But it is not obvious what happens if a position:fixed element is contained in a parent that creates a stacking context. What happens is that the fixed element participates to the scroll and gets clipped http://jsbin.com/kehexi/3/edit?html,output

  <style>
    .container {
      border: 1px solid gray;
      width: 200px;
      height: 200px;
      overflow: auto;
      transform: translateZ(0);
    }

    .tall {
      height: 400px;
    }

    .fixed {
      position: fixed;
      padding: 20px;
      top: 80px;
      left: 80px;
      width: 150px;
      background-color: orange;
    }
  </style>

  <div class="container">
    <div class="tall"></div>
    <div class="fixed">
      I should not scroll! <br>
      I should not get clipped!
    </div>
  </div>

Resulting in

screen shot 2016-08-11 at 7 12 02 pm

Is this an expected behavior for position: fixed? From the spec, I'd expect it to render in the top stacking context and not participate to the scrolling (e.g. comment the transform: translateZ(0) of .container)

screen shot 2016-08-11 at 7 12 10 pm
upsuper commented 8 years ago

This behavior is speced in CSS Transforms spec that:

For elements whose layout is governed by the CSS box model, any value other than none for the transform results in the creation of both a stacking context and a containing block. The object acts as a containing block for fixed positioned descendants.

valdrinkoshi commented 8 years ago

Thanks for the pointer @upsuper. There are several css properties that create a new stacking context, between them position: fixed itself. It is not obvious what would be the outcome in situations where a position: fixed element is contained into a stacking context that is not the root stacking context. What I ended up doing is trying all these css properties and see what's the behavior on the different browsers; FWIW here's my findings on creating a stacking context on .container through:

I'm not sure if these are bugs or by design, and couldn't easily find the spec that defines the desired behavior. Any pointer would be greatly appreciated :)

Would it be possible to at least mention that position: fixed elements are affected by stacking contexts of their parents?

upsuper commented 8 years ago

opacity, position, mix-blend-mode, fliter: safari will crop the orange box, while chrome & firefox will not

This is a bug of Safari. Those properties only create a stacking context, and do not act as a containing block for fixed positioned descendants.

perspective: safari crops the orange box, while chrome & ff will make it scroll

Looks like the spec isn't clear that perspective property should act as a containing block for fixed positioned descendants, but I suppose it should because it should have same behavior as perspective() function for transform.

Safari's behavior here looks buggy either way, though.

will-change: safari & ff don't crop neither scroll the orange box, chrome renders it behind the scrollbar

will-change has different behavior when it has different values. What will-change value are you referring to here?

Specifying will-change: transform makes it work like transform: xxx, in both Firefox and Chrome, and Safari's behavior again looks buggy.

You can find the spec of behavior of will-change in CSS Will Change spec.

upsuper commented 8 years ago

cc @grorg @smfr for the issue of perspective property mentioned above.

valdrinkoshi commented 8 years ago

will-change has different behavior when it has different values. What will-change value are you referring to here?

I've been playing with will-change: opacity http://jsbin.com/kigiqi/2/edit?html,output, and on chrome it gets rendered behind the scrollbar: screen shot 2016-08-15 at 4 46 01 pm

upsuper commented 8 years ago

That is a bug of Chrome then, as will-change: opacity should be effectively identical to specifying opacity to a value smaller than 1 in terms of positioning.

mstange commented 8 years ago

opacity, position, mix-blend-mode, fliter: safari will crop the orange box, while chrome & firefox will not

When you checked position, did you include a test for position: sticky? position: sticky does not establish a containing block for fixed positioned descendants, but if I recall correctly, it clips in all browsers except Firefox at the moment. (As an aside, I'd like to change Firefox's behavior to match the other browsers', because it makes the implementation a little simpler.)

valdrinkoshi commented 8 years ago

My tests were done with position: fixed on .container. I just tried with position: sticky and see cropping only on safari http://jsbin.com/zivofer/2/edit?html,output

Aside: I'm working on an overlay custom element; it should be capable of rendering on top of the other content when opened (like <dialog>, <select>, or the tooltips rendering the alt attribute value). I rely on position: fixed to have it positioned in the viewport and z-index for rendering on top of the content. The above mentioned native elements (dialog, select) are stacking-context agnostic, in the sense that are not cropped, nor participate to the scrolling, and are always rendered on top of everything. I couldn't find a way to access to this magic through css yet, the only way is to ask the users of my element to declare it in a stacking-context-safe parent 😞

upsuper commented 8 years ago

No, there is no such magic thing in CSS. Some DOM APIs use a concept of top layer for putting things on the very top, which is not available in CSS, though.

valdrinkoshi commented 8 years ago

It would be pure gold to have access to the top layer stack (e.g. add/remove nodes via DOM APIs would be more than enough) 😄 Is there any WIP around it, or an issue where to follow updates?

upsuper commented 8 years ago

No, and I don't think we want that actually. Top layer is a rather hacky thing initially created for fullscreen.

oriadam commented 6 years ago

A position that is relative to viewport is necessary. Solution proposal: position: viewport in which the containing block is established by the viewport, regardless of stacking.

Another proposal: position: #element-id in which the containing block is a specific element. This way designers could attach tooltips and floating menus to elements or rows without having to use JS and listen to scrolls and element position changes. Circular definition can be a problem.

tabatkins commented 4 years ago

As far as I can tell, nothing here is an error in the Position spec, it's just a pile of browser bugs wrt what properties create fixed positioning containing blocks, and how to respond to that.

So just marking as Needs Testcase to make sure these cases are exercised in WPT.

smfr commented 4 years ago

Cropping of position:fixed inside stacking-context with overflow:hidden or scroll is a WebKit bug.