Web Design 101: Positioning
Share your comments on this topic with other web professionals
In: Columns > Web Design 101
Published on April 16, 2007
Any cascading style sheets (CSS) newbie will have heard about it, right? CSS positioning—absolute this and relative that. Perhaps you have some vague idea about what it is, but are afraid to try it yourself.
An element with
position:absolute
is removed from the document flow, which means the rest of the document acts as if the element weren’t there. It won’t affect subsequent elements. Instead, it could end up layered over other elements, unless we make sure it doesn’t.
Let’s shed some light on the shadowy mysteries of CSS positioning. If your CSS skills are limited or even moderate, you will learn what you need to master positioning—it’s not difficult, once you understand the fundamental ideas behind the concept.
And a one, two, three, four…
There is essentially one markup language usable for web documents: HTML. (Some people will use an XHTML document type declaration, but they will still have to serve it as HTML or it won’t work for the majority that use Microsoft Internet Explorer.)
There are two ways to specify the visual layout of an HTML document: tables or CSS. Layout tables are old-school, deprecated and bad for accessibility, so they’re out.
There are three ways to specify the visual layout of an HTML document using CSS: fake tables, floats, and positioning. Fake tables (using display:table-cell
, etc.) only work in the most modern browsers. Floats will be covered in a future article, so that leaves us with positioning.
There are four types of positioning in CSS 2.1: static, relative, absolute, and fixed. How are they different? You will find the answers below. Which one is best? That one depends entirely on you.
What’s in a name?
The names used by the World Wide Web Consortium (W3C) for the positioning schemes can be very confusing, unless you understand their point of view.
Most of us probably regard this matter from a layout point of view, from which the names seem quite odd. A static element isn’t static at all, it moves around as we scroll the page. A relatively positioned element is only relative to itself. An absolutely positioned element is not absolute, but relative to some other element. And a fixed element is both absolute and static.
So the W3C only scores a batting average of .250? No, they just look at the whole thing from a different perspective: the document. If we do that, too, it all makes sense. (Well, at least it is somewhat less confusing.) A static element is static within the document flow. A relatively positioned element is indeed relative to itself. An absolutely positioned element is absolute—with respect to another box—and removed from the flow. And a fixed element is indeed fixed with respect to the flow.
Setting the position property
So how do we affect this mysterious positioning then? The answer is the position
property in CSS, and the valid values are the keywords we have already seen (static
, relative
, absolute
, and fixed
), plus inherit
. The position
property is not inherited by descendant elements, but we can force inheritance by using position:inherit
.
The position
property applies to all elements, so we can use it for block-level element types such as div
or p
, or for inline element types such as span
or em
.
The default value, if we say nothing else, is static
.
For relative, absolute, and fixed positioning, five other CSS properties are of interest: top
, right
, bottom
, left
, and z-index
. The uses for the first four and how they apply vary between the three positioning schemes, and we will look at them more closely when the time comes. We will get to z-index
at the end of this article.
Static Positioning
An element with position:static
—which is the default—isn’t really positioned at all. You will rarely see this explicit declaration. The only time you need to use it is to undo a rule that affects the same element. A silly example:
div {position:absolute}
#foo {position:static}
This would make all div
elements absolutely positioned (which is rarely a good idea, but this is just for the sake of example). A div
element with id="foo"
, however, will be static within the document flow. The second rule is used as an undo of the first.
The phrase “statically positioned” is, therefore, something of a misnomer. I’m going to use it anyway. You’ll know what I mean.
A statically positioned element occurs in a place which is determined by whatever static content precedes it in the markup. We can push it around a bit using margin
or padding
, but it mainly stays in place with respect to the rest of the content.
Explaining the details of how static elements are laid out would require three whole articles the size of this one, and it would probably make most of you swear off web design forever. We don’t want to do that, so I’ll skim right over this issue and say that it’s fairly straightforward and intuitive. Ahem.
Relative Positioning
The position of an element with position:relative
is, as has been mentioned, relative to itself. This may sound odd, but it can be rather useful once you grasp the concept.
This is how it works, in layman’s terms: The box generated by the element is laid out just as it is for static elements. Then the rendered box is shifted horizontally and/or vertically, according to the values of the top
, right
, bottom
, and left
properties. But here’s the tricky part—as far as the rest of the document is concerned, the box is still in its original place! It is laid out where it should be, and it will occupy that position in the document flow. It’s only the rendered box that is shifted, relative to that original position. If we move it a large distance, we will leave a hole in the document.
This means that position:relative
is utterly useless for things like column layout. It is normally only useful for nudging an element a few pixels or so. Does this mean we can forget about position:relative
? Not at all. In fact, it has a side effect that makes it very valuable, indeed, which we will get to in a moment.
The top
, right
, bottom
, and left
properties can be used for specifying how far to push the rendered box from its original position. Again, the way this works can seem counter-intuitive at first, but there’s some logic in it.
If we set top:20px
for a relatively positioned element, we move it twenty pixels down. Say what? Actually it’s not as crazy as it may seem: top:20px
means that we push the top edge of the box twenty pixels from where it would have been without positioning. These values can even be negative, so we could use bottom:-20px
to achieve the same thing.
The alert reader will have realized by now that this means there’s no point in setting top
and bottom
at the same time, nor right
and left
. They will counter each other, or one of them will be ignored. However, specifying top
and left
together is perfectly all right, if we want to, for example, push the box down and to the right.
The results of setting position:relative
for table elements (rows, columns, row/column groups, cells, and captions) is undefined in the CSS 2.1 specification, which means you shouldn’t muck around with it.
Absolute Positioning
Absolute positioning can be quite useful, even for laying out columns. It has its drawbacks, though. An element with position:absolute
is removed from the document flow, which means the rest of the document acts as if the element weren’t there. It won’t affect subsequent elements. Instead, it could end up layered over other elements, unless we make sure it doesn’t. (Sometimes we want that to happen, of course, for example for pop-ups without JavaScript.)
With absolute positioning, the top
, right
, bottom
, and left
properties apply in a very different way than for relatively positioned elements. They now specify the positions of the four edges of the generated box. The values can be specified with length units, such as top:50px
or left:-8.5em
. They can also be specified in percents, in which case it gets slightly more complicated. A percent value for top
and bottom
refers to a percentage of the height of the containing block, while for right
and left
it refers to the width of the containing block.
What is a containing block? I’m glad you asked…
It sounds like one of those boring technical details that only real nerds pay any attention to, doesn’t it? To be honest, it is a boring technical detail, but unfortunately we have to understand it. The containing block is extremely important when we use absolute positioning.
Take a look at what the CSS 2.1 specification has to say about containing blocks:
If the element has ‘position: absolute’, the containing block is established by the nearest ancestor with a ‘position’ of ‘absolute’, ‘relative’ or ‘fixed’ … If there is no such ancestor, the containing block is the initial containing block.
OK, the containing block for an absolutely positioned element is the nearest positioned ancestor. An ancestor is a parent element or a grandparent, and so on. If none of the ancestors is positioned, the containing block is some mythical entity called “the initial containing block”, about which the specification has the following to say:
The containing block in which the root element lives is chosen by the user agent. (It could be related to the viewport.) This containing block is called the initial containing block.
Very helpful, isn’t it? The root element in any HTML or XHTML document is the html
element. So the initial containing block is like the parent of the html
element, whatever that is. The specification starts flapping at this point and mumbles about “chosen by the user agent” and “could be related to the viewport.” In W3C lingo, user agent is (in most cases) what we mere mortals call a browser, and viewport means browser window.
In other words, we can assume that if there is no positioned ancestor for an absolutely positioned element, the containing block is the document itself.
If we look closer at the first quotation, we will discover that interesting side effect of relative positioning I mentioned earlier: apparently, a relatively positioned element becomes the containing block for any absolutely positioned children. That is extremely useful to us, because that allows us full control over our containing blocks.
If we want to position an element absolutely, but with respect to some other element, all we have to do is make that other element (or a common ancestor) be our containing block. We can now achieve that easily by setting position:relative
on it. We don’t have to specify any movement at all. The containing block doesn’t move, it just becomes a containing block. Nice and simple, eh?
For an absolutely positioned element, the top
, right
, bottom
, and left
properties specify the distances from the corresponding edges of the containing block. To be specific, it is the padding edge of the containing block we’re talking about. That means the outer limits of any padding specified on the block, but not borders or the margin area.
So right:20px
means the right-hand edge of the absolutely positioned element should be 20 pixels from the right-hand edge of the containing block.
In a CSS2-compliant browser, we could specify all four properties to give both the position and the dimensions of our absolutely positioned element. Unfortunately, IE5/6 do not support this, so in the real world, we will often need to specify one vertical position (either top
or bottom
) and one horizontal position (left
or right
) and then set the width
property and, sometimes, the height
property.
These bugs also apply to values specified as percents. In IE5/6, they do not apply to the dimensions of the containing block, but of the parent block. As we have seen, those can be two very different things. The percentage bugs are also present in Opera, up to and including version 8. They are fixed in Opera 9.
I mentioned earlier that absolutely positioned elements are removed from the document flow. They do not affect anything that comes later in the markup; these elements will be laid out as if the absolutely positioned element wasn’t there. This makes absolute positioning virtually impossible to use for a multi-column layout if you also need a full-width footer.
But although an absolutely positioned element doesn’t care about what follows, it isn’t totally unaware of what came before it in the markup. The default value for the four dimensional properties is auto
, which is also a valid value to assign explicitly. Setting left:auto
means the left edge of the absolutely positioned element will occur where it would have been if it were a normal, static element. We could still set top:6em
or whatever to establish a vertical position. This can sometimes be useful when you want to pin an element in only one dimension, for instance in drop-down menus.
Absolute positioning can be very useful, as long as you understand how it works (and are aware of the browser bugs). The key to success is to be well aware of your containing blocks.
Fixed Positioning
Fixed positioning is similar to absolute positioning. The only real difference is that for fixed positioning, the containing block is always the initial containing block. You know, the one that is “chosen by the user agent” and “could be related to the viewport.” The top
, right
, bottom
, and left
properties refer to the edges of the browser window (or the paper, when printing).
Basically, an element with fixed positioning is stuck at a specific position in the browser window and doesn’t move, even if you scroll the document. When printing, a fixed element is printed on every page, at the same position on each page.
That sounds just great, doesn’t it? Didn’t you always want a navigation menu that didn’t scroll with the document, but those clunky JavaScript solutions never really cut it? The position:fixed
thing looks like the answer to all your prayers, and you’re about to cry out, “Hallelujah!” But then you realize that this is CSS we’re talking about. There is always a but somewhere. Torn between hope and despair, you look through the CSS 2.1 specification and cannot find a caveat. Is it possible…?
No, of course not. What crushes those dreams, cruelly and insensitively is—what else?—Microsoft Internet Explorer. Versions 5.x and 6 do not support position:fixed
, so it’s back to those clunky JavaScript solutions after all. Deep down you knew it, didn’t you?
Z-Index
A relatively positioned element occupies space in one place, but the rendered box may be somewhere else. Absolutely positioned elements and fixed elements are removed from the document flow. In all three cases it’s possible that we end up with boxes that are superimposed on top of other boxes: overlap. We will have to be aware of this and make sure to make room for them if this effect is undesirable.
Sometimes, overlaying boxes can be a desirable effect, but we might like some control over which box is on top. We can control this with the use of the z-index
property, which applies to positioned elements (i.e., anything except position:static
). In addition to the auto
and inherit
keywords, this property accepts an integer value (which may be negative).
A computer screen is (currently) two-dimensional. It’s a flat area with a width and a height, onto which we can render our CSS boxes. But CSS actually works in three dimensions. The X and Y axes are our normal horizontal and vertical axes, respectively. The Z axis is perpendicular to those two. Think of it as pointing straight into the front of and out the back of the screen. The higher the value of the z-index
property, the closer the element is to the user. This is called the stacking level.
Going into the details of stacking contexts and stacking levels is beyond the scope of this article, so let us just say that a positioned element establishes something called a stacking context. The element and its positioned children can be assigned a stacking level, using the z-index
property. This stacking level only applies within the parent’s stacking context, though. Or, to put it differently, we can only change the stacking order of elements within the same stacking context.
Consider a markup fragment such as the following:
...
...
...
...
Let’s say the div
s with id="first"
and id="next"
are relatively positioned, which means they are the containing blocks for any absolutely positioned children. Let’s also say that the inner div
s (a, b, c, and d)are absolutely positioned. We can affect the stacking order of a
and b
using z-index
, because they live within the same stacking context (that of first
). If we want a
to be on top of b
, we can set z-index:1
for a
(or z-index:5
, or z-index:99
—the value is not important, it merely has to be higher than the z-index
of the element over which you want to place the element concerned).
We can also affect the stacking order of first
and next
, should they overlap, because they live in the same stacking context.
But what if we want next
or c
to slide in between a
and b
? Now we’re out of luck, because next
and c
exist in different stacking contexts from a
and b
, and there is no way that we can insert them between those two.
Likewise, we cannot intersperse child elements of b
with child elements of a
, because a
and b
establish their own separate stacking contexts.
Just as knowledge of the containing block is extremely important for absolute positioning, the stacking context is important when using z-index
.
Closing Up
I’ve created an example document that shows the various positioning schemes in action. It will not work properly in Internet Explorer 5.x or 6, since they don’t support fixed positioning, but users with Opera 9, Firefox, Safari, or IE7 should be able to see it.
All the CSS that controls the layout is present in the markup, so all you have to do to see how it is done is to view the page source.
CSS positioning has its uses, but in many real-world applications, it isn’t all that usable for the page layout. Floats are often better, although they are fraught with browser bugs.
If you always know which column will be the longest, then you may use positioning for layout. Fixed positioning must be avoided for the time being, at least for public sites, since the most ubiquitous browser doesn’t support it.