meyerweb.com

Skip to: site navigation/presentation
Skip to: Thoughts From Eric

Un-fixing Fixed Elements with CSS Transforms

In the course of experimenting with some new artistic scripts to follow up “Spinning the Web“, I ran across an interesting interaction between positioning and transforms.

Put simply: as per the Introduction of the latest CSS 2D Transforms draft, a transformed element creates a containing block for all its positioned descendants.  This occurs in the absence of any explicit positioning of the transformed element.

Let’s walk through that.  Say you have a document whose body contains nothing except a position: static (normal-flow) div that contains some absolutely-positioned descendants.  The containing block for those positioned elements will be the root element.  Nothing unusual or unexpected there.

But then you decide to declare div {transform: rotate(10deg);}.  (Or even 0deg, which will have the same result.)  Now the div is the containing block for the absolutely-positioned elements that descend from it.  It’s as though transforming an element force-adds position: relative.  The positioned elements will rotate with their ancestor and be placed according to its containing block—not that of the root element.

Okay, so that’s a little unusual but perhaps not unexpected.  I could make arguments both ways, and some of the arguments could get pretty complex.  To pick one example, if the transformed element didn’t generate a containing block, how would translate transforms be handled?

Either way, here’s where things got really troublesome for me:  a transformed element creates a containing block even for descendants that have been set to position: fixed.  In other words, the containing block for a fixed-position descendant of a transformed element is the transformed element, not the viewport.  Furthermore, if the transformed element is in the normal flow, it will scroll with the document and the fixed-position descendants will scroll with it. You can see my test case, where the red and blue boxes would overlap each other and stay fixed in place, except the second green div has been rotated.

Obviously this makes the fixed-position elements something less than fixed-position.  In effect, not only does the transformed element act as if it’s been force-assigned position: relative, the fixed descendants behave as if they’ve been force-changed to position: absolute.

I find this not only unusual and unexpected, but also a wee bit unsettling.  Personally, I think it goes too far.  Fixed-position elements should be fixed to the viewport, regardless of the transformation of their ancestors.  Of course, if you agree with my thinking there, realize that opens a whole new debate about how, or even whether, transforms of ancestors should be carried to fixed-position descendants.

I have my own intuitions about that, but this is definitely territory where intuitions are to be treated with caution.  There are a lot of interacting behaviors no matter what you do, and no matter what you do someone’s going to find the results baffling in some way or other.

But since I do have intuitions, here’s what they are:  transformed elements in the normal flow or floated do not establish containing blocks for absolutely- and fixed-position descendants.  This means that any transforms you apply to the transformed element are not applied to the positioned descendants, because transforms don’t inherit.

What if you want a normal-flow transformed element to be a containing block?  Use position: relative, same as you would if there were no transform.  And if you want the transforms to be passed on to the descendants even though no containing block is established?  The inherit value would work in some cases, though not all.  That’s where my approach runs aground, and I’m not yet sure how to get it back to sea.

Okay, so that’s what I think.  What do you think?

25 Responses»

    • #1
    • Comment
    • Mon 12 Sep 2011
    • 1220
    Jason Simanek wrote in to say...

    I agree. Why would one style property (transform) create an exception in inheritance behavior for a different style property (postion)? Seems inconsistent with the rest of CSS, but maybe I’m overlooking existing CSS styles that do this very thing.

    • #2
    • Comment
    • Mon 12 Sep 2011
    • 1352
    Stephanie Hobson wrote in to say...

    I’m with your gut on this one – especially given the use case with the fixed positioned elements.

    I think behaviour should have to be explicitly coded by the CSS author, automatic stuff seems to screw people up, especially people just learning.

    If the browser vendors can’t apply transforms *without* the element also being position:relative, then maybe the transform shouldn’t work unless position:relative is also explicitly declared (this is how z-index works now I believe).

    • #3
    • Comment
    • Mon 12 Sep 2011
    • 1410
    BrianMB wrote in to say...

    When I read that transformed elements become offsetParents, I was baffled.

    Who would possibly want this, and why? Explicit declaration of “position” takes care of this with no unexpected side-effects.

    The CSSWG should be focusing their time on useful additions to transformations, such as a way to change discreet transformation functions a la carte.

    • #4
    • Pingback
    • Mon 12 Sep 2011
    • 1508
    Received from Elegant D » Eric's Archived Thoughts: Un-fixing Fixed Elements with CSS …

    […] Source: CSS – Google Blog Search […]

    • #5
    • Comment
    • Mon 12 Sep 2011
    • 1804
    onno De Jong wrote in to say...

    I ran into the issue earlier, and thought it was a browser implementation bug. Then again, I kind of got there by accident, so I didn’t think twice. Fixed position is sparsely used in practice, and it may be OK to not be completely logical, and have some incongruities where otherwise its difficult to figure things out to their logical end. I like the idea that these blocks create alternative coordinate systems and I like the idea of using absolute positioning within these altered coordinates. But I agree with you that if an element is fixed, it should be handed off to the viewport at whatever coordinate position it was fixed.

    • #6
    • Comment
    • Tue 13 Sep 2011
    • 0027
    philippe wrote in to say...

    Hmm. Transform() doesn’t inherit, but it applies to the element and all its content. So all descendants of the transformed() element are e.g. rotated. That is similar to the way opacity works.

    But that doesn’t square with your proposal about A.P and fixed pos. elements descendant of a transformed() element being exempted unless the transformed() element is rel. positioned…

    Both behaviours can be equally unsettling or lead to unexpected results.

    • #7
    • Comment
    • Thu 15 Sep 2011
    • 0235
    Priit Pirita wrote in to say...

    This is not the answer you expect, but I write it anyway.
    One should not use static & fixed positioning. Never. Only trouble may occur if you use them and never anything positive. So feel free and add

    * {position: relative}

    somewhere at the beginning of your CSS and emulate fixed with absolute and you’ll be safe (and warm as a bonus).

    PS! I personally use also started to use the following

    * {overflow: hidden; box-sizing: border-box;}

    and never looked back.

    • #8
    • Pingback
    • Tue 20 Sep 2011
    • 2100
    Received from 2D Transforms in CSS3 | Web Directions

    […] […]

    • #9
    • Pingback
    • Wed 21 Sep 2011
    • 0520
    Received from Some links for light reading (21/9/11) | Max Design

    […] Un-fixing Fixed Elements with CSS Transforms […]

    • #10
    • Comment
    • Sun 25 Sep 2011
    • 1834
    Jon R. Humphrey wrote in to say...

    While I can understand the argument against this, as it stands this might be a “fly-swatter” that can be used to adjust for view-ports that “can’t” support position:fixed such as the iOS’ or ‘Droid?
    As we all know the viewport on these devices is just a window overtop the site which allows us to see, and scale, the area below it … with these limitations the position:fixed falls over as the boudaries are not the page edges but rather the viewport’s. Wouldn’t using this little “splat” thereby allow us to transform our fixed elements back to the page and NOT to the viewport?
    Or do I have this all wrong in my head?
    Cheers for this one Eric, I’m off to create a test case now … results to follow!

    • #11
    • Pingback
    • Fri 30 Sep 2011
    • 1056
    Received from Robert’s read: links and suggestions, September 30th 2011 - Robert's talk

    […] Un-fixing Fixed Elements with CSS Transforms […]

    • #12
    • Comment
    • Fri 30 Sep 2011
    • 1129
    Lea Verou wrote in to say...

    The reason for this is that it’s hard to implement otherwise, not that it wouldn’t make sense. Check this excerpt from the spec:

    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.
    Need to go into more detail here about why fixed positioned objects should do this, i.e., that it’s much harder to implement otherwise.

    Of course I agree with you that it’s very inconvenient and nonsensical.

    • #13
    • Comment
    • Sun 2 Oct 2011
    • 1013
    Eric Meyer wrote in to say...

    I saw that “much harder to implement” bit, Lea, but I wonder: is it really? It seems to me like it would be fairly easy to define that a fixed-position element A) always has the viewport as its containing block; and B) it doesn’t inherit transforms or their effects (at a minimum) from its parent. Or that the transforms apply, but are computed relative to the containing block (the viewport).

    • #14
    • Comment
    • Mon 23 Jan 2012
    • 1713
    web design wrote in to say...

    I would have to agree with Onno, I was feeling it was a bug with the many different browsers I was using to view my content. It should be a fixed element.

    • #15
    • Comment
    • Fri 3 Feb 2012
    • 0437
    Felix wrote in to say...

    I’m glad I’m not the only one who’s bemused by this behavior.
    For those who want to experiment with it here’s a dabblet (btw thanks, Lea!): http://dabblet.com/gist/1723937

    Is there any known workaround to keep the inner div fixed?

    • #16
    • Comment
    • Fri 17 Aug 2012
    • 0414
    Jens Riisom Schultz wrote in to say...

    This was driving me bonkers. It was nice to find an explanation, though I was hoping for a workaround too… I may be able to substitute fixed for absolute in the case I am working on, though…

    • #17
    • Comment
    • Sun 2 Sep 2012
    • 1551
    Kushagra Gour wrote in to say...

    Is there any work around to keep the fixed position elements fixed?

    • #18
    • Comment
    • Sun 17 Mar 2013
    • 0950
    Vesa Piittinen wrote in to say...

    Another bug/issue is caused by this: say I have a relative element next to a static element and they overlap. Relative element should always stay over the static element. However if I apply opacity (less than 1) or transform (other than none) then the static element is drawn over the relative element if the relative element is before the static element in DOM.

    The fix is of course easy, just add z-index, but this just feels so wrong. I made a test page at CodePen.

    • #19
    • Comment
    • Thu 22 Aug 2013
    • 2019
    Joel Padua wrote in to say...

    I’ve found a way of working this out, but the results on ipad suck.

    You can emulate the idea of fixed elements by using jquery to find the windows position related to the original position of the div, and assign the top styling to the div. Somehow you also need to get the height from the window height and assign it too since the 100% won’t work.

    The result as I said return an emulation of a fixed element but when you scroll on a device like ipad, the images only goes to the position when scrolling stops.

    • #20
    • Comment
    • Fri 6 Sep 2013
    • 2022
    Chuck Kollars wrote in to say...

    Let me describe how I think of a browser interpreting HTML/CSS, and see if it changes your expectations. The way it used to work, on the first pass, the browser parsed and displayed each and every item in its own layer. Then it went back and collapsed both the explicit (z-index) and implicit (background, etc.) layers. The new CSS transpositions are a third pass in the middle. As before, on the first pass, the browser parses and displays each and every item in its own layer. Then the new second pass, whenever it finds transposition instructions, simply picks up all the pixels already drawn in that area of that layer and moves them. What it works with is _pixels_; the CSS is not relevant at that point. The browser has no idea which element drew which pixels, so it can’t skip doing anything with only certain pixels (or look “under” a pixel, since it’s working with only one layer at a time). That’s why subelements seem to always “inherit” no matter what you do, and why position “fixed” behaves unexpectedly: all the subelements are already drawn, and it’s simply the pixels that are transposed. Finally, as before, on the last pass the browser collapses all the layers.

    • #21
    • Comment
    • Mon 24 Mar 2014
    • 0438
    Chris wrote in to say...

    Seems Firefox and Chrome still have the fixed is not really fixed behaviour, IE11 actually makes a fixed element really fixed to the viewport.

    (BTW, care to declare a max-width to your Content? IE11 Metro in Fullscreen in 2560×1440 is kinda hard to read ;)

    • #22
    • Comment
    • Thu 27 Mar 2014
    • 1524
    Sumit wrote in to say...

    I’m on your side as well. We have a position property for a reason. transforms should not work unless we explicitly set position: relative. It’s the same with z-index.

    • #23
    • Comment
    • Fri 28 Mar 2014
    • 2301
    Joan wrote in to say...

    As I checked in my IE11, the inner position: fixed element just stays fixed and ignores all those transforms. I think this approach better.

    • #24
    • Comment
    • Wed 28 May 2014
    • 0935
    Kumar Harsh wrote in to say...

    I believe IE11 does that because the child elements do not inherit the transforms, so maybe there are other areas where it falls short, like fixed positioning. Ref: http://msdn.microsoft.com/en-in/library/ie/hh673529(v=vs.85).aspx

    Chuck Kollar’s explanation does come a little closer to the issue, although it is incredibly irritating to have the ONE BELIEF about css positioning shaken! :P

    • #25
    • Pingback
    • Wed 26 Nov 2014
    • 2111
    Received from How to: Fixed Position but Relative to Container | SevenNet

    […] I did some research, and found that the issue was already been covered by Eric Meyer and even if it felt like a “trick”, turns out that this is part of the […]

Leave a Comment

Line and paragraph breaks automatic, e-mail address required but never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



Remember to encode character entities if you're posting markup examples! Management reserves the right to edit or remove any comment—especially those that are abusive, irrelevant to the topic at hand, or made by anonymous posters—although honestly, most edits are a matter of fixing mangled markup. Thus the note about encoding your entities. If you're satisfied with what you've written, then go ahead...


September 2011
SMTWTFS
August October
 123
45678910
11121314151617
18192021222324
252627282930  

Sidestep

Feeds

Extras