loading...

CSS3 – Positioning

The idea behind positioning is fairly simple. It allows you to define
exactly where element boxes will appear relative to where they would
ordinarily be—or position them in relation to a parent element, another
element, or even to the viewport (e.g., the browser window) itself.

Basic Concepts

Before we delve into the various kinds of positioning, it’s a good idea
to look at what types exist and how they differ. We’ll also need to
define some basic ideas that are fundamental to understanding how
positioning works.

Types of Positioning

You can choose one of five different types of positioning, which affect
how the element’s box is generated, by using the position property.

The values of position have the following meanings:

static

The element’s box is generated as normal. Block-level elements generate
a rectangular box that is part of the document’s flow, and inline-level
boxes cause the creation of one or more line boxes that are flowed
within their parent element.

relative

The element’s box is offset by some distance. The element retains the
shape it would have had were it not positioned, and the space that the
element would ordinarily have occupied is preserved.

absolute

The element’s box is completely removed from the flow of the document
and positioned with respect to its containing block, which may be
another element in the document or the initial containing block
(described in the next section). Whatever space the element might have
occupied in the normal document flow is closed up, as though the element
did not exist. The positioned element generates a block-level box,
regardless of the type of box it would have generated if it were in the
normal flow.

fixed

The element’s box behaves as though it was set to absolute, but its
containing block is the viewport itself.

sticky

The element is left in the normal flow, until the conditions that trigger
its stickiness come to pass, at which point it is removed from the
normal flow but its original space in the normal flow is preserved. It
will then act as if absolutely positioned with respect to its containing
block. Once the conditions to enforce stickiness are no longer met, the
element is returned to the normal flow in its original space.

Don’t worry so much about the details right now, as we’ll look at each
of these kinds of positioning later. Before we do that, we
need to discuss containing blocks.

The Containing Block

In general terms, a containing block is the box that contains another
element. As an example, in the normal-flow case, the root element
(html in HTML) is the containing block for the body element, which
is in turn the containing block for all its children, and so on. When it
comes to positioning, the containing block depends entirely on the type
of positioning.

For a non-root element whose position value is relative or static,
its containing block is formed by the content edge of the nearest
block-level, table-cell, or inline-block ancestor box.

For a non-root element that has a position value of absolute, its
containing block is set to the nearest ancestor (of any kind) that has a
position value other than static. This happens as follows:

  • If the ancestor is block-level, the containing block is set to be
    that element’s padding edge; in other words, the area that would be
    bounded by a border.

  • If the ancestor is inline-level, the containing block is set to the
    content edge of the ancestor. In left-to-right languages, the top and
    left of the containing block are the top and left content edges of the
    first box in the ancestor, and the bottom and right edges are the bottom
    and right content edges of the last box. In right-to-left languages, the
    right edge of the containing block corresponds to the right content edge
    of the first box, and the left is taken from the last box. The top and
    bottom are the same.

  • If there are no ancestors, then the element’s containing block is
    defined to be the initial containing block.

There’s an interesting variant to the containing-block rules when it
comes to sticky-positioned elements, which is that a rectangle is
defined in relation to the containing block called the
sticky-constraint rectangle. This rectangle has everything to do with
how sticky positioning works, and will be explained in full later, in
“Sticky Positioning”.

An important point: positioned elements can be positioned
outside of their containing block. This is very similar to the way in
which floated elements can use negative margins to float outside of
their parent’s content area. It also suggests that the term “containing
block” should really be “positioning context,” but since the
specification uses “containing block,” so will I. (I do try to minimize
confusion. Really!)

Offset Properties

Four of the positioning schemes described in the previous
section—relative, absolute, sticky, and fixed—use four distinct
properties to describe the offset of a positioned element’s sides with
respect to its containing block. These four properties, which are
referred to as the offset properties, are a big part of what makes
positioning work.

These properties describe an offset from the nearest side of the
containing block (thus the term offset properties). For example, top
describes how far the top margin edge of the positioned element should
be placed from the top of its containing block. In the case of top,
positive values move the top margin edge of the positioned element
downward, while negative values move it above the top of its
containing block. Similarly, left describes how far to the right (for
positive values) or left (for negative values) the left margin edge of
the positioned element is from the left edge of the containing block.
Positive values will shift the margin edge of the positioned element to
the right, and negative values will move it to the left.

Another way to look at it is that positive values cause inward offsets,
moving the edges toward the center of the containing block, and negative
values cause outward offsets.

The implication of offsetting the margin edges of a positioned element
is that everything about an element—margins, borders, padding, and
content—is moved in the process of positioning the element. Thus, it is
possible to set margins, borders, and padding for a positioned element;
these will be preserved and kept with the positioned element, and they
will be contained within the area defined by the offset properties.

It is important to remember that the offset properties define an offset
from the analogous side (e.g., left defines the offset from the left
side) of the containing block, not from the upper-left corner of the
containing block. This is why, for example, one way to fill up the
lower-right corner of a containing block is to use these values:

top: 50%; bottom: 0; left: 50%; right: 0;

In this example, the outer-left edge of the positioned element is placed
halfway across the containing block. This is its offset from the left
edge of the containing block. The outer-right edge of the positioned
element, however, is not offset from the right edge of the containing
block, so the two are coincident. Similar reasoning holds true for the
top and bottom of the positioned element: the outer-top edge is placed
halfway down the containing block, but the outer-bottom edge is not
moved up from the bottom. This leads to what’s shown in Figure 11-1.

Figure 11-1. Filling the lower-right quarter of the containing block
Note

What’s depicted in Figure 11-1, and in most of the examples in this
chapter, is based around absolute positioning. Since absolute
positioning is the simplest scheme in which to demonstrate how top,
right, bottom, and left work, we’ll stick to that for now.

Note the background area of the positioned element. In Figure 11-1, it has
no margins, but if it did, they would create blank space between the
borders and the offset edges. This would make the positioned element
appear as though it did not completely fill the lower-right quarter of
the containing block. In truth, it would fill the area, but this fact
wouldn’t be immediately apparent to the eye. Thus, the following two
sets of styles would have approximately the same visual appearance,
assuming that the containing block is 100em high by 100em wide:

#ex1 {top: 50%; bottom: 0; left: 50%; right: 0; margin: 10em;}
#ex2 {top: 60%; bottom: 10%; left: 60%; right: 10%; margin: 0;}

Again, the similarity would be only visual in nature.

By using negative offset values, it is possible to position an element
outside its containing block. For example, the following values will
lead to the result shown in Figure 11-2:

top: 50%; bottom: -2em; left: 75%; right: -7em;

Figure 11-2. Positioning an element outside its containing block

In addition to length and percentage values, the offset properties can
also be set to auto, which is the default value. There is no single
behavior for auto; it changes based on the type of positioning used.
We’ll explore how auto works later on, as we consider each of the
positioning types in turn.

Width and Height

There will be many cases when, having determined where you’re going to
position an element, you will want to declare how wide and how high that
element should be. In addition, there will likely be conditions where
you’ll want to limit how high or wide a positioned element gets, not to
mention cases where you want the browser to go ahead and automatically
calculate the width, height, or both.

Setting Width and Height

If you want to give your positioned element a specific width, then the property to turn to is width. Similarly, height will let you
declare a specific height for a positioned element.

Although it is sometimes important to set the width and height of an
element, it is not always necessary when positioning elements. For
example, if the placement of the four sides of the element is described
using top, right, bottom, and left, then the height and
width of the element are implicitly determined by the offsets. Assume
that we want an absolutely positioned element to fill the left half of
its containing block, from top to bottom. We could use these values,
with the result depicted in Figure 11-3:

top: 0; bottom: 0; left: 0; right: 50%;

Figure 11-3. Positioning and sizing an element using only the offset properties

Since the default value of both width and height is auto, the
result shown in Figure 11-3 is exactly the same as if we had used these
values:

top: 0; bottom: 0; left: 0; right: 50%; width: 50%; height: 100%;

The presence of width and height in this example add nothing to the
layout of the element.

If we were to add padding, a border, or a margin to the
element, then the presence of explicit values for height and width
could very well make a difference:

top: 0; bottom: 0; left: 0; right: 50%; width: 50%; height: 100%;
    padding: 2em;

This will give us a positioned element that extends out of its
containing block, as shown in Figure 11-4.

Figure 11-4. Positioning an element partially outside its containing block

This happens because (by default) the padding is added to the content
area, and the content area’s size is determined by the values of
height and width. In order to get the padding we want and still have
the element fit inside its containing block, we would either remove the
height and width declarations, explicitly set them both to auto,
or set box-sizing to border-box.

Limiting Width and Height

Should it become necessary or desirable, you can place limits on an
element’s width by using the following properties, which I’ll refer to
as the min-max properties. An element’s content area can be defined to
have minimum dimensions using min-width and min-height.

Similarly, an element’s dimensions can be limited using the properties
max-width and max-height.

The names of these properties make them fairly self-explanatory. What’s
less obvious at first, but makes sense once you think about it, is that
values for all these properties cannot be negative.

The following styles will force the positioned element to be at least
10em wide by 20em tall, as illustrated in Figure 11-5:

top: 10%; bottom: 20%; left: 50%; right: 10%;
    min-width: 10em; min-height: 20em;

Figure 11-5. Setting a minimum width and height for a positioned element

This isn’t a very robust solution since it forces the element to be at
least a certain size regardless of the size of its containing block.
Here’s a better one:

top: 10%; bottom: auto; left: 50%; right: 10%;
    height: auto; min-width: 15em;

Here we have a case where the element should be 40% as wide as the
containing block but can never be less than 15em wide. We’ve also
changed the bottom and height so that they’re automatically
determined. This will let the element be as tall as necessary to display
its content, no matter how narrow it gets (never less than 15em, of
course!).

Note

We’ll look at the role auto plays in the height and width of
positioned elements in the upcoming section, “Placement and Sizing of Absolutely Positioned Elements”.

You can turn all this around to keep elements from getting too wide or
tall by using max-width and max-height. Let’s consider a situation
where, for some reason, we want an element to have three-quarters the
width of its containing block but to stop getting wider when it hits 400
pixels. The appropriate styles are:

left: 0%; right: auto; width: 75%; max-width: 400px;

One great advantage of the min-max properties is that they let you mix
units with relative safety. You can use percentage-based sizes while
setting length-based limits, or vice versa.

It’s worth mentioning that these min-max properties can be very useful
in conjunction with floated elements. For example, we can allow
a floated element’s width to be relative to the width of its parent
element (which is its containing block), while making sure that the
float’s width never goes below 10em. The reverse approach is also
possible:

p.aside {float: left; width: 40em; max-width: 40%;}

This will set the float to be 40em wide, unless that would be more than
40% the width of the containing block, in which case the float will be
limited to that 40% width.

Content Overflow and Clipping

If the content of an element is too much for the element’s size, it will
be in danger of overflowing the element itself. There are a few
alternatives in such situations, and CSS lets you select among them.
It also allows you to define a clipping region to determine the area of
the element outside of which these sorts of things become an issue.

Overflow

So let’s say that we have, for whatever reason, an element that has been
pinned to a specific size, and the content doesn’t fit. You can take
control of the situation with the overflow property.

The default value of visible means that the element’s content may be
visible outside the element’s box. Typically, this leads to the content
running outside its own element box, but not altering the shape of
that box. The following markup would result in Figure 11-6:

div#sidebar {position: absolute; top: 0; left: 0; width: 25%; height: 7em;
    background: #BBB; overflow: visible;}

If overflow is set to scroll, the element’s content is clipped—that
is, hidden—at the edges of the element box, but there is some
way to make the extra content available to the user. In a web browser,
this could mean a scroll bar (or set of them), or another method of
accessing the content without altering the shape of the element itself.
One possibility is depicted in Figure 11-6.

If scroll is used, the panning mechanisms (e.g., scroll bars) should
always be rendered. To quote the specification, “this avoids any problem
with scrollbars appearing or disappearing in a dynamic environment.”
Thus, even if the element has sufficient space to display all its
content, the scroll bars may still appear and take up space (though they may not).
In addition, when printing
a page or otherwise displaying the document in a print medium, the
content may be displayed as though the value of overflow were declared
to be visible.

If overflow is set to hidden, the element’s content is clipped at
the edges of the element box, but no scrolling interface should be
provided to make the content outside the clipping region accessible to
the user. In such an instance, the clipped content would not be accessible to the user.

Figure 11-6 illustrates each of these three overflow values.

Figure 11-6. Three methods for handling overflowing content

Finally, there is overflow: auto. This allows user agents to determine
which behavior to use, although they are encouraged to provide a
scrolling mechanism when necessary. This is a potentially useful way to
use overflow, since user agents could interpret it to mean “provide
scroll bars only when needed.” (They may not, but they certainly could
and probably should.)

Element Visibility

In addition to all the clipping and overflowing, you can also control
the visibility of an entire element.

This one is pretty easy. If an element is set to have
visibility: visible, then it is, as you might expect, visible. If an element is set to visibility: hidden, it is made “invisible” (to
use the wording in the specification). In its invisible state, the
element still affects the document’s layout as though it were visible.
In other words, the element is still there—you just can’t see it, pretty
much as if you’d declared opacity: 0.

Note the difference between this and display: none. In the latter
case, the element is not displayed and is removed from the document
altogether so that it doesn’t have any effect on document layout.
Figure 11-7 shows a document in which a paragraph has been set to hidden,
based on the following styles and markup:

em.trans {visibility: hidden; border: 3px solid gray; background: silver;
    margin: 2em; padding: 1em;}
<p>
    This is a paragraph which should be visible. Nulla berea consuetudium ohio
    city, mutationem dolore. <em >Humanitatis molly shannon
    ut lorem.</em> Doug dieken dolor possim south euclid.
</p>

Figure 11-7. Making elements invisible without suppressing their element boxes

Everything visible about a hidden element—such as content, background,
and borders—is made invisible. The space is still there because the
element is still part of the document’s layout. We just can’t see it.

It’s possible to set the descendant element of a hidden element to be
visible. This causes the element to appear wherever it normally would,
despite the fact that the ancestor is invisible. In order to do so, we
explicitly declare the descendant element visible, since visibility
is inherited:

p.clear {visibility: hidden;}
p.clear em {visibility: visible;}

As for visbility: collapse, this value is used in CSS table rendering,
which we don’t really have room to cover here. According to the
specification, collapse has the same meaning as hidden if it is used
on nontable elements.

Absolute Positioning

Since most of the examples and figures in the previous sections are
examples of absolute positioning, you’re already halfway to
understanding how it works. Most of what remains are the details of what
happens when absolute positioning is invoked.

Containing Blocks and Absolutely Positioned Elements

When an element is positioned absolutely, it is completely removed from
the document flow. It is then positioned with respect to its containing
block, and its margin edges are placed using the offset properties
(top, left, etc.). The positioned element does not flow around the
content of other elements, nor does their content flow around the
positioned element. This implies that an absolutely positioned element
may overlap other elements or be overlapped by them. (We’ll see how to
affect the overlapping order later.)

The containing block for an absolutely positioned element is the nearest
ancestor element that has a position value other than static. It is
common for an author to pick an element that will serve as the
containing block for the absolutely positioned element and give it a
position of relative with no offsets, like so:

.contain {position: relative;}

Consider the example in Figure 11-8, which is an illustration of the
following:

p {margin: 2em;}
p.contain {position: relative;} /* establish a containing block*/
b {position: absolute; top: auto; right: 0; bottom: 0; left: auto;
    width: 8em; height: 5em; border: 1px solid gray;}
<body>
<p>
    This paragraph does <em>not</em> establish a containing block for any of
    its descendant elements that are absolutely positioned. Therefore, the
    absolutely positioned <b>boldface</b> element it contains will be
    positioned with respect to the initial containing block.
</p>
<p >
    Thanks to <code>position: relative</code>, this paragraph establishes a
    containing block for any of its descendant elements that are absolutely
    positioned. Since there is such an element-- <em>that is to say, <b>a
    boldfaced element that is absolutely positioned,</b> placed with respect
    to its containing block (the paragraph)</em>, it will appear within the
    element box generated by the paragraph.
</p>
</body>

The b elements in both paragraphs have been absolutely positioned. The
difference is in the containing block used for each one. The b element
in the first paragraph is positioned with respect to the initial
containing block, because all of its ancestor elements have a position
of static. The second paragraph has been set to
position: relative, so it establishes a containing block for its
descendants.

Figure 11-8. Using relative positioning to define containing blocks

You’ve probably noted that in that second paragraph, the positioned
element overlaps some of the text content of the paragraph. There is no
way to avoid this, short of positioning the b element outside of the
paragraph (by using a negative value for right or one of the other
offset properties) or by specifying a padding for the paragraph that is
wide enough to accommodate the positioned element. Also, since the b
element has a transparent background, the paragraph’s text shows through
the positioned element. The only way to avoid this is to set a
background for the positioned element, or else move it out of the
paragraph entirely.

You will sometimes want to ensure that the body element establishes a
containing block for all its descendants, rather than allowing the user
agent to pick an initial containing block. This is as simple as
declaring:

body {position: relative;}

In such a document, you could drop in an absolutely positioned
paragraph, as follows, and get a result like that shown in Figure 11-9:

<p style="position: absolute; top: 0; right: 25%; left: 25%; bottom:
    auto; width: 50%; height: auto; background: silver;">
    ...
</p>

The paragraph is now positioned at the very beginning of the document,
half as wide as the document’s width and overwriting other content.

Figure 11-9. Positioning an element whose containing block is the root element

An important point to highlight is that when an element is absolutely
positioned, it establishes a containing block for its descendant
elements. For example, we can absolutely position an element and then
absolutely position one of its children, as shown in Figure 11-10, which was
generated using the following styles and basic markup:

div {position: relative; width: 100%; height: 10em;
    border: 1px solid; background: #EEE;}
div.a {position: absolute; top: 0; right: 0; width: 15em; height: 100%;
    margin-left: auto; background: #CCC;}
div.b {position: absolute; bottom: 0; left: 0; width: 10em; height: 50%;
    margin-top: auto; background: #AAA;}

<div>
    <div class="a">
        absolutely positioned element A
        <div >
            absolutely positioned element B
        </div>
    </div>
    containing block
</div>

Remember that if the document is scrolled, the absolutely positioned
elements will scroll right along with it. This is true of all absolutely
positioned elements that are not descendants of fixed-position or
sticky-position elements.

This happens because, eventually, the elements are positioned in
relation to something that’s part of the normal flow. For example, if
you absolutely position a table, and its containing block is the initial
containing block, then it will scroll because the initial containing
block is part of the normal flow, and thus it scrolls.

If you want to position elements so that they’re placed relative to the
viewport and don’t scroll along with the rest of the document, keep
reading. The upcoming section on fixed positioning has the answers you
seek.

Figure 11-10. Absolutely positioned elements establish containing blocks

Placement and Sizing of Absolutely Positioned Elements

It may seem odd to combine the concepts of placement and sizing, but
it’s a necessity with absolutely positioned elements because the
specification binds them very closely together. This is not such a
strange pairing upon reflection. Consider what happens if an element is
positioned using all four offset properties, like so:

#masthead h1 {position: absolute; top: 1em; left: 1em; right: 25%; bottom: 10px;
    margin: 0; padding: 0; background: silver;}

Here, the height and width of the h1’s element box is determined by
the placement of its outer margin edges, as shown in Figure 11-11.

Figure 11-11. Determining the height of an element based on the offset properties

If the containing block were made taller, then the h1 would also
become taller; if the containing block is narrowed, then the h1 will
become narrower. If we were to add margins or padding to the h1, then
that would have further effects on the calculated height and width of
the h1.

But what if we do all that and then also try to set an explicit height and width?

#masthead h1 {position: absolute; top: 0; left: 1em; right: 10%; bottom: 0;
    margin: 0; padding: 0; height: 1em; width: 50%; background: silver;}

Something has to give, because it’s incredibly unlikely that all those
values will be accurate. In fact, the containing block would have to be
exactly two and a half times as wide as the h1’s computed value for
font-size for all of the shown values to be accurate. Any other
width would mean at least one value is wrong and has to be ignored.
Figuring out which one depends on a number of factors, and the factors
change depending on whether an element is replaced or nonreplaced.

For that matter, consider the following:

#masthead h1 {position: absolute; top: auto; left: auto;}

What should the result be? As it happens, the answer is not “reset the
values to zero.” We’ll see the actual answer, starting in the next
section.

Auto-edges

When absolutely positioning an element, there is a special behavior that
applies when any of the offset properties other than bottom is set to
auto. Let’s take top as an example. Consider the following:

<p>
    When we consider the effect of positioning, it quickly becomes clear that
    authors can do a great deal of damage to layout, just as they can do very
    interesting things.<span style="position: absolute; top: auto;
    left: 0;">[4]</span> This is usually the case with useful technologies:
    the sword always has at least two edges, both of them sharp.
</p>

What should happen? For left, it’s easy: the left edge of the element
should be placed against the left edge of its containing block (which
we’ll assume here to be the initial containing block). For top,
however, something much more interesting happens. The top of the
positioned element should line up with the place where its top would
have been if it were not positioned at all. In other words, imagine
where the span would have been placed if its position value were
static; this is its static position—the place where its top edge
should be calculated to sit. CSS 2.1 had this to say about static
positions:

the term “static position” (of an element) refers, roughly, to the position an element would have had in the normal flow. More precisely: the static position for top is the distance from the top edge of the containing block to the top margin edge of a hypothetical box that would have been the first box of the element if its specified position value had been static and its specified float had been none and its specified clear had been none… The value is negative if the hypothetical box is above the containing block.

Therefore, we should get the result shown in Figure 11-12.

Figure 11-12. Absolutely positioning an element consistently with its “static” top edge

The “[4]” sits just outside the paragraph’s content because the initial
containing block’s left edge is to the left of the paragraph’s left
edge.

The same basic rules hold true for left and right being set to
auto. In those cases, the left (or right) edge of a positioned element
lines up with the spot where the edge would have been placed if the
element weren’t positioned. So let’s modify our previous example so that both top and left are set to auto:

<p>
    When we consider the effect of positioning, it quickly becomes clear that
    authors can do a great deal of damage to layout, just as they can do very
    interesting things.<span style="position: absolute; top: auto; left:
    auto;">[4]</span> This is usually the case with useful technologies:
    the sword always has at least two edges, both of them sharp.
</p>

This would have the result shown in Figure 11-13.

Figure 11-13. Absolutely positioning an element consistently with its “static” position

The “[4]” now sits right where it would have were it not positioned.
Note that, since it is positioned, its normal-flow space is closed up.
This causes the positioned element to overlap the normal-flow content.

This auto-placement works only in certain situations, generally wherever
there are few constraints on the other dimensions of a positioned
element. Our previous example could be auto-placed because it had no
constraints on its height or width, nor on the
placement of the bottom and right edges. But suppose, for some reason,
there had been such constraints. Consider:

<p>
    When we consider the effect of positioning, it quickly becomes clear that
    authors can do a great deal of damage to layout, just as they can do very
    interesting things.<span style="position: absolute; top: auto; left: auto;
    right: 0; bottom: 0; height: 2em; width: 5em;">[4]</span> This is usually
    the case with useful technologies: the sword always has at least two edges,
    both of them sharp.
</p>

It is not possible to satisfy all of those values. Determining what
happens is the subject of the next section.

Placing and Sizing Nonreplaced Elements

In general, the size and placement of an element depends on its
containing block. The values of its various properties (width,
right, padding-left, and so on) affect its layout, but
the foundation is the containing block.

Consider the width and horizontal placement of a positioned element. It
can be represented as an equation which states:

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

This calculation is fairly reasonable. It’s basically the equation that
determines how block-level elements in the normal flow are sized, except
it adds left and right to the mix. So how do all these interact?
There is a series of rules to work through.

First, if left, width, and right are all set to auto, then you
get the result seen in the previous section: the left edge is placed at
its static position, assuming a left-to-right language. In right-to-left
languages, the right edge is placed at its static position. The width of
the element is set to be “shrink to fit,” which means the element’s
content area is made only as wide as necessary to contain its content.
The nonstatic position property (right in left-to-right languages,
left in right-to-left) is set to take up the remaining distance. For
example:

<div style="position: relative; width: 25em; border: 1px dotted;">
    An absolutely positioned element can have its content <span style="position:
    absolute; top: 0; left: 0; right: auto; width: auto; background:
    silver;">shrink-wrapped</span> thanks to the way positioning rules work.
</div>

This has the result shown in Figure 11-14.

Figure 11-14. The “shrink-to-fit” behavior of absolutely positioned elements

The top of the element is placed against the top of its containing block
(the div, in this case), and the width of the element is just as much
as is needed to contain the content. The remaining distance from the
right edge of the element to the right edge of the containing block
becomes the computed value of right.

Now suppose that only the left and right margins are set to auto, not
left, width, and right, as in this example:

<div style="position: relative; width: 25em; border: 1px dotted;">
    An absolutely positioned element can have its content <span style="position:
    absolute; top: 0; left: 1em; right: 1em; width: 10em; margin: 0 auto;
    background: silver;">shrink-wrapped</span> thanks to the way positioning
    rules work.
</div>

What happens here is that the left and right margins, which are both
auto, are set to be equal. This will effectively center the element,
as shown in Figure 11-15.

Figure 11-15. Horizontally centering an absolutely positioned element with auto margins

This is basically the same as auto-margin centering in the normal flow.
So let’s make the margins something other than auto:

<div style="position: relative; width: 25em; border: 1px dotted;">
    An absolutely positioned element can have its content <span style="position:
    absolute; top: 0; left: 1em; right: 1em; width: 10em; margin-left: 1em;
    margin-right: 1em; background: silver;">shrink-wrapped</span> thanks to the
    way positioning rules work.
</div>

Now we have a problem. The positioned span’s properties add up to only
14em, whereas the containing block is 25em wide. That’s an 11-em deficit
we have to make up somewhere.

The rules state that, in this case, the user agent ignores the value for
right (in left-to-right languages; otherwise, it ignores left) and
solves for it. In other words, the result will be the same as if we’d
declared:

<span style="position: absolute; top: 0; left: 1em;
right: 12em; width: 10em; margin-left: 1em; margin-right: 1em;
right: auto; background: silver;">shrink-wrapped</span>

This has the result shown in Figure 11-16.

Figure 11-16. Ignoring the value for right in an overconstrained situation

If one of the margins had been left as auto, then that would have been
changed instead. Suppose we change the styles to state:

<span style="position: absolute; top: 0; left: 1em;
right: 1em; width: 10em; margin-left: 1em; margin-right: auto;
background: silver;">shrink-wrapped</span>

The visual result would be the same as that in Figure 11-16, only it would
be attained by computing the right margin to 12em instead of overriding
the value assigned to the property right.

If, on the other hand, we
made the left margin auto, then it would be reset, as illustrated in
Figure 11-17:

<span style="position: absolute; top: 0; left: 1em;
right: 1em; width: 10em; margin-left: auto; margin-right: 1em;
background: silver;">shrink-wrapped</span>

Figure 11-17. Ignoring the value for margin-right in an overconstrained situation

In general, if only one of the properties is set to auto, then it will
be used to satisfy the equation given earlier in the section. Thus,
given the following styles, the element’s width would expand to whatever
size is needed, instead of “shrink-wrapping” the content:

<span style="position: absolute; top: 0; left: 1em;
right: 1em; width: auto; margin-left: 1em; margin-right: 1em;
background: silver;">not shrink-wrapped</span>

So far we’ve really only examined behavior along the horizontal axis,
but very similar rules hold true along the vertical axis. If we take the
previous discussion and rotate it 90 degrees, as it were, we get almost
the same behavior. For example, the following markup results in Figure 11-18:

<div style="position: relative; width: 30em; height: 10em; border: 1px solid;">
    <div style="position: absolute; left: 0; width: 30%;
        background: #CCC; top: 0;">
            element A
    </div>
    <div style="position: absolute; left: 35%; width: 30%;
        background: #AAA; top: 0; height: 50%;">
            element B
    </div>
    <div style="position: absolute; left: 70%; width: 30%;
        background: #CCC; height: 50%; bottom: 0;">
            element C
    </div>
</div>

In the first case, the height of the element is shrink-wrapped to the
content. In the second, the unspecified property (bottom) is set to
make up the distance between the bottom of the positioned element and
the bottom of its containing block. In the third case, top is unspecified, and therefore used to make up the difference.

Figure 11-18. Vertical layout behavior for absolutely positioned elements

For that matter, auto-margins can lead to vertical centering. Given the
following styles, the absolutely positioned div will be vertically
centered within its containing block, as shown in Figure 11-19:

<div style="position: relative; width: 10em; height: 10em; border: 1px solid;">
    <div style="position: absolute; left: 0; width: 100%; background: #CCC;
        top: 0; height: 5em; bottom: 0; margin: auto 0;">
            element D
    </div>
</div>

There are two small variations to point out. In horizontal layout,
either right or left can be placed according to the static position
if their values are auto. In vertical layout, only top can take on
the static position; bottom, for whatever reason, cannot.

Also, if an absolutely positioned element’s size is overconstrained in
the vertical direction, bottom is ignored. Thus, in the following
situation, the declared value of bottom would be overridden by the
calculated value of 5em:

<div style="position: relative; width: 10em; height: 10em; border: 1px solid;">
    <div style="position: absolute; left: 0; width: 100%; background: #CCC;
        top: 0; height: 5em; bottom: 0; margin: 0;">
            element D
    </div>
</div>

There is no provision for top to be ignored if the properties are
overconstrained.

Figure 11-19. Vertically centering an absolutely positioned element with auto-margins

Placing and Sizing Replaced Elements

Positioning rules are different for replaced elements (e.g., images)
than they are for nonreplaced elements. This is because replaced
elements have an intrinsic height and width, and therefore are not
altered unless explicitly changed by the author. Thus, there is no
concept of “shrink to fit” in the positioning of replaced elements.

The behaviors that go into placing and sizing replaced elements are most
easily expressed by a series of rules to be taken one after the other.
These state:

  1. If width is set to auto, the used value of width is determined
    by the intrinsic width of the element’s content. Thus, if an image is
    intriniscally 50 pixels wide, then the used value is calculated to be
    50px. If width is explicitly declared (that is, something like
    100px or 50%), then the width is set to that value.

  2. If left has the value auto in a left-to-right language, replace
    auto with the static position. In right-to-left languages, replace an
    auto value for right with the static position.

  3. If either left or right is still auto (in other words, it
    hasn’t been replaced in a previous step), replace any auto on
    margin-left or margin-right with 0.

  4. If, at this point, both margin-left and margin-right are still
    defined to be auto, set them to be equal, thus centering the element
    in its containing block.

  5. After all that, if there is only one auto value left, change it to
    equal the remainder of the equation.

This leads to the same basic behaviors we saw with absolutely positioned
nonreplaced elements, as long as you assume that there is an explicit
width for the nonreplaced element. Therefore, the following two
elements will have the same width and placement, assuming the image’s
intrinsic width is 100 pixels (see Figure 11-20):

<div>
    <img src="frown.gif" alt="a frowny face"
        style="position: absolute; top: 0; left: 50px; margin: 0;">
</div>
<div style="position: absolute; top: 0; left: 50px;
        width: 100px; height: 100px; margin: 0;">
    it's a div!
</div>

Figure 11-20. Absolutely positioning a replaced element

As with nonreplaced elements, if the values are overconstrained, the
user agent is supposed to ignore the value for right in left-to-right
languages and left in right-to-left languages. Thus, in the following
example, the declared value for right is overridden with a computed
value of 50px:

<div style="position: relative; width: 300px;">
    <img src="frown.gif" alt="a frowny face" style="position: absolute; top: 0;
        left: 50px; right: 125px; width: 200px; margin: 0;">
</div>

Similarly, layout along the vertical axis is governed by a series of
rules that state:

  1. If height is set to auto, the computed value of height is
    determined by the intrinsic height of the element’s content. Thus, the
    height of an image 50 pixels tall is computed to be 50px. If height
    is explicitly declared (that is, something like 100px or 50%), then
    the height is set to that value.

  2. If top has the value auto, replace it with the replaced
    element’s static position.

  3. If bottom has a value of auto, replace any auto value on
    margin-top or margin-bottom with 0.

  4. If, at this point, both margin-top and margin-bottom are still
    defined to be auto, set them to be equal, thus centering the element
    in its containing block.

  5. After all that, if there is only one auto value left, change it to
    equal the remainder of the equation.

As with nonreplaced elements, if the values are overconstrained, then
the user agent is supposed to ignore the value for bottom.

Thus, the following markup would have the results shown in Figure 11-21:

<div style="position: relative; height: 200px; width: 200px; border: 1px solid;">
    <img src="one.gif" alt="one" width="25" height="25"
        style="position: absolute; top: 0; left: 0; margin: 0;">
    <img src="two.gif" alt="two" width="25" height="25"
        style="position: absolute; top: 0; left: 60px; margin: 10px 0;
            bottom: 4377px;">
    <img src="three.gif" alt=" three" width="25" height="25"
        style="position: absolute; left: 0; width: 100px; margin: 10px;
            bottom: 0;">
    <img src="four.gif" alt=" four" width="25" height="25"
        style="position: absolute; top: 0; height: 100px; right: 0;
            width: 50px;">
    <img src="five.gif" alt="five" width="25" height="25"
        style="position: absolute; top: 0; left: 0; bottom: 0; right: 0;
            margin: auto;">
</div>

Figure 11-21. Stretching replaced elements through positioning

Placement on the Z-Axis

With all of the positioning going on, there will inevitably be a
situation where two elements will try to exist in the same place,
visually speaking. One of them will have to overlap the
other—but how does one control which element comes out “on top”? This is
where the property z-index comes in.

z-index lets you alter the way in which elements overlap each other.
It takes its name from the coordinate system in which side-to-side is
the x-axis and top-to-bottom is the y-axis. In such a case, the
third axis—that which runs from back to front, as you look at the
display surface—is termed the z-axis. Thus, elements are given values
along this axis using z-index. Figure 11-22 illustrates this system.

Figure 11-22. A conceptual view of z-index stacking

In this coordinate system, an element with a higher z-index value is
closer to the reader than those with lower z-index values. This will
cause the high-value element to overlap the others, as illustrated in
Figure 11-23, which is a “head-on” view of Figure 11-22. This precedence of
overlapping is referred to as stacking.

Figure 11-23. How the elements are stacked

Any integer can be used as a value for z-index, including negative
numbers. Assigning an element a negative z-index will move it further
away from the reader; that is, it will be moved lower in the stack.
Consider the following styles, illustrated in Figure 11-24:

p {background: rgba(255,255,255,0.9); border: 1px solid;}
p#first {position: absolute; top: 0; left: 0;
    width: 40%; height: 10em; z-index: 8;}
p#second {position: absolute; top: -0.75em; left: 15%;
    width: 60%; height: 5.5em; z-index: 4;}
p#third {position: absolute; top: 23%; left: 25%;
    width: 30%; height: 10em; z-index: 1;}
p#fourth {position: absolute; top: 10%; left: 10%;
    width: 80%; height: 10em; z-index: 0;}

Each of the elements is positioned according to its styles, but the
usual order of stacking is altered by the z-index values. Assuming the
paragraphs were in numeric order, then a reasonable stacking order would
have been, from lowest to highest, p#first, p#second, p#third,
p#fourth. This would have put p#first behind the other three
elements, and p#fourth in front of the others. Thanks to z-index,
the stacking order is under your control.

Figure 11-24. Stacked elements can overlap

As the previous example demonstrates, there is no particular need to
have the z-index values be contiguous. You can assign any integer of
any size. If you want to be fairly certain that an element stayed in
front of everything else, you might use a rule along the lines of
z-index: 100000. This would work as expected in most cases—although if
you ever declared another element’s z-index to be 100001 (or
higher), it would appear in front.

Once you assign an element a value for z-index (other than auto),
that element establishes its own local stacking context. This means
that all of the element’s descendants have their own stacking order,
relative to the ancestor element. This is very similar to the way that
elements establish new containing blocks. Given the following styles,
you would see something like Figure 11-25:

p {border: 1px solid; background: #DDD; margin: 0;}
#one {position: absolute; top: 1em; left: 0;
    width: 40%; height: 10em; z-index: 3;}
#two {position: absolute; top: -0.75em; left: 15%;
    width: 60%; height: 5.5em; z-index: 10;}
#three {position: absolute; top: 10%; left: 30%;
    width: 30%; height: 10em; z-index: 8;}
p[id] em {position: absolute; top: -1em; left: -1em;
    width: 10em; height: 5em;}
#one em {z-index: 100; background: hsla(0,50%,70%,0.9);}
#two em {z-index: 10; background: hsla(120,50%,70%,0.9);}
#three em {z-index: -343; background: hsla(240,50%,70%,0.9);}

Figure 11-25. Positioned elements establish local stacking contexts

Note where the em elements fall in the stacking order. Each of them is
correctly layered with respect to its parent element. Each
em is drawn in front of its parent element, whether or not its
z-index is negative, and parents and children are grouped together like layers in an editing program. (The specification keeps children from
being drawn behind their parents when using z-index stacking, so the
em in p#three is drawn on top of p#one, even though its z-index
value is -343.) Its z-index value is taken with respect to its local
stacking context: its containing block. That containing block, in turn,
has a z-index, which operates within its local stacking context.

There remains one more value to examine. The CSS specification has this
to say about the default value, auto:

The stack level of the generated box in the current stacking context is
0. The box does not establish a new stacking context unless it is the
root element.

So, any element with z-index: auto can be treated as though it is set
to z-index: 0.

Tip

z-index is also honored by flex and grid items, even though they are not positioned using the position property. The rules are essentially the same.

Fixed Positioning

As implied in a previous section, fixed positioning is just like
absolute positioning, except the containing block of a fixed element is
the viewport. A fixed-position element is totally removed from the
document’s flow and does not have a position relative to any part of
the document.

Fixed positioning can be exploited in a number of interesting ways.
First off, it’s possible to create frame-style interfaces using fixed
positioning. Consider Figure 11-26, which shows a very common layout scheme.

Figure 11-26. Emulating frames with fixed positioning

This could be done using the following styles:

div#header {position: fixed; top: 0; bottom: 80%; left: 20%; right: 0;
    background: gray;}
div#sidebar {position: fixed; top: 0; bottom: 0; left: 0; right: 80%;
    background: silver;}

This will fix the header and sidebar to the top and side of the
viewport, where they will remain regardless of how the document is
scrolled. The drawback here, though, is that the rest of the document
will be overlapped by the fixed elements. Therefore, the rest of the
content should probably be contained in its own div and employ
something like the following:

div#main {position: absolute; top: 20%; bottom: 0; left: 20%; right: 0;
    overflow: scroll; background: white;}

It would even be possible to create small gaps between the three
positioned divs by adding some appropriate margins, as follows:

body {background: black; color: silver;} /* colors for safety's sake */
div#header {position: fixed; top: 0; bottom: 80%; left: 20%; right: 0;
    background: gray; margin-bottom: 2px; color: yellow;}
div#sidebar {position: fixed; top: 0; bottom: 0; left: 0; right: 80%;
    background: silver; margin-right: 2px; color: maroon;}
div#main {position: absolute; top: 20%; bottom: 0; left: 20%; right: 0;
    overflow: auto; background: white; color: black;}

Given such a case, a tiled image could be applied to the body
background. This image would show through the gaps created by the
margins, which could certainly be widened if the author saw fit.

Another use for fixed positioning is to place a “persistent” element on
the screen, like a short list of links. We could create a persistent
footer with copyright and other information as follows:

footer {position: fixed; bottom: 0; width: 100%; height: auto;}

This would place the footer element at the bottom of the viewport and
leave it there no matter how much the document is scrolled.

Note

Many of the layout cases for fixed positioning, besides
“persistent elements,” are handled as well, if not better, by Grid layout (see Chapter 13 for more).

Relative Positioning

The simplest of the positioning schemes to understand is relative
positioning
. In this scheme, a positioned element is shifted by use of
the offset properties. However, this can have some interesting
consequences.

On the surface, it seems simple enough. Suppose we want to shift an
image up and to the left. Figure 11-27 shows the result of these styles:

img {position: relative; top: -20px; left: -20px;}

Figure 11-27. A relatively positioned element

All we’ve done here is offset the image’s top edge 20 pixels upward and
offset the left edge 20 pixels to the left. However, notice the blank
space where the image would have been had it not been positioned. This
happened because when an element is relatively positioned, it’s shifted
from its normal place, but the space it would have occupied doesn’t
disappear. Consider the results of the following styles, which are
depicted in Figure 11-28:

em {position: relative; top: 10em; color: red;}

Figure 11-28. Another relatively positioned element

As you can see, the paragraph has some blank space in it. This is where
the em element would have been, and the layout of the em element in
its new position exactly mirrors the space it left behind.

It’s also possible to shift a relatively positioned element
to overlap other content. For example, the following styles and markup
are illustrated in Figure 11-29:

img.slide {position: relative; left: 30px;}
<p>
    In this paragraph, we will find that there is an image that has been
    pushed to the right. It will therefore <img src="star.gif" alt="A star!"
    > overlap content nearby, assuming that it is not the
    last element in its line box.
</p>

Figure 11-29. Relatively positioned elements can overlap other content

There is one interesting wrinkle to relative positioning. What happens
when a relatively positioned element is overconstrained? For example:

strong {position: relative; top: 10px; bottom: 20px;}

Here we have values that call for two very different behaviors. If we
consider only top: 10px, then the element should be shifted downward
10 pixels, but bottom: 20px clearly calls for the element to be
shifted upward 20 pixels.

The original CSS2 specification does not say what should happen in this
case. CSS2.1 stated that when it comes to overconstrained relative
positioning, one value is reset to be the negative of the other. Thus,
bottom would always equal -top. This means the previous example
would be treated as though it had been:

strong {position: relative; top: 10px; bottom: -10px;}

Thus, the strong element will be shifted downward 10 pixels. The
specification also makes allowances for writing directions. In relative
positioning, right always equals -left in left-to-right languages;
but in right-to-left languages, this is reversed: left always equals
-right.

Note

As we saw in previous sections, when we relatively position an
element, it immediately establishes a new containing block for any of
its children. This containing block corresponds to the place where the
element has been newly positioned.

Sticky Positioning

A new addition to CSS is the concept of sticky positioning. If you’ve
ever used a decent music app on a mobile device, you’ve probably noticed
this in action: as you scroll through an alphabetized list of artists,
the current letter stays stuck at the top of the window until a new
letter section is entered, at which point the new letter replaces the
old. It’s a little hard to show in print, but Figure 11-30 takes a stab at
it by showing three points in a scroll.

Figure 11-30. Sticky positioning

CSS makes this sort of thing possible by declaring an element to be
position: sticky, but (as usual) there’s more to it than that.

First off, the offsets (top, left, etc.) are used to define a
sticky-positioning rectangle with relation to the containing block.
Take the following as an example. It will have the effect shown in
Figure 11-31, where the dashed line shows where the sticky-positioning
rectangle is created:

#scrollbox {overflow: scroll; width: 15em; height: 18em;}
#scrollbox h2 {position: sticky; top: 2em; bottom: auto;
    left: auto; right: auto;}

Figure 11-31. The sticky-positioning rectangle

Notice that the h2 is actually in the middle of the rectangle in Figure 11-31. That’s its place in the normal flow of the content inside the
#scrollbox element that contains the content. The only way to make it
sticky is to scroll that content until the top of the h2 touches the
top of the sticky-positioning rectangle—at which point, it will stick
there. This is illustrated in Figure 11-32.

Figure 11-32. Sticking to the top of the sticky-positioning rectangle

In other words, the h2 sits in the normal flow until its sticky edge
touches the sticky edge of the rectangle. At that point, it sticks there
as if absolutely positioned, except that it leaves behind the space it
otherwise would have occupied in the normal flow.

You may have noticed that the scrollbox element doesn’t have a
position declaration. There isn’t one hiding offstage, either: it’s
overflow: scroll that created a containing block for the
sticky-positioned h2 elements. This is the one case where a containing
block isn’t determined by position.

If the scrolling is reversed so that the h2’s normal-flow position
moves lower than the top of the rectangle, the h2 is detached from the
rectangle and resumes its place in the normal flow. This is shown in
Figure 11-33.

Figure 11-33. Detaching from the top of the sticky-positioning rectangle

Note that the reason the h2 stuck to the top of the rectangle in
these examples is that the value of top was set to something other
than auto for the h2; that is, for the sticky-positioned element.
You can use whatever offset side you want. For example, you
could have elements stick to the bottom of the rectangle as you scroll
downwards through the content. This is illustrated in Figure 11-34:

#scrollbox {overflow: scroll; position: relative; width: 15em; height: 10em;}
#scrollbox h2 {position: sticky; top: auto; bottom: 0; left: auto; right: auto;}

Figure 11-34. Sticking to the bottom of the sticky-positioning rectangle

This could be a way to show footnotes or comments for a given paragraph,
for example, while allowing them to scroll away as the paragraph moves
upward. The same rules apply for the left and right sides, which is
useful for side-scrolling content.

If you define more than one offset property to have a value other than
auto, then all of them will become sticky edges. For example, this
set of styles will force the h2 to always appear inside the scrollbox,
regardless of which way its content is scrolled (Figure 11-35):

#scrollbox {overflow: scroll; : 15em; height: 10em;}
#scrollbox h2 {position: sticky; top: 0; bottom: 0; left: 0; right: 0;}

Figure 11-35. Making every side a sticky side

You might wonder: what happens if I have multiple sticky-positioned
elements in a situation like this, and I scroll past two or more? In
effect, they pile up on top of one another:

#scrollbox {overflow: scroll; width: 15em; height: 18em;}
#scrollbox h2 {position: sticky; top: 0; width: 40%;}
h2#h01 {margin-right: 60%; background: hsla(0,100%,50%,0.75);}
h2#h02 {margin-left: 60%; background: hsla(120,100%,50%,0.75);}
h2#h03 {margin-left: auto; margin-right: auto;
    background: hsla(240,100%,50%,0.75);}

It’s not easy to see in static images like Figure 11-36, but the way the headers are
piling up is that the later they are in the source, the closer they are
to the viewer. This is the usual z-index behavior—which means that you
can decide which sticky elements sit on top of others by assigning
explicit z-index values. For example, suppose we wanted the first
sticky element in our content to sit atop all the others. By giving it
z-index: 1000, or any other sufficiently high number, it would sit on
top of all the other sticky elements that stuck in the same place. The
visual effect would be of the other elements “sliding under” the topmost
element.

Figure 11-36. A sticky-header pileup
Warning

As of late 2017, the only browsers that didn’t support
position: sticky were Microsoft IE and Edge, and Opera Mini. Safari required a -webkit- prefix on the value, so: position: -webkit-sticky.

Summary

Thanks to positioning, it’s possible to move elements around in ways that
the normal flow could never accommodate. Although many positioning
tricks are soon to give way to grid layout, there are still a lot of
uses for positioning—from sidebars that always stay in the viewport to
sticky section headings in lists or long articles. Combined with the
stacking possibilities of the z-axis and the various overflow
patterns, there’s still a lot to like in positioning.

Comments are closed.

loading...