Box model recap

We looked at the basics of the CSS box model in our Introduction to CSS module. This article will provide a recap, and dive into some further details on the subject.

Prerequisites: HTML basics (study Introduction to HTML), and an idea of How CSS works (study Introduction to CSS.)
Objective: To recap the basics of the CSS box model, and add a few more details.

Box properties

As you'll no doubt know by now, every element within a document is structured as a rectangular box inside the document layout, with properties than can be altered like so:

  • width and height set the width/height of the content box.
  • The padding family of properties sets the width of the padding (don't forget longhand properties like padding-bottom that allow you to set one side at once).
  • The border family of properties sets the width, style and color of the border (there are many longhand border properties available).
  • The margin family of properties sets the width of the outer area surrounding the CSS box, which pushes up against other CSS boxes in the layout (again, longhand properties like margin-bottom are available).

Some other points worth remembering:

  • Box heights don't observe percentage lengths; box height always adopts the height of the box content, unless a specific absolute height is set (e.g. pixels or ems.) This is more convenient than the height of every box on your page defaulting to 100% of the viewport height.
  • Borders ignore percentage width settings too.
  • Margins have a specific behavior called margin collapsing: When two boxes touch against one another, the distance between them is the value of the largest of the two touching margins, and not their sum.

Overflow

When you set the size of a box with absolute values (e.g. a fixed pixel width/height), the content may not fit within the allowed size, in which case the content overflows the box. To control what happens in such cases, we can use the overflow property. It takes several possible values, but the most common are:

  • auto: If there is too much content, the overflowing content is hidden and scroll bars are shown to let the user scroll to see all the content.
  • hidden: If there is too much content, the overflowing content is hidden.
  • visible: If there is too much content, the overflowing content is shown outside of the box (this is usually the default behavior.)

Background clip

Box backgrounds are made up of colors and images, stacked on top of each other (background-color, background-image.) They are applied to a box and drawn under that box. By default, backgrounds extend to the outer edge of the border. This is often fine, but in some cases it can be annoying (what if you have a tiled background image that you want to only extend to the edge of the content?) This behaviour can be adjusted by setting the background-clip property on the box.

See the CSS box model article background-clip coverage for an example.

Outline

The outline of a box is something that looks like a border but which is not part of the box model. It behaves like the border but is drawn on top of the box without changing the size of the box (to be specific, the outline is drawn outside the border box, inside the margin area.)

Note: Beware when using the outline property. It is used in some cases for accessibility reasons to highlight active parts of a web page such as links when a user clicks on them. If you do find a use for outlines, make sure you don't make them look just like link highlights as this could confuse users.

Advanced box properties

Now we've had a brief recap, let's look at some more advanced box properties that you'll find useful

Setting width and height constraints

Other properties exist that allow more flexible ways of handling content box size — setting size constraints rather than an absolute size. This can be done with the properties min-width, max-width, min-height, and max-height.

An obvious use is if you want to allow a layout's width to be flexible, by setting its outer container's width as a percentage, but you also don't want it to become too wide or too narrow because the layout would start to look bad. One option here would be to use responsive web design techniques (which we'll cover later), but a simpler method might be to just give the layout a maximum and minimum width constraint:

width: 70%;
max-width: 1280px;
min-width: 480px;

You could also couple this with the following line, which centers the container it is applied to inside its parent:

margin: 0 auto;

0 causes the top and bottom margins to be 0, while auto (in this case) shares the available space between the left and right margins of the container, centering it. The end result is that when the container is within the min and max width constraints, it will fill the entire viewport width. When 1280px width is exceeded, the layout will stay at 1280px wide, and start to be centered inside the available space. When the width goes below 480px, the viewport will become smaller than the container, and you'll have to scroll to see it all.

You can see the above example in action in min-max-container.html (see the source code).

Another good use of max-width is to keep media (e.g. images and video) constrained inside a container. Returning to the above example, the image causes a problem — it looks ok until the container becomes narrower than the image, but at this point the image starts to overflow the container (as it is a fixed width). To sort the image out, we can set the following declarations on it:

display: block;
margin: 0 auto;
max-width: 100%;

The first two make it behave like a block element and center it inside the parent container. But the real magic is in the fourth one — this constrains the image's width to be at the most the same size as the container, therefore if the container tries to shrink smaller than the image width, the image will shrink along with it.

You can try this modified example at min-max-image-container.html (see the source code).

Note: This technique works as far back as Internet Explorer 7, so it is nicely cross browser.

Changing the box model completely

The total width of a box is the sum of its width, padding-right, padding-left, border-right, and border-left properties. In some cases it is annoying (for example, what if you want have a box with a total width of 50% with border and padding expressed in pixels?) To avoid such problems, it's possible to tweak the box model with the property box-sizing. with the value border-box, it changes the box model to this new one:

Let's look at a live example. We will set up two identical <div> elements, giving each one the same width, border and padding. We will also use some JavaScript to print out the computed value (the final on-screen value in pixels) of the width for each box. The only difference is that we've given the bottom box box-sizing: border-box, but we've left the top box with its default behaviour.

First, the HTML:

<div class="one"></div>
<div class="two"></div>

Now the CSS:

html {
  font-family: sans-serif;
  background: #ccc;
}

.one, .two {
  background: red;
  width: 300px;
  height: 150px;
  padding: 20px;
  border: 10px solid black;
  margin: 20px auto;
}

.two {
  box-sizing: border-box;
}

Finally, a little JavaScript to measure the overall computed widths:

var one = document.querySelector('.one');
var two = document.querySelector('.two');

function outputWH(box) {
  var width = box.offsetWidth;
  var height = box.offsetHeight;
  box.textContent = 'Width: ' + width + 'px, Height: ' + height + 'px.'
}

outputWH(one);
outputWH(two);

This gives us the following result:

So what's happened here? The width and height of the first box are equal to content + padding + border, as you'd expect. The second box however has its width and height equal to the width and height set on the content via CSS. The padding and border haven't aded onto the total width and height; instead, they've taken up some of the content's space, making the content smaller and keeping the total dimensions the same.

You can also see this example running live at box-sizing-example.html (see the source code).

Box display types

Everything we've said so far applies to boxes that represent block level elements. However, CSS has other types of boxes that behave differently. The type of box applied to an element is specified by the display property.

Common display types

There are many different values available for display, the three most common ones of which are block, inline, and inline-block.

  • A block box is defined as a box that's stacked upon other boxes (i.e. content before and after the box appears on a separate line), and can have width and height set on it. The whole box model as described above applies to block boxes.
  • An inline box is the opposite of a block box: it flows with the document's text (i.e. it will appear on the same line as surrounding text and other inline elements, and its content will break with the flow of the text, like lines of text in a paragraph.) Width and height settings have no effect on inline boxes; any padding, margin and border set on inline boxes will update the position of surrounding text, but will not affect the position of surrounding block boxes.
  • An inline-block box is something in between the first two: It flows with surrounding text without creating line breaks before and after it like an inline box, but it can be sized using width and height and maintains its block integrity like a block box — it won't be broken across paragraph lines (in the below example the inline-box goes onto the 2nd line of text, as there is not enough space for it on the first line, and it won't break across two lines.)

By default, block level elements have display: block; set on them, and inline elements have display: inline; set on them.

Uncommon display types

There are also some less commonly-used values for the display property that you will come across in your travels. Some of these have been around for a while and are fairly well supported, while others are newer and less well supported. These values were generally created to make create web page/application layouts easier. We discuss the most interesting ones below: display: table, display: flex, and display: grid (and related values).

CSS tables

HTML tables are fine for displaying tabular data, but many years ago — before even basic CSS was supported reliably across browsers — web developers used to also use tables for entire web page layouts — putting their headers, footers, different columns, etc. in various table rows and columns. This worked at the time, but it has many problems — table layouts are inflexible, very heavy on markup, difficult to debug, and semantically wrong (e.g. screen reader users have problems navigating table layouts).

CSS tables exist to allow you to layout elements like they were a table, without any of the issues described above — this may sound strange, and you should use table elements for tabular data, but sometimes this can be useful. For example, you might want to lay out a form with the labels and text inputs lined up; this can be tricky, but CSS tables make it easy.

Let's look at an example. First, some simple markup that creates an HTML form. Each input element has a label, and we've also included a caption inside a paragraph. Each label/input pair is wrapped in a <div>, for layout purposes.

<form>
  <p>First of all, tell us your name and age.</p>
  <div>
    <label for="fname">First name:</label>
    <input type="text" id="fname">
  </div>
  <div>
    <label for="lname">Last name:</label>
    <input type="text" id="lname">
  </div>
  <div>
    <label for="age">Age:</label>
    <input type="text" id="age">
  </div>
</form>

Now, the CSS for our example. Most of the CSS is fairly ordinary, except for the uses of the display property. The <form>, <div>s, and <label>s and <input>s have been told to display like a table, table rows, and table cells respectively — basically they'll act like HTML table markup, causing the labels and inputs to nicely line up by default. All we then have to do is add a bit of sizing, margin, etc. to make everything look a bit nicer and we're done.

You'll notice that the caption paragraph has been given display: table-caption; — which makes it act like a table <caption> — and caption-side: bottom; to tell the caption to sit on the bottom of the table for styling purposes, even thought the markup is before the inputs in the source. This allows for a nice bit of flexbiility.

html {
  font-family: sans-serif;
}

form {
  display: table;
  margin: 0 auto;
}

form div {
  display: table-row;
}

form label, form input {
  display: table-cell;
  margin-bottom: 10px;
}

form label {
  width: 200px;
  padding-right: 5%;
  text-align: right;
}

form input {
  width: 300px;
}

form p {
  display: table-caption;
  caption-side: bottom;
  width: 300px;
  color: #999;
  font-style: italic;
}

This gives us the following result:

You can also see this example live at css-tables-example.html (see the source code too.)

Note: CSS tables have fairly good cross browser support, being available as far back as Internet Explorer 8.

Flexible boxes

CSS is a powerful language that can do many things, but one place where it has traditionally fallen down is layout. As you will see in the CSS layout module (TBD), the traditional old fashioned layout methods of using float and positioning work, but sometimes they feel more complex, fiddly, inflexible and hacky than they need to be. For example, what if you wanted to:

  • Vertically center a box of content (not just text; line-height won't work).
  • Make several columns containing different amounts of content have the same height, without using a fixed height, or faking it with background images.
  • Make several boxes in a line take up the same amount of the available space, regardless of how many of them there are, and if they have padding, margin, etc. applied.

The examples above are pretty much impossible to achieve with regular CSS — Flexible boxes (or flexbox) was invented to allow such things to be more easily achieved. Let's look at an example; first, some simple HTML:

<section>
  <div>This is a box</div>
  <div>This is a box</div>
  <div>This is a box</div>
</section>

<button class="create">Create box</button>
<button class="reset">Reset demo</button>

Here we've got a <section> element with three <div>s inside, plus a couple of buttons to create a new box, and reset the demo. By default, the child elements would not be laid out or sized at all, and using old traditional methods we'd have to carefully size each one, allowing for width, padding, border and margin, and if we added another child elelment we'd have to completely change all the values.

Let's instead do this with Flexbox:

html {
  font-family: sans-serif;
}

section {
  width: 93%;
  height: 240px;
  margin: 20px auto;
  background: purple;
  display: flex;
}

div {
  color: white;
  background: orange;
  flex: 1;
  margin-right: 10px;
  text-shadow: 1px 1px 1px black;
}

div:last-child {
  margin-right: 0;
}

section, div {
  border: 5px solid rgba(0,0,0,0.85);
  padding: 10px;
}

Two lines of this CSS are really interesting:

  • display: flex; tells the <section> element's children to be laid out as flexible boxes — by default, they will all stretch to fill the available height of the parent, whatever that is, and be laid out in a row — with enough width to wrap their content.
  • flex: 1; tells each <div> element to take up an equal amount of the space available in the row, no matter how many there are.

To illustrate further how amazing this is, let's add a little JavaScript so that you can add further child <div>s  by pressing the Create box button. You can also reset the demo if you add too many <div>s, by pressing the Reset demo button.

var section = document.querySelector('section');
var createBtn = document.querySelector('.create');
var resetBtn = document.querySelector('.reset');

function createBox() {
  var box = document.createElement('div');
  box.textContent = 'This is a box';
  section.appendChild(box);
}

createBtn.onclick = createBox;

resetBtn.onclick = function() {
  while (section.firstChild) {
      section.removeChild(section.firstChild);
  }
  createBox();
  createBox();
  createBox();
}

Here's the example live — have a play, to witness how powerful Flexbox is as a layout tool.

You can also find this demo at flexbox-example.html (see also the source code).

Note: Flexbox is only really available in fairly new browsers — Firefox, Chrome, Internet Explorer 11 and Edge, and Safari 6.1 and above. We will discuss strategies for coping with cross browser support in the layout module (TBD).

Grid layout

The most experimental feature to mention here is CSS Grids, which aren't supported very widely across browsers yet. Web pages are often laid out using grid systems, in the same manner as print media, and the idea here is to make this process easier and more natural, by defining a grid, and then defining which parts of the content sit within each area of the grid.

CSS Grids in their present state aren't really supported anywhere yet (except in experimental versions of Firefox and Chrome). IE and Edge support an older, obsolete version of the technology. This is something we can look forward to in the future!

What's next

After a quick little recap, we looked at some more advanced features of CSS for manipulating boxes, and finished up by touching on some advanced layout features. With this all behind us, we will now go on to look at Backgrounds.

Document Tags and Contributors

 Contributors to this page: chrisdavidmills
 Last updated by: chrisdavidmills,