Part 1: the box model
The box model
The foundation of CSS layouts is the box model. Everything in CSS is a rectangular box.
Going from the outside, each box has (optionally):
- margins that separate it from other boxes
- borders that can be different colours and styles (or images in CSS 3)
- padding that separates its border from its content
- content.
Box model diagram (Rich Hauck)
When people start using CSS, they often assume margins and padding are synonymous. The main difference you quickly discover is that padding inherits the background colour but margin doesn’t. This is because padding is conceptually “inside” the element, providing padding for its content, while margins are conceptually “outside” the element.
Every box in the CSS model has padding, margin and borders. Unless they are defined — by you or your browser — the size of each defaults to zero. In addition to the size of the internal content and any scrollbars, they add together to make up the width and height of the box.
The one unusual behaviour of these boxes in CSS is that the vertical margins between boxes collapse. That is, if you have a box with a ten-pixel bottom margin and a second below it with a ten-pixel top margin, you will only have ten pixels between the boxes because the margins collapseto the greater value of the two margins. Horizontal margins do not collapse.
In some circumstances, the padding and/or margins are ignored for the purposes of CSS layouts. We’ll cover this soon.
The next part in the series will be about the CSS visual formatting model.
Part 2: the visual formatting model
The visual formatting model
Now we’ve declared everything is is a rectangular box with margins, border and padding, we need to determine how to arrange these boxes on the screen. This is the role of the CSS visual formatting model.
Lots of boxes — how do we arrange them?
You could never say that the W3C keeps the scope of their standards too small. CSS as a whole attempts to address not just screens, projectors and printers — visual media — but also devices for listening to content, Braille rendering, and other ways to access content. The “visual” formatting model is limited to defining how to render content just for visual media like screens, projectors and paper.
In order to define a rendering model that fits well with the existing structure of HTML, CSS splits boxes into several types with different properties.
There are two primary types of boxes described by the visual formatting model:
- inline box
- block box.
Although there are other types of boxes, we’ll limit our discussion to these two major types. There’s enough complexity here to have an interesting discussion, and enough flexibility to create many different types of layouts.
The two types of boxes correspond pretty much with the element types in HTML 4. By default, inline boxes are generated by inline elements, and block boxes are generated by block boxes.
However, CSS provides the ability to change the type of box generated by an element. You do this with the display
property. For example, the following CSS style will format paragraphs as inline boxes instead of block boxes:
p {
display: inline;
}
As we’ll see later, the ability to change the type of box generated by certain content is critical to creating CSS layouts.
Sometimes, for the purposes of CSS formatting, there are situations where a single HTML element generates multiple boxes in the layout. An example is splitting an inline element into multiple lines. It also sometimes happens that CSS boxes are generated for content without a corresponding element. These are called “anonymous boxes” and you can read more about them in the specification if you’re interested.
The next part will cover the properties of inline and block boxes and how these two combine to create the normal flow.
Part 3: block and inline boxes
Block box formatting
Block boxes are formatted one after the other down the page. Block boxes can contain other block boxes (or inline boxes, as we shall see). The diagram below shows one example.
Block box formatting
In this example, you could think of block #1 as the <body>
tag in an HTML page. Blocks #2-5 could be paragraphs in the page.
Any element in HTML can be turned into a block box by setting the display
CSS property to “block”.
Inline box formatting
Inline boxes behave very differently to block boxes. Inline boxes flow into line boxes, and are split as necessary to fit the width of their container.
Inline box formatting
In this example, the inline elements have vertical alignment of bottom or baseline so that inline #2 sticks out the top of the first line. Inline #3 is split over two lines.
Vertical margin and padding on inline elements is ignored in the visual formatting model. You can use the line-height
property to adjust the height of inline elements instead.
Any element in HTML can be turned into an inline box by setting the display
CSS property to “inline”.
Normal flow
The following diagram shows the normal flow. Block boxes contain other block or inline boxes. Inline boxes flow into line boxes.
Normal flow
All blocks in this example have a position of “static”. Static is the default position value.
CSS 2 provides functionality to position boxes relative to, and outside of the normal flow. This is what we’ll cover in the next part.
Part 4: positioning
The CSS 2.1 visual formatting model has four kinds of positioning which can be used to move boxes around in a layout: static, relative, absolute and fixed positioning. These are essential to know if you are going to prepare a CSS-based layout.
Static positioning
As described in the last article, normal flow is the default positioning scheme for CSS boxes. Boxes in the normal flow are said to have “static positioning”.
Normal flow, all boxes have static positioning
Occasionally in CSS layouts, you’ll need to override some other positioning value (see below) to get back to the default. If you need to do this, you can set the position
property to “static”.
The following example statically positions all paragraphs elements with a class of “prose”, leaving them in the normal flow.
p.prose {
position: static
}
In CSS terminology, any box with a position value other than “static” is said to be positionedcontent. The three types of positioned content — relative, absolute and fixed — were first introduced in CSS 2, and are what we’ll cover now.
Relative positioning
Relative positioning allows content to be move relative to the normal flow. It is done by setting the position
property to “relative”, and optionally defining offsets with the top
, right
, bottom
and left
properties.
With relative positioning, neighbouring content in the page remains as if the relatively positioned content was actually positioned statically. Relative positioning is not used very often in CSS-based layouts, as its functionality isn’t needed for most common page layouts.
In the following simplified example, the second inline element has been relatively positioned. Note how the top
and left
property values are used as offsets from the box’s normal position, and the neighbouring content is laid out as if the box was not moved at all.
inline#2 {
position: relative;
left: 1em;
top: -1em;
}
Relative positioning example
Absolute positioning
Absolute positioning positions content relative to the nearest positioned ancestor box (recall that positioned content is any box with a position
value other than “static”), outside of the normal flow.
By outside the normal flow, I mean that neighbouring content is rendered as if the absolutely positioned box did not exist. If no positioned ancestor box exists, the absolutely positioned box is positioned relative to the canvas boundaries.
The top
, right
, bottom
and left
properties indicate how far away the element should be from the corresponding boundary of the containing box. The CSS specification says how positioned elements should behave if you have both top
and bottom
or both left
and right
defined (the box is stretched to the appropriate dimensions). However, Internet Explorer doesn’t conform to the specification in this matter, so you probably want to avoid using it.
With the following example CSS, paragraphs with a class of “date” would be positioned at the top-right of any container which is positioned, or at the top-right of the page if there is no positioned container.
p.date {
position: absolute;
top: 0;
right: 0;
}
We’ll see a visual example of absolute positioning in the demonstration layouts later in the series.
Fixed positioning
Like absolute positioning, boxes with position: fixed
are rendered outside the normal flow. Unlike absolute positioning though, fixed content is positioned relative to the viewport — the browser window, for most users.
This means that when you scroll down the page, the fixed content stays in the same spot on your screen, regardless of what else is on the page. Fixed content will typically overlap any other content on the page.
Fixed positioning doesn’t work in Internet Explorer 6, and therefore isn’t very useful for most CSS layouts which need to work in multiple browsers. However, it is quite surprising to see when you notice it on a web page.
One example of a fixed layout is on Sam Ruby’s blog, where the navigation bar on the right uses the following CSS. When you scroll down the page, the navigation bar stays at a fixed location in your viewport.
aside {
position: fixed;
right: 1em;
top: 5em;
}
There’s a lot of flexibility provided by these three new positioning mechanisms introduced in CSS 2. The next part will describe the final tool in the CSS layout toolbox: floats.
Part 5: floats
Floats
At its simplest, floating a box in CSS means moving it to the left- or right-most position possible in its containing box, without overlapping any other floated content. You do it by setting the float
CSS property to either ‘left’ or ‘right’; the default value is ‘none’.
Here’s an example which floats the second inline element to the left. (The original layout prior to applying the float is as shown in the normal flow example in the earlier articles.)
inline#2 {
float: left;
margin-right: 1em;
}
Floated content example
As you can see from the example, the floated box is removed from the normal flow. Line boxes inside the containing box wrap around the floated box or boxes. Once the floated element ends, element #4 is positioned back on the left edge of the container. Floated boxes are treated as block boxes for layout purposes, regardless of the value of their display
property.
The classic example of floated content is positioning an image on the left- or right-hand side of an article. I use this technique quite a lot in articles on my blog (example). This is what the float functionality is designed for in CSS.
Issues with floats
The technicalities around positioning floats in a web page are many. Because the initial CSS specification didn’t clearly define all the edge cases around floated layouts, there were different implementations from different browsers. In the following years, Internet Explorer didn’t have many updates, so many bugs with floated layouts are still prevalent in browsers widely used today.
A discussion of the issues around floated layouts and getting them to work perfectly in Internet Explorer as well as the modern browsers is enough to require another series of articles. So rather than give a comprehensive coverage of all these issues, I’ll just cover two of the most important ones. For more information, I’d recommend a thorough reading of the specification and other online documentation like Position is everything.
The two main problems people find with using floats for layouts in any browser is handling those boxes which either contain or are adjacent to floated content. Float boxes can extend beyond the bottom of the containing box if the other content in the box isn’t taller than the float. A floated box can also overlap the borders or margins of adjacent block boxes in the same container. Below is an example of both problems.
CSS float layout, looking somewhat broken
Assuming the author wants a layout where the borders of the container and the paragraph are in logical places, this layout seems a bit broken. I’ve made the image transparent so the problems are obvious. The red paragraph border goes behind the floated image when it should have its left border slightly to the right of the image. The blue container border doesn’t extend far enough down, so it also goes behind the floated image.
Fixing adjacent boxes
Let’s look at the red paragraph border problem first. The reason the paragraph border appears behind the image is because floats don’t reposition block boxes that are adjacent to them. In this example, the floated image is only pushing the line boxes inside the paragraph across. So the text is pushed to the right, but the actual paragraph box still stretches across the full width of the container.
The fix for this issue comes from a very well-hidden paragraph in the floats section of the CSS 2.1 specification:
The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with ‘overflow’ other than ‘visible’) must not overlap any floats in the same block formatting context as the element itself.
So to prevent our paragraph block from overlapping the floated content, the solution is to create a “new block formatting context”, in CSS specification author terminology. Sounds tricky, eh? Fortunately, it isn’t that hard. New block formatting contexts are created by any block which meets these criteria:
- is floated
- is absolutely positioned
- has a
display
property value of one of more unusual flavours (inline-block, table-cell, etc.) - has its
overflow
property set to something other than visible.
The last option is the one that is easiest to do in most cases: setting overflow: auto
on our paragraph will create a new “block formatting context”, and render the paragraph border in the right place.
p {
overflow: auto;
}
CSS float layout, now with paragraph border fixed
Fixing containing boxes
Now, the blue border of our container box is still in the wrong place. Because the paragraph contents is not as tall as the image, the container seems to collapse to the height of the paragraph. Thus the blue border still appears behind the image.
The CSS specification provides a way for boxes which contain floats to have a height which encompasses the full height of any contained floated content. This is covered by sections 10.6.6 and 10.6.7 of the specification.
This section applies to:
- Block-level, non-replaced elements in normal flow when ‘overflow’ does not compute to ‘visible’ (except if the ‘overflow’ property’s value has been propagated to the viewport).
…
In addition, if the element has any floating descendants whose bottom margin edge is below the bottom, then the height is increased to include those edges. Only floats that are children of the element itself or of descendants in the normal flow are taken into account, e.g., floats inside absolutely positioned descendants or other floats are not.
What this means is that the height of the containing box will expand to include any floated elements if it has an overflow
property value other than ‘visible’. So the fix for this problem is the same as the first, albeit for a different reason: set the overflow
property to ‘auto’.
p {
overflow: auto;
}
div {
overflow: auto;
}
CSS float layout, now with container border fixed
The content used for this is available as a float demonstration. You can launch Firebug and have a play around with disabling the CSS styles to see how it works.
The next article in the series will cover how we use all the CSS techniques covered so far—floats, positioning and the box model—to create useful page layouts.