Why does "position: absolute; left: 0; right: 0; width: XYpx; margin: 0 auto" actually center?

I'm pretty proficient when it comes to CSS but today I stumbled over a snippet that got me head-scratching (here and here).

I never thought that one can center an absolutely positioned element via margin: 0 auto but given the element in question has a set width and there's left: 0 and right: 0, it actually seems to work:

#parent
{
    background: #999;
    height: 300px;
    position: relative;
    width: 300px;
}

#child
{
    background: #333;
    height: 50px;
    left: 0;
    margin: 0 auto;
    position: absolute;
    right: 0;
    width: 50px;
}
<div id="parent">
    <div id="child"></div>
</div>

(JSFiddle)

I always thought left: 0 and right: 0 will dictate the element's width (100% of it's first relatively positioned parent) but it seems width takes precedence here and therefore makes margin: 0 auto work.

Is this defined behavior? Can I find something about it in any spec? I googled around a bit but came up with nothing useful.


Solution 1:

This is accounted for in section 10.3.7 of the CSS2.1 spec:

The constraint that determines the used values for these elements is:

'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right' = width of containing block

If none of the three is 'auto': If both 'margin-left' and 'margin-right' are 'auto', solve the equation under the extra constraint that the two margins get equal values [...]

As you can see, auto margins on both sides behave the same for absolutely-positioned elements as they do for non-positioned block-level elements, provided that the left and right offsets, as well as width, are not auto.

Interestingly, only for absolutely-positioned elements, this applies to top, height and bottom as well, which essentially means that it is possible to vertically center an absolutely-positioned element using auto margins. Again, this is provided that the three properties above are not auto, and the respective margins are auto. (In your case, this means margin: auto rather than margin: 0 auto as the latter zeroes out the vertical margins.)

Solution 2:

The left and right (and top and bottom) properties don't define width per se but rather the offset of the relative margins from the parents respective side.

In the absence of a defined width and with the offsets declared as zero, the width is calculated based on the offsets.

Giving the block a width or a height prevents the block from taking up all available space and forces the browser to calculate margin: auto based on the new bounding box

MDN - on "Top"...and the same applies to the other properties.

For absolutely positioned elements (those with position: absolute or position: fixed), it specifies the distance between the top margin edge of the element and the top edge of its containing block.

Smashing Magazine Article with more detail

Thanks to j08691 for the link