facebook google plus twitter
Webucator's Free Advanced CSS Tutorial

Lesson: Advanced CSS Page Layout

Welcome to our free Advanced CSS tutorial. This tutorial is based on Webucator's Advanced CSS Training course.

Originally, web designers were forced to use tables for anything beyond the most basic layout.

CSS has changed that: as browser support for CSS became ubiquitous, pages could be laid out with standard CSS to look the same across browsers. With more features and finer control, CSS now allows us to craft complex page layouts, even to the point of styling differently for different devices.

So the challenge is more deciding what you want to do rather than what you can do. Let's take a look at some of the decision points and some common page layouts.

Lesson Goals

  • Use CSS positioning and float to layout professional web pages.
  • Use CSS3 columns to display content in columns.
  • Use the Flexbox to layout page content.

Resetting Styles

Web browsers make certain assumptions that can sometimes interfere with layouts. Designers often have to undo or override these defaults. It has become a common practice to do so from the outset. This practice has come to be known as "resetting" styles.

A very simple reset that affects page layout is shown below:

* {
	margin: 0;
	padding: 0;
	border: 0;
}

Note the universal selector. This effectively does away with all margin, padding and border for every element including the body, headings, lists, and list items. It then becomes the designers responsibility (freedom?) to assign margin, padding and border.

The screenshots below illustrate the impact of this reset:

Default Styles Reset Styles

Resets can get much more sophisticated. CSS Guru Eric Meyer provides a freely downloadable detailed reset style sheet at http://meyerweb.com/eric/tools/css/reset/.

The simple reset we have shown above gives us a blank slate on which to lay out our page. Now we have to make some decisions:

  • Will our design use a fixed width or the full width of the browser?
  • How many "rows" and "columns" will we have?
  • What sort of positioning should we use: absolute, relative, fixed, or static?

We address these questions in the sections below by looking at specific layouts. For all layouts, we'll assume we want a header and a footer.

Full Width Layouts

Pages designed to use the full width of the browser window are often described as having a "fluid" or "liquid" layout. That's because one or more of the columns must be automatically resized according to the size of the browser window.

Two-column

We'll start with the simple two-column layout shown below: Fluid Two Column

As you can see, the page has four content areas. Let's take a look at the HTML first:

Code Sample:

CssPageLayout/Demos/fluid-two-column.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<link href="fluid-two-column.css" rel="stylesheet">
<title>Two-column fluid layout</title>
</head>
<body>
<div id="wrapper">
	<header id="header">
		<h1>Two-column fluid layout</h1>
	</header>
	<section id="main">
		<div id="menu">
			<h2>Menu</h2>
			<p>This column is fixed.</p>
			<p>This column is fixed.</p>
			<p>This column is fixed.</p>
			<p>This column is fixed.</p>
			<p>This column is fixed.</p>
		</div>
		<article id="content">
			<h2>Content</h2>
			<p>This column is fluid.</p>
		</article>
		<div class="clearer"></div>
	</section>
	<footer id="footer">footer</div>
               </footer>
</body>
</html>

As you can see, the page has four content areas. Let's take a look at the HTML first:

Code Explanation

The four content areas have the following ids: header, footer, menu, and content. Note that we use HTML5 semantic tags here: <section> for the main section, <header> for the header, <nav> for the left-side navigation element, and <footer> for the footer.

The page content is contained in a wrapper div with id wrapper, which we can use to put a border around the content. Later we'll see how we can use it to adjust the width of the whole content. The wrapper div is divided into three "rows": header, main, and footer.

The main row is broken up into two "columns": menu and content. It also has a "clearer" div, which we'll discuss shortly.

Now the CSS:

Code Sample:

CssPageLayout/Demos/fluid-two-column.css
* {
	margin:0;
	padding:0;
	border:0;
}
#wrapper {
	border: 1px solid #000;
}
#header {
	border-bottom: 1px solid #000;
	padding: 10px;
	background-color: #eee;
}
#main {
	background-image: url(Images/bgMain.gif);
	background-repeat: repeat-y;
}
#menu {
	width: 180px;
	float: left;
	padding: 10px;
	border-right: 1px solid #000;
	color: #fff;
}
#content {
	margin-left: 200px;
	border-left: 1px solid #000;
	padding: 10px;
	line-height: 2em;
}
.clearer {
	clear: both;
}
#footer {
	border-top: 1px solid #000;
	background-color: #eee;
	padding: 10px;
}

Code Explanation

  1. We'll start by resetting margins, padding, and borders with the universal selector:
    * {
    	margin: 0;
    	padding: 0;
    	border: 0;
    }
    The result: Resulting Image
  2. Next let's add a border to the wrapper div:
    #wrapper {
    	border: 1px solid #000;
    }
    The result: Image with Border
  3. Next let's add a bottom border to the header and give it some padding and a background:
    #header {
    	border-bottom: 1px solid #000;
    	padding: 10px;
    	background-color: #eee;
    }
    The result: Bottom Border Added
  4. Next let's add a background image to the main section. The background image is 1px high by 200px wide. This is a trick to apply a background color to the left column that extends all the way to the bottom of the column. If we put the background image or a background color on the menu element itself, the background would end at the bottom of the content in that div. By putting it on the containing main element, we make sure that the background extends to the bottom of the whole column:
    #main {
    	background-image: url(Images/bgMain.gif);
    	background-repeat: repeat-y;
    }
    The result: Extended Background
  5. Now we need to float the menu nav left so that the content article will come up to its right edge. We'll also add some other styles to make it more readable and give it a width of 180px and padding of 10px, which gives it a total width of 200px: 180px + (2 x 10px):
    #menu {
    	width: 180px;
    	float: left;
    	padding: 10px;
    	border-right: 1px solid #000;
    	color: #fff;
    }
    The result: Fluid Two Column ImageWhat happened there??? The text in the content article is wrapping underneath the menu. We don't want that.
  6. And there's another problem - watch what happens when we shorten the text in the content article: Shortened ImageThe height of the main section only extends to the bottom of its content, which it considers to be the content article. So the background only tiles down that far.
  7. The solution to the wrapping problem is to set the left margin of the content article to the total width of the menu element:
    #content {
    	margin-left: 200px;
    	border-left: 1px solid #000;
    	padding: 10px;
    	line-height: 2em;
    }
    The result (Notice we added the content back to the content article): Resulting ImageThat looks good.
  8. But what happens when we remove content from the content article:Content RemovedThis is the same problem we mentioned earlier. The height of the main section only extends to the bottom of its content.
  9. This is where the clearer comes in. By putting the clearer div at the bottom of the main section, we force its height to extend:
    .clearer {
    	clear: both;
    }
    The result: Resulting ImageGood! We're almost there. And it even works when the content element grows: Content div Grows
  10. Now we just need to style the footer a little to visually separate it from the rest of the page:
    #footer {
    	border-top: 1px solid #000;
    	background-color: #eee;
    	padding: 10px;
    }
    The result: Resulting ImageAnd we're done. Phew.

Three-column

Let's see how we would add a third column with the right and left columns fixed and the center column fluid: Third Column Added

First the HTML:

Code Sample:

CssPageLayout/Demos/fluid-three-column.html
---- C O D E   O M I T T E D ----

<div id="wrapper">
	<header id="header">
		<h1>Three-column fluid layout</h1>
	</header>
	<section id="main">
		<div id="inner">
			<nav id="menu">
				<h2>Menu</h2>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
			</nav>
			<aside id="sidebar">
				<h2>Sidebar</h2>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
			</aside>
			<article id="content">
				<h2>Content</h2>
				<p>This column is fluid. This column is fluid. 
---- C O D E   O M I T T E D ----
</p>
			</article>
			<div class="clearer"></div>
		</div>
	</section>
	<footer id="footer">footer</footer>
</div>
</body>
</html>

Code Explanation

Things to notice:

  1. We've added a sidebar as an <aside> tag. Notice the location: it comes before the content article even though it will show up to the right of it.
  2. We've added an inner div, which we will use to get the background on the right-hand column.

Now the CSS:

Code Sample:

CssPageLayout/Demos/fluid-three-column.css
---- C O D E   O M I T T E D ----

#inner {
	background-image: url(Images/bgMainInner.gif);
	background-position: right;
	background-repeat: repeat-y;
}
#menu {
	width: 180px;
	float: left;
	padding: 10px;
	color: #fff;
}
#sidebar {
	width: 160px;
	float: right;
	padding: 10px;
	color: #ded;
}
#content {
	margin-left: 200px;
	margin-right: 180px;
	padding: 10px;
	line-height: 2em;
}

---- C O D E   O M I T T E D ----

Code Explanation

  1. First, what we need to do is to float the sidebar to the right:
    #sidebar {
    	width: 160px;
    	float: right;
    	padding: 10px;
    	color: #ded;
    }
    The result: Sidebar Floated to the Right Notice how the content in the center flows under the sidebar.
  2. To fix that we need to give the content article a right margin equal to the total width of the sidebar aside (160px + 2*(10px)) = 180px:
    #content {
    	margin-left: 200px;
    	margin-right: 180px;
    	padding: 10px;
    	line-height: 2em;
    }
    The result: Right Margin Equal to Total Width
  3. Finally, to get the background, we use the same trick with our new inner wrapper div we used earlier. The only difference is we need to set the background-position to right so that it tiles along the right side of the browser:
    #inner {
    	background-image: url(Images/bgMainInner.gif);
    	background-position: right;
    	background-repeat: repeat-y;
    }
    The result: Resulting Image

Positioning the Headings

Duration: 15 to 25 minutes.

In this exercise, you will position the heading elements within the columns, so the page looks like this: Heading Elements within Columns

  1. Open CssPageLayout/Exercises/fluid-three-column.html in your browser. It should look familiar.
  2. Open CssPageLayout/Exercises/fluid-three-column.css in your editor. Modify the code so that the page looks like the screenshot above.
  3. Although it's not required, you may find that it helps to modify the HTML file as well.
  4. Save your changes and test your solution in the browser.

Challenge

See if you can change the page so that the design is centered and takes up 85% of the width of the screen: Centered DesignYou'll only need to change the CSS.

Solution:

CssPageLayout/Solutions/fluid-three-column.html
---- C O D E   O M I T T E D ----
<div id="inner">
			<nav id="menu" class="column">
				<h2>Menu</h2>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
			</nav>
			<aside id="sidebar" class="column">
				<h2>Sidebar</h2>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
				<p>This column is fixed.</p>
			</aside>
			<article id="content" class="column">
				<h2>Content</h2>
				<p>This column is fluid. This column is fluid. This column is fluid. This column is fluid. This column is fluid. This column is fluid. This column is fluid. This column is fluid. This column is fluid. This column is fluid. This column is fluid. This column is fluid. This column is fluid.</p>
			</article>
			<div class="clearer"></div>
		</div>
---- C O D E   O M I T T E D ----

Code Explanation

Solution:

CssPageLayout/Solutions/fluid-three-column.css
* {
	margin: 0;
	padding: 0;
	border: 0;
}
---- C O D E   O M I T T E D ----
.column h2 {
	position: relative;
	left: -10px;
	top: -10px;
	padding: 3px;
	line-height: 1em;
	border: 2px solid #ccc;
	font-variant: small-caps;
	font-size: 1em;
	letter-spacing: .5em;
}

#menu h2 {
	background-color: #fff;
	color: #1b1bdb;	
}

#sidebar h2 {
	background-color: #ded;
	color: #460d3b;	
}

#content h2 {
	background-color: #000;
	color: #fff;	
}
---- C O D E   O M I T T E D ----

Code Explanation

Challenge Solution:

CssPageLayout/Solutions/fluid-three-column-challenge.css
* {
	margin: 0;
	padding: 0;
	border: 0;
}
body {
	min-width: 600px;	
}
#wrapper {
	border: 1px solid #000;
	width: 85%;
	margin: auto;
}
---- C O D E   O M I T T E D ----

Code Explanation

When you set margin to auto it makes the left and right margins equal. You might have tried setting each margin to 7.5%. Theoretically, that should work, and it does in Firefox and Safari, but it can be buggy in some older browser versions. Use auto instead.

Another rule we've added to this solution is:

body {
	min-width: 600px;
}

This prevents the columns from wrapping when the browser window is made really small.

Fixed Width Layouts

Changing our fluid layouts to fixed layouts is easy. In fact, the challenge to the last exercise gets us most of the way there. Instead of setting the width to a percentage value, we set it to a fixed-width value in pixels and we set margin to auto:

#wrapper { border: 1px solid #000; width: 950px; margin: auto; }

The screenshots below show the results: Fixed Two Columns Fixed Width Three-Column LayoutThe full code can be found in CssPageLayout/Demos/fixed-two-column.css and CssPageLayout/Demos/fixed-three-column.css.

A Recommendation on Positioning

When designing, we recommend the following general principles with regards to positioning choices:

  1. Try to layout the page using static positioning, which is the default. In this case, you'll rely entirely on float and the box model (padding, margin, and border) to lay out your page.
  2. If static positioning doesn't work (e.g., you need to have overlapping elements), try relative positioning first.
  3. Finally, use absolute positioning in conjunction with the other forms of positioning and only when you need to have an element positioned in an exact location.

Other Methods of Layout

There are many other ways to create multi-column layouts, each with its own advantages and disadvantages. We feel that the method we've laid out here is a good choice; however, it does have its disadvantages:

  • Meaningful markup: One of the advantages of CSS is that it makes it possible to structure HTML in a logical way. Ideally, you shouldn't have to throw in extra tags for formatting purposes. The methodology we show here makes a small compromise on this in two ways:
    1. The wrapper divs used to create "rows" and to apply backgrounds to columns are there for formatting purposes only. There is no other reason to structurally combine the individual columns in the "row".
    2. The "clearer" div contains no content at all. It's only there for formatting purposes.
  • Content order: In the three-column layout, we put both the menu and the sidebar before the main content. This is counter-intuitive as they don't show up on the page in that order. If a screenreader were to read the page, it wouldn't know to read the main content area before it read the sidebar. Likewise, search engines might place more importance on words that show up earlier in the source code. We've had to push those words down the page to achieve the layout we want.

The good news is that the other methodologies use the same basic CSS properties you have learned to use here. So if you decide that you can't deal with either of the two compromises we've made above, you should be able to pick up the other methodologies relatively easily.

One that we recommend is known as the Holy Grail. This methodology uses less meaningless markup (i.e., markup for formatting purposes only) and it doesn't compromise on content order. However, it makes other compromises:

  1. It doesn't allow for backgrounds that flow to the bottom of the "row".
  2. It's more difficult to modify and maintain. For example, to add padding to one column, you need to adjust multiple declarations in the code. With the methodology we show here, you simply change the value of the appropriate padding property.

Laying Out a Page - The Power of Float

Duration: 30 to 45 minutes.

Creating well-designed pages is more art than science. Somebody has to come up with the design. You don't have to be a designer to be a CSS master. You just have to be able to turn a designer's design into a web page. In this exercise, you will learn how much you know. Most of the CSS is done, but you'll have to review it and compare it to the HTML to get an understanding of the different design elements. The design you're shooting for is shown below: Design

  1. Open CssPageLayout/Exercises/services.html in a browser. You'll see some work needs to be done.
  2. Open CssPageLayout/Exercises/services.html in your editor. Study the HTML. You'll only need to make one change to this file. If you need a hint, check out the suggestion below.
  3. Open CssPageLayout/Exercises/style.css in your editor.
    1. You'll notice a comment at the top with a bunch of float declarations. Your job is to figure out where to put these declarations. You will not need to add any new rules.
    2. One trick to getting a feel for the different elements on the page is to add dotted red borders to the different rules. For example, to call out the heading, change the rule like this:
      #header {
      	background-image: url(Images/header.png);
      	background-repeat: no-repeat;
      	height: 207px;
      	border: 5px dotted red;
      }
      The result: Resulting Image

Exercise hint: you need to add a "clearer" div somewhere.

Solution:

CssPageLayout/Solutions/services.html
---- C O D E   O M I T T E D ----
			</article>
			<div class="clearer"></div>
		</div>
	</div>
	<div id="footer"> ramblingsoul.com © All Rights Reserved <br>
		Template provided to Webucator for use in its CSS courseware. <br>
		<a href="http://ramblingsoul.com" title="high quality free css templates">css template by ramblingsoul </div>
</div>
---- C O D E   O M I T T E D ----

Solution:

CssPageLayout/Solutions/style.css
* {
	padding: 0;
	margin: 0;
}

---- C O D E   O M I T T E D ----
#wrap #header #logo {
	width: 260px;
	padding-top: 50px;
	padding-left: 50px;
	height: 150px;
	float: left;
}
#wrap #header #headercontent {
	width: 300px;
	float: right;
	padding-right: 55px;
	padding-top: 60px;
	height: 125px;
}
---- C O D E   O M I T T E D ----
#submenu a {
	background-image: url(Images/submenudivider.png);
	background-repeat: no-repeat;
	background-position: left;
	display: block;
	float: left;
	height: 16px;
	padding-top: 2px;
	padding-right: 15px;
	padding-left: 15px;
	color: #666;
	text-decoration: none;
}
---- C O D E   O M I T T E D ----
#mainmenu a {
	display: block;
	height: 40px;
	padding-right: 25px;
	padding-left: 25px;
	float: left;
	text-decoration: none;
	padding-top: 15px;
	background-attachment: url(Images/menudivider.png);
	background-repeat: no-repeat;
	background-position: 0px 3px;
	color: #5B920A;
}
---- C O D E   O M I T T E D ----
#homeleft {
	width: 480px;
	float: left;
}
---- C O D E   O M I T T E D ----
#homeright .column1 {
	width: 31%;
	float: left;
	padding-right: 5px;
}
#homeright .column3 {
	float: left;
	width: 32%;
	padding-left: 8px;
}
#homeright .column2 {
	float: left;
	width: 33%;
}
---- C O D E   O M I T T E D ----
#submenu a:visited, #submenu a:active {
	background-image: url(Images/submenudivider.png);
	background-repeat: no-repeat;
	background-position: left;
	display: block;
	float: left;
	height: 16px;
	padding-top: 2px;
	padding-right: 15px;
	padding-left: 15px;
	color: #666666;
	text-decoration: none;
}
#submenu a:hover {
	background-image: url(Images/submenudivider.png);
	background-repeat: no-repeat;
	background-position: left;
	display: block;
	float: left;
	height: 16px;
	padding-top: 2px;
	padding-right: 15px;
	padding-left: 15px;
	color: #000000;
	text-decoration: none;
}
---- C O D E   O M I T T E D ----
.productimage {
	float: left;
}
---- C O D E   O M I T T E D ----


The design used in this exercise was provided courtesy of RamblingSoul.com.

CSS3 Text Columns

CSS3 introduced tools for formatting text into multiple columns, much like one might see in a print newspaper. While not page layout per se (that is, we don't use it to position the header, navigation, and footer elements), the CSS3 "Multiple-column Layout Module" offers an easy way to organize text content into columns. We can control the size and number of columns, the gap between columns, and an optional rule between columns. Listed below are the relevant properties:

While browser support for CSS3 features like columns is better and better with each version of the major browsers, it's a good idea to use the corresponding -webkit- and -moz- vendor prefixes (for Safari/Chrome and Firefox, respectively) for these properties; Internet Explorer supports unprefixed column properties from version 10. See caniuse.com for current browser support.

CSS3 Columns
Property Description Example/Possible Values
columns Shorthand for setting width and count columns: 10em 2;
column-count Number of columns Number of columns or auto
column-fill How to populate columns with content auto (fill columns sequentially) or balance (balance content equally, as possible)
column-gap Size of gap between columns pixel or em length or normal
column-rule Width, style, and color of rule 2px dotted #000 or 1px solid hsl(20, 20%, 90%); can also specify column-rule-width, column-rule-style, and column-rule-color properties explicitly
column-span How many columns an element should span none, 1 or all [NB: not well supported by browsers]
column-width Width of columns in pixels or ems, or auto column-width:45px

At its simplest, we can use the shorthand columns property to specify the width and number of columns:

.columns {
	columns: auto 3;
}

The code above would style any element of class columns to flow text into three columns (because the second parameter has value 3) of equal width (because the first parameter has value auto). We can use any of the specific properties to exercise more control over the layout - to add a rule, say, between the columns or to add a gap between the columns.

Let's look at an example that demonstrates CSS3 columns:

Code Sample:

CssPageLayout/Demos/fixed-two-column-text-columns.css
* {
	margin: 0;
	padding: 0;
	border: 0;
}
#wrapper {
	border: 1px solid #000;
	width: 950px;
	margin: auto;
}
#header {
	border-bottom: 1px solid #000;
	padding: 10px;
	background-color: #eee;
}
#main {
	background-image: url(Images/bgMain.gif);
	background-repeat: repeat-y;
}
#menu {
	width: 180px;
	float: left;
	padding: 10px;
	border-right: 1px solid #000;
	color: #fff;
}
#content {
	margin-left:200px;
	border-left:1px solid #000;
	padding:10px;
	line-height:1.2em;
}
#content h2 {
	margin-bottom:10px;
}
#content .columns {
	-moz-columns:auto  2;
	-webkit-columns:auto 2;
	columns:auto 2;
	-moz-column-rule:1px solid #000;
	-webkit-column-rule:1px solid #000;
	column-rule:1px solid #000;
}
#content p {
	margin-bottom:17px;
}
.clearer {
	clear: both;
}
#footer {
	border-top: 1px solid #000;
	background-color: #eee;
	padding:10px;
}

Code Explanation

Open ClassFiles/CssPageLayout/Demos/fixed-two-column-text-columns.html in a text editor and in a browser to view the results; open ClassFiles/CssPageLayout/Demos/fixed-two-column-text-columns.css in a code editor to view the CSS code. We use a two-column fixed layout (that is, with the navigation on the left and the main content on the right) and we display the text in the large main column in two columns:

Columns demo

Inside the article with id content, we add a div with class columns, wrapping a series of paragraphs with text content.

In the CSS file, we style this new div so it has two equal-width columns (columns:auto 2) and add a thin, black vertical rule between the columns (column-rule:1px solid #000).

Flexible Box Model

The CSS3 flexible box model (or "flexbox", as it's commonly known) offers a handy way to control the horizontal and vertical arrangement of elements on a page. As the W3C description states:

In the flex layout model, the children of a flex container can be laid out in any direction, and can "flex" their sizes, either growing to fill unused space or shrinking to avoid overflowing the parent. Both horizontal and vertical alignment of the children can be easily manipulated. Nesting of these boxes (horizontal inside vertical, or vertical inside horizontal) can be used to build layouts in two dimensions.

Prior to CSS3, CSS defined four layout modes:

  • block layout - for laying out documents.
  • inline layout - for laying out text.
  • table layout - for laying out two-dimensional data in table fashion.
  • positioned layout - for laying out content in absolute terms, ignoring other elements in the document.

The flexbox layout mode is designed to address the shortcomings of these layout modes - the modes we've used for years to layout our pages - and to better address the challenges of modern web design - a way to present content visually that doesn't force us to exploit these other layout modes in ways for which they weren't originally conceived.

CSS's block layout is inherently designed for vertical organization of elements; inline is designed for horizontal layout. Flexbox is designed to be agnostic and, as the W3C's CSS Flexible Box Layout Module site states, "designed for laying out more complex applications and webpages".

An Evolving Standard

At the time of this writing, there is considerable (and justified) confusion about the "correct" CSS values for flexible box-model layouts. The use of vendor prefixes is recommended (and very much needed); support among browsers varies considerably. While the flexbox layout is becoming better and better supported by browsers, if you expect users with older browsers (even not-too-old browsers) as a significant percentage of your traffic, then using flex layout might not be a good idea.

Chris Coyer's excellent article "Old" Flexbox and "New Flexbox" details the evolution of the flexbox standard and browser support thereof: display:box and display:flexbox are outdated; display:flex is the current standard. We will use the newer (display:flex) syntax here, as that is the emerging consensus standard. But keep in mind that it is poorly supported as of now; Chrome is your best bet for the demos and exercises below.

See the caniuse.com site for the current state of browser support.

Examples

Flexbox layout is perhaps best demonstrated through a set of examples. Open ClassFiles/CssPageLayout/Demos/flex.html in a code editor and browser, and ClassFiles/CssPageLayout/Demos/flex.css in a code editor. The page presents a series of examples of flexbox layout; each example is a set of three <div>s illustrating some aspects and properties of the flexible box layout. Please note that, throughout our discussion here, for brevity, we will refer to the unprefixed syntax for the various properties; the example code includes the -webkit- prefix.

The markup for each example is very simple:

<div id="box1" class="box">
	<div>item 1</div>
	<div>item 2</div>
	<div>item 3</div>
</div>

At the top of our CSS, we give each "item" box some padding, set its text color to white, align its text in the center, and add some rounding to its corners. We use nth-child() to set the color for the first, second, and third element of each example.

"Example 1" shows the three <div>s arranged in a simple row:

#box1 {
	display: flex;
	flex-flow: row wrap;
}

The display: flex statement indicates that we want to use flexible box layout for child elements.

The flex-flow property is shorthand for flex-direction (the first value) and flex-wrap (the second value). flex-direction specifies how child elements are arranged in the container, defining both the main axis (whether elements should be presented vertically or horizontally) and also the order in which items should be placed along that axis (in-order or reverse.) Valid values for flex-direction are:

  • row (default)
  • row-reverse
  • column
  • column-reverse

In this first ("Example 1") example, we've used a value of row for flex-direction, so the three <div>s display horizontally. We'll experiment with other values in a bit.

Flexbox Example 1

The flex-wrap property defines whether children are forced into a single line (vertical or horizontal, depending on the flex-direction value). Valid values are:

  • nowrap (default)
  • wrap
  • wrap-reverse

Because the content in our three <div>s is small, this property doesn't affect the layout for "Example 1"; we'll change this in a later example.

"Example 2" presents the same three <div>s, but this time we specify how the content should justify:

#box2 {
	display: flex;
	flex-flow: row-reverse wrap;
	justify-content: space-around;
}

This second example specifies a row-based flex layout, just like the first example. But now we add justify-content, which defines how flex items align along the main axis of the flex container. In the absence of any individual flex items specifying how they take up space relative to their sibling flex items (which we'll see below), justify-content defines how unused space in the container will be allocated. Here's the list of possible values:

justify-content Values
Value Description
flex-start Pack items toward the start of the container
flex-end Pack items toward the end of the container
center Pack items toward the center of the container
space-between Unused space in the flex container is allocated evenly between the items, with the first and last items at the start and end of the container, respectively
space-around Unused space in the flex container is allocated evenly around the items

In the first example, in the absence of an explicit statement, #box1 had a default justify-content value of flex-start, meaning the elements were packed into the start (left) end of the container. In "Example 2," we set justify-content to space-between: each of the three <div>s takes up only as much space for itself as needed for its text content (plus padding), and the extra (light gray) space from #box2 is allocated evenly around each element. Try resizing the browser wider and narrower to see this change.

Flexbox Example 2

In "Example 3," we set flex-direction (the first value of flex-flow) to column-reverse; we also give a set height and width (300 and 100 pixels, respectively) to the container #box3. The result is that the three elements show in reverse order, with "item3" first and "item1" last.

We also set justify-content: flex-start. This has the result of packing the elements at the bottom of the container: since the "reverse" part of flex-flow: column-reverse wrap defines the "start" of the container as the end, justify-content: flex-start packs the elements at the bottom of #box3.

Flexbox Example 3

In "Example 4" and "Example 5," we give each of the three contained <div>s a bit more text content. We now see the effect of flex-wrap: wrap (the wrap value of flex-flow: row wrap): for "Example 4", at narrower browser widths the third item (and, for very narrow browser width, both second and third items) wraps to the next line. The screenshots below show "Example 4" in very narrow, less narrow, and wide browser widths:

Flexbox Example 4

"Example 5" looks exactly like "Example 4" when the browser is wide enough. When the browser narrows, however, the three contained elements in "Example 5" shrink in width, a result of the fact that we set #box5 to not wrap: flex-flow: row nowrap.

Flexbox Example 5

In "Example 6," we apply a style to each of the contained <div>s to allow it to specify its own order within the flow of the container: order:3, for instance, sets the "item 1" element to be third in the flow of elements within #box6; similarly, "item 2" is set as the first and "item 3" as the second. Values for order can be any positive or negative integer; the default is 0. Ordering is relative to the direction specified for the flow: reversing the order through a statement like flex-direction: row-reverse would mean that the element with the lowest order value would be on the right side of the container. The order property has no effect on elements that are not flex items.

Flexbox Example 6

Last, in "Example 7" and "Example 8," we demonstrate the use of the flex property on the contained elements. The flex property is a shorthand for setting flex-grow (the first value), flex-shrink (the second value), and flex-basis (the third value). In "Example 7," we set flex-grow to 8, 2, and 4 for each of the three contained elements, respectively. Coupled with the fact that we set flex-basis to 0 for each of the elements, the result is that the first element ("item 1 item 1 item 1") gets 8/14 of the total space, the second element gets 2/14 of the total space, and the third element gets 4/14 of the total space. To summarize: add up the total of all of the contained flex items' flex-grow values to get the denominator and allocate each element's space (width or height, depending on whether we are working with rows or columns) according to its value as the numerator.

Flexbox Example 7

The flex-basis value determines whether to allocate each flex element's width or height in the contained space absolutely or after determining each contained element's inherent width or height. A value of 0 means "allocate space absolutely"; as in "Example 7", we ignore how big or small each flex item is (per its content, padding, etc.) and set each item according to its flex-grow value.

In "Example 8," we set each flex item to have a flex-basis value of auto. This means that the browser will first give each flex item its own inherent width, then allocate the remaining space from the container around each element proportionally, per their flex-grow values. See the W3C specification for more information. Try changing the width of the browser to see the difference between "Example 7" and "Example 8".

Flexbox Example 8

We'll explore more of the flexible box layout concepts in the next exercise.

Flexible Box Layout

Duration: 20 to 30 minutes.

In this exercise, you will style a typical blog listing page, using flexible box layout to position and order elements. Before any user interaction, the finished page should look like this:

Flexbox Exercise screenshot - before click

The left navigation column remains fixed in width while the main content is fluid, expanding as the browser width changes. As you'll see, the markup code for the navigation elements comes after the markup code for the article listing.

The articles shown in the browser in date-ascending order (earlier to later); you'll note, as you work with the code, that the markup code lists the articles in date-descending order.

When the user clicks an article, it should show its (initially hidden) body content and, move to the top of the list, and show with some background color and padding:

Flexbox Exercise screenshot - after click

  1. Open ClassFiles/CssPageLayout/Exercises/flex.html in a code editor and in a browser to view the results.
  2. Style section#articlelisting and nav as flexible children of section#maincontent; use an appropriate value for flex-flow on #maincontent to ensure that the navigation (the second item, in terms of source-code order) appears on the left. This is a common need: one tactic for better search engine optimization is to keep the markup source for important content as high on the page as possible. Style the navigation elements as shown in the screenshot.
  3. Use appropriate values for flex for the nav and #articlelisting elements, so that nav appears on the left with a fixed width and #articlelisting appears on the right with a fluid width.
  4. Style #articlelisting to be a flex container, with each of the <article>s as a flexible child. Reverse the order of the <article>s: show them in the browser in the opposite of the order in which they appear in the code.
  5. Use the :target pseudo-class to style the user-selected <article>: use the order property to move the selected article (that is, the one the user has clicked on, whose id matches that of the fragment at the end of the page's URL) to the top. Remember that the default value for order is 0 - and be sure, when ordering <article>s, to account for the fact that we are showing them in reverse order. Unhide the body content for the targeted <article>and give it some formatting to set it off from the other articles. Display the contents of the displayed post in two columns.

Solution:

CssPageLayout/Solutions/flex.css
* {
	margin: 0;
	padding: 0;
	border: 0;
}
body {
	background: #ccc;
}
#main {
	width: 80%;
	margin: 1em auto;
	padding: 2%;
	background: #fff;
}
#maincontent {
	display: -webkit-flex;
	-webkit-flex-flow: row-reverse wrap;
	-webkit-justify-content: flex-end;

	display: flex;
	flex-flow: row-reverse wrap;
	justify-content: flex-end;
}
#articlelisting {
	-webkit-flex: 1 1 0;
	flex: 1 1 0;

	display: -webkit-flex;
	-webkit-flex-flow: column-reverse wrap;

	display: flex;
	flex-flow: column-reverse wrap;
}
article {
	padding: 0 5em 1.2em 0;
}
article h3 {
	margin: 0;
}
article time {
	font-size: 0.8em;
}
article div {
	display: none;
	font-size:0.9rem;
	-moz-columns: auto  2;
	-webkit-columns: auto 2;
	columns:auto 2;
}
article:target {
	-webkit-order: 1;
	order: 1;
	background-color: Linen;
	margin-bottom: 1em;
	padding: 1em;
}
article:target div {
	display: block;
}
nav {
	width: 180px;
	padding: 0 1em 0 0;
}
nav ul {
	margin: 0;
	padding: 0;
	list-style: none;
}
nav ul li a {
	display: block;
	margin: 0;
	padding: 0.25em;
	border-bottom: 1px solid lightGray;
}
nav ul li a.current {
	color: black;
	margin-left: 0.5em;
}

Code Explanation

We use the -webkit- prefix throughout in our solution; for brevity, we won't state it here in the review of the code.

We style #maincontent as a flex container with display: flex, present the child elements in reverse order with flex-flow: row-reverse wrap, and align the elements against the left edge with justify-content: flex-end (flex-end, rather than flex-start, because we have reversed the order.)

We style #articlelisting with flex: 1 1 0 and nav with no flex rules - thus #articlelisting has a flex-grow value (the first value of the flex shorthand) of 1 and nav has a fixed width. This fact, coupled with the fact that both elements have a flex-basis (the third value of the flex shorthand) value of 0 and that nav has a fixed width, means that, when the browser width is changed, the left-side navigation will stay fixed in width and the wider right column will be fluid.

#articlelisting acts both as a flex child element, as discussed above, and as a flex container element. We style #articlelisting with flex-flow: column-reverse wrap, which displays its several <article>s in reverse order, as desired.

We style article: target to format it slightly differently than its siblings and also, with order: 1, move it to the top of the list: since we list the children of #articlelisting in reverse column order, the highest order element will be shown first. We unhide the body content for the :targeted <article> with display:block.

We use columns: auto 2 to display the text contents in the targeted article in two columns.

Last, we add some formatting styles to the unordered lists items that make up the navigation element.