Introduction to CSS Layout

This article needs an editorial review. How you can help.

CSS layout is the art of using various CSS properties to alter the positioning of elements on a document in order to fit the design requirements. CSS provides many layout mechanisms, the more advanced and modern techniques are so complex that they get their own articles. In this article, we will introduce the basic techniques that have been used for years.

To properly layout a document with CSS, there are a few notions that one must know. The most important of these is HTML text flow. We will cover it in this article. Articles about other layout mechanisms will, at some point or the other, refer back to what we are discussing here.

The flow

At its most basic level an HTML document is a text document structured with tags. In such a document, the text flows. That means text is displayed in the reading direction (from left to right, for example, in Latin based languages like English or French) and is broken automatically - creating new lines - each time the text reaches an edge of the document.

Each elements in the document alters the flow of text in various ways:

  • Some elements can simply follow the text flow as if they were non-existent.
  • Some elements can force a line break at any point in the flow whether it has reached the edge of the document or not.
  • Some elements can create a new text flow for their inner content independent from the main text flow.

Elements display categories

CSS is used to define how an HTML element behaves within the flow and how it get in and out of that flow. The element behavior is defined using the property display. This property can take tons of values but let's focus on the four most important:

none
This value completely removes the element from the flow, exactly as if the element and its content weren't there in the first place.
inline
This value makes an element "transparent": it flows with the text normally and its content is part of the global text flow.
block
This value is for the element to break the text flow with a forced line break before and after it. Its content is no longer part of the global text flow and flows only within the constraints provided by the element box model.
inline-block
This value makes the element somewhat in between inline and block type display: like inline boxes it flows with the text normally but, like block boxes, it's content is no longer part of the global text.

Let's see an example.

HTML:

<p class="none">
  1. I'm a big black cat,
  <span>walking under a ladder,</span>
  and I can see broken mirrors everywhere.
</p>

<p class="inline">
  2. I'm a big black cat,
  <span>walking under a ladder,</span>
  and I can see broken mirrors everywhere.
</p>

<p class="block">
  3. I'm a big black cat,
  <span>walking under a ladder,</span>
  and I can see broken mirrors everywhere.
</p>

<p class="inline-block">
  4. I'm a big black cat,
  <span>walking under a ladder,</span>
  and I can see broken mirrors everywhere.
</p>

CSS:

span {
  width: 5em;
  background: yellow;
}

.none span         { display: none;         }
.inline span       { display: inline;       }
.block span        { display: block;        }
.inline-block span { display: inline-block; }

Results:

Altering the flow

By setting the display property you're already altering the flow, but you can go further.

Text layout

While an HTML document is nothing more than a long text flow, CSS provides many properties to deal with simple text layout. The text layout is everything that allows changing the text line breaking rules and the way the text is positioned over the natural text line.

Those properties are: hyphens, text-align, text-align-last, text-indent, vertical-align, white-space, word-break, and word-wrap.

Except for text-align andtext-indent the other properties have subtle effects on the text and vertical-align is often used with inline-block boxes.

An example will make things clearer.

HTML:

<p lang="en">WHEN Scrooge awoke, it was so dark, that looking out of bed, he could scarcely distinguish the transparent window from the opaque walls of his chamber. He was endeavouring to pierce the darkness with his ferret eyes, when the chimes of a neighbouring church struck the four quarters. So he listened for the hour. To his great astonishment the heavy bell went on from six to seven, and from seven to eight, and regularly up to twelve; then stopped. Twelve! It was past two when he went to bed. The clock was wrong. An icicle must have got into the works. Twelve! He touched the spring of his repeater, to correct this most preposterous clock. Its rapid little pulse beat twelve: and stopped.</p>
<p class="format" lang="en">WHEN Scrooge awoke, it was so dark, that looking out of bed, he could scarcely distinguish the transparent window from the opaque walls of his chamber. He was endeavouring to pierce the darkness with his ferret eyes, when the chimes of a neighbouring church struck the four quarters. So he listened for the hour. To his great astonishment the heavy bell went on from six to seven, and from seven to eight, and regularly up to twelve; then stopped. Twelve! It was past two when he went to bed. The clock was wrong. An icicle must have got into the works. Twelve! He touched the spring of his repeater, to correct this most preposterous clock. Its rapid little pulse beat twelve: and stopped.</p>

CSS:

.format {
  /* The first line is "pull" to a 2em distance */
  text-indent: -2em;

  /* We need to compensate the negative indent
     to avoid unwanted text clipping and keep
     the whole text within the boundary of its
     element box */
  padding-left: 2em;

  /* The text is aligned on both edges, adjusting
     spacing between words as necessary */
  text-align: justify;

  /* The last line of the block of text is centered */
  -moz-text-align-last: center;
       text-align-last: center;

  /* Rather than line break between two words the line
     break can occur inside words, according to the rules
     defined for the text language. This makes nice word cut
     with a clear hyphen dash. If you don care about word
     breaking rules, you could just use word-break or
     word-wrap instead  */
  -webkit-hyphens: auto;
     -moz-hyphens: auto;
      -ms-hyphens: auto;
          hyphens: auto;
}

As you might notice, some properties are written multiple times with some prefix. This is because those prefixed properties are still experimental for some browsers and it is considered best practice to use them all with the standard property at the end of the list in order to provide the best backward compatibility possible.

Results:

It's worth noting that the trick we used to compensate the negative text indentation is a very common trick. Any property that accepts a length also accepts negative values. By fiddling with negative values and compensating them with other properties,  it's possible to produce very clever effects on the layout, especially when it applies to properties of the box model.

Floating

Okay, handling text is nice, but at some point what we really want is to move boxes around the document. The first way to handle that is to deal with floating boxes. Floating boxes are still attached to the global text flow,  but the text will flow around. Sounds weird, so let's see an example.

Simple floating

HTML:

<div>
  <p class="excerpt">"Why, it isn't possible," said Scrooge, "that I can have slept through a whole day and far into another night. It isn't possible that anything has happened to the sun, and this is twelve at noon!" </p>
  <p> The idea being an alarming one, he scrambled out of bed, and groped his way to the window. He was obliged to rub the frost off with the sleeve of his dressing-gown before he could see anything; and could see very little then. All he could make out was, that it was still very foggy and extremely cold, and that there was no noise of people running to and fro, and making a great stir, as there unquestionably would have been if night had beaten off bright day, and taken possession of the world. This was a great relief, because "three days after sight of this First of Exchange pay to Mr. Ebenezer Scrooge or his order," and so forth, would have become a mere United States' security if there were no days to count by.</p>
</div>

CSS:

.excerpt {
  /* A floating box will act like a block whatever
     the value of display we are using */
  display: block;

  /* Our box is floating to the left, which means
     it will stack on the left side of the containing
     block and the text will flow on its right side. */
  float: left;

  /* It is required to set a width to a floating box.
     If we don't its width will be set
     automatically, which means that it will grow as much
     as possible and nothing will flow around it, like an
     ordinary block box */
  width: 40%;

  /* We set some margins on the right and bottom side of
     the box to avoid having the text flowing around being
     in direct visual contact of our floating box */
  margin: 0 1em 1em 0;

  /* We make our floating box more visible with
     a simple background color */
  background: lightgrey;

  /* As we have a solid background color it's a nice idea
     to push the content a little bit away from the edges
     of the box */
  padding: 1em;
}

Results:

Layout with floating

This is a very simple effect to start tweaking the flow to our wishes. Now it's possible to do better and start performing some true layout. A floating box that floats in a given direction stacks horizontally, it's a very convenient way to create rows of boxes instead of natural columns: In the flow, block boxes stack in columns and floating boxes stack in rows.

Once again, an example will make things clearer.

HTML:

<div class="layout">
  <div class="row">
    <p class="cell size50">Scrooge went to bed again, and thought, and thought, and thought it over and over and over, and could make nothing of it. The more he thought, the more perplexed he was; and the more he endeavoured not to think, the more he thought.</p>
    <p class="cell size50">Marley's Ghost bothered him exceedingly. Every time he resolved within himself, after mature inquiry, that it was all a dream, his mind flew back again, like a strong spring released, to its first position, and presented the same problem to be worked all through, "Was it a dream or not?"</p>
  </div>
  <div class="row">
    <p class="cell size100">Scrooge lay in this state until the chime had gone three quarters more, when he remembered, on a sudden, that the Ghost had warned him of a visitation when the bell tolled one. He resolved to lie awake until the hour was passed; and, considering that he could no more go to sleep than go to Heaven, this was perhaps the wisest resolution in his power.</p>
  </div>
  <div class="row">
    <p class="cell size33">The quarter was so long, that he was more than once convinced he must have sunk into a doze unconsciously, and missed the clock. At length it broke upon his listening ear.</p>
    <p class="cell size33">
      "Ding, dong!"<br>
      "A quarter past," said Scrooge, counting.<br>
      "Ding, dong!"<br>
      "Half-past!" said Scrooge.<br>
      "Ding, dong!"<br>
      "A quarter to it," said Scrooge.<br>
      "Ding, dong!"<br>
      "The hour itself," said Scrooge, triumphantly, "and nothing else!"
    </p>
    <p class="cell size33">
      He spoke before the hour bell sounded, which it now did with a deep, dull, hollow, melancholy ONE. Light flashed up in the room upon the instant, and the curtains of his bed were drawn.
    </p>
  </div>
</div>

CSS:

/* This is our main layout container */
.layout {
  /* A background makes it visible */
  background: lightgrey;

  /* We add a small visual spacing to harmonize
     the distance between the cells content and
     and the layout border */
  padding   : 0.5em;
}

/* A floating box gets somewhat out of
   the flow, so if their container is empty
   it will have a zero height size and the
   floating box will overflow it. To
   avoid such a situation, we make sure floating
   boxes are not allowed to overflow. In
   that specific context, with an overflow
   hidden, floating boxes won't be clipped,
   the parent box will extend to avoid any
   floating box overflow.  */
.row {
  overflow: hidden;
}

/* Each cell is a left floating box */
.cell {
  float : left;

  /* we add padding to our cell to create
     some nice visual gutters between them */
  padding   : 0.5em;

  /* Because we are adding padding, we need
     to be sure that it will not impact
     the box width. */
  box-sizing: border-box;

  /* As margins cannot be controlled with the 
     box-sizing property, we need to be sure
     there is none applied to our cell. */
  margin    : 0;
}

/* Those are the sizes we can apply to our boxes */
.size33  { width:  33%; } /* Not exactly a third, but good enough */
.size50  { width:  50%; } /* A half */
.size100 { width: 100%; } /* A full row */

Results:

Using floating boxes this way, is what many CSS frameworks do. It's a robust and well-known technique but it has limits as everything must go with the flow: it's not possible to handle boxes in arbitrary order, variable sizing can be quite tricky to achieve, and vertical centering is impossible. We encourage you to dig deeper as floating boxes has been studied for long and they are among the most robust solutions for a simple layout that must be compatible with legacy browsers.

If you want to better understand all the subtleties of floating boxes, we encourage you to read All about float by Chris Coyer.

Positioning

If floating boxes are still part of the flow, another mechanism exists to perform some layouts by extracting boxes out of the flow: CSS Positioning. Positioning is acheived by defining a positioning context with the position property and then allows boxes to be positioned using top, left, right, and bottom properties.

The position property can take on four different values:

static
This is the default value for all elements: they are part of the flow and don't define any specific positioning context.
relative
With this value, elements are still part of the flow, but they can be visually moved around their positions with top, left, right, and bottom. They also define a positioning context for their children elements.
absolute
With this value, elements are pushed out of the flow and no longer influence it. The position of such blocks is defined by the top, left, right, and bottom properties. The 0,0 position point for the top/left corner of the box is the top/left corner of the closest parent element which defines a positionning context other than static. If there is no parent with a positioning context, then, the 0,0 position point for the top/left corner of the box is the top/left corner of the document.
fixed
With this value, elements are pushed out of the flow and no longer influence it. The position of such blocks is define by the top, left, right, and bottom properties. The 0,0 position point for the top/left corner of the box is the top/left corner of the browser window viewport.

Such positioned boxes can stack on top of each other. In that case, it's possible to change the stacking order by using the z-index property.

Okay, let's see an example to visualize it at work.

HTML:

<div class="relative">
    <div class="absolute-one">
        <p>Position:absolute</p></br>
        <p>Top Right</p>
    </div>

    <div class="absolute-two">
        <p>Position:absolute</p></br>
        <p>Bottom Centre</p>
    </div>

    <div class="absolute-three">
        <p>Position:static</p></br>
        <p>Where it falls</p>
    </div>
</div>

CSS:

p {
  text-align:  centre;
  color: #fff;
}

/* Setting the position to relative
allows any child elements to be positioned 
anywhere, in relation to its container. */

.relative {
  position: relative;
  width: 95%;
  margin: 0 auto;
  height: 300px;
  background-color: #fff;
  border: 3px solid #ADD8E6;
}

/* Just some styles for text alignment /*
.relative div {
  text-align: center;
  padding: 5px;
  display: block;
  width: 125px;
  height: 125px;
  background-color: #ADD8E6;
}

/* By setting this div to position absolute
we can position this element anywhere in relation
to the 'relative' div /*
.absolute-one { 
   position: absolute;
   top: 0;
   right: 0;
}

/* Unlike the first div which was positioned at the
top right corner of the container div. '.absolute-two' 
is positioned bottom centre. By setting both left and right
to 0, along with margin:auto.
.absolute-two { 
   position: absolute;
   bottom: 0;
   right: 0;
   left: 0;
   margin: auto;
}

/* Where the div would fall naturally within it's container.
This is also useful for returning elements from a floated 
position. E.g. on responsive styles. /*
.absolute-three { 
   position: static;
}

Results:

If CSS positioning isn't really used for full layout, it is used quite often to deal with trick UX effect such as navigation layout, tooltip and such. This is something you'll see on a regular basis so we encourage you to get into it. Among various resources about it, we suggest you take a look at the article CSS positioning 101 by Noah Stokes.

What's next

The flow, the floating boxes,  and CSS positioning are the basic CSS knowledge that will drive you into CSS layout. Now you are ready to use CSS to its full potential! You should now take some time looking at practical usage of CSS. If you want to dig even deeper into layouts, you should definitely take a look at the other layout mechanism that exists with CSS: Table display, multiple columns layout, and flexible box layout.

 

Document Tags and Contributors

 Contributors to this page: Joshydt, hbloomer, asdofindia, Jeremie
 Last updated by: Joshydt,