Excited to start a new Deep Dive, and even more so to learn about layout techniques, as this is the part of CSS I feel like I am still most in the dark about.
Before planning out a page, you need to consider how a browser’s default styling might affect our layout. It may give certain elements like the body and lists certain margins and paddings by default. If you don’t reset these, the browser will use its own instead, which can lead to an inconsistency of our layout between browsers. A CSS reset is good to use because it removes most of those styles, giving you a clean slate to work from in all browsers. Below, you can see the page with normal browser styling.
There are two common CSS reset methods used today.
- Reset CSS – a method that zeros out every elements default browser styles. That means that for all properties like padding, margin, etc, they’re all set to zero or none. The line height and fonts are also affected. It should look exactly like it does below in every browser. You shouldn’t use all the of the reset CSS if you don’t need it – you can customize it and remove the selectors you don’t. You just copy and paste the styles on the site into your CSS.
- Normalize.css – depending on the size of your project, this zeroing out might not be the best option. It may make a lot of elements unreadable, forcing you to spend time writing more CSS to produce the new default values. Normalize is more suitable for this, as a lot of the times is totally okay to leave in some of those default browser styles. It brings all browsers down to a even playing field, by leaving those useful default browser styles. However, it also styles elements with no common default styles, so it looks consistent in every browser. This is used in frameworks like Bootstrap and Foundation. You just link to it like any other CSS file. It’s pretty well split up and labeled, so you’ll know what each rule is doing and why. Once again, you can remove sections you don’t need for that specific project, or add things you do need.
Block vs inline elements – most elements are one of these by default, but you can modify that using the display property.
block level elements take up an entire line in the layout, so no elements can be positioned to their right or left.
In the example page we’re starting with, it’s missing a wrapper or main container, which are commonly used to center a layout and keep it from looking too wide or narrow, depending on the device or viewport width. There are two ways to do this. The first is using the body element as the wrapper, as the body is a container anyways. Start by giving it a width of 90%, which will prevent the layout from stretching out too wide, and setting the margin to auto will center it on the page. The advantage to this method is you don’t have to create an additional div element for our layout. However, with this method if you want to add a full background color or image, we’ll have to add it to the html element instead. This is ok, but adding a wrapper div gives you a little more control over the layout.
Simply wrap all the content in a div, give it a class, then use that to set the width to 90% and the margin to auto.
Now, in the normalize.css we set the body’s default margins to 0, so why is there a 24px gap above our header at the top? What we’re actually seeing here is the top margin of our h1 element. But, the h1 element is inside of its header element container, which has the green background, shouldn’t the margin show up there? This is called a margin collapsing issue. The reason this is happening here is because there’s no border, padding, content or anything to separate the top margin of the block level body element with the top margin of its first child (the main-wrapper div). If there’s nothing to separate those margins, then they collapse, which is why we’re seeing the margin for the h1 element outside of its parent element. This is also happening in the soon to be columns below.
To fix this, give the parent element (in this case the header container) either a border or padding. In the example below, you can now see the backgrounds of the header container, the h1 and the list clearly defined.
If you tried to set the class for the ul to display:inline as well, it wouldn’t look any different because the list items act as block level items. They’re actually set by the browser to the list item display mode that generates a block level box. So, set the individual list items to display:inline as well. Below, I show what happens if you don’t set the ul element to inline as well, then what it looks like for both. The reason the first example still shows the list items on the second line is because the ul is still block level.
Next, we’ll give the h1 and the list items some padding and a border-radius to make them rounded, and give the h1 a margin-right to put some space between it and the list items.
Now, the downside to this inline method is that you can’t give inline elements a width or a top or bottom margin. There’s also a minor whitespace issue that happens with them, notice the default space between the list items. This won’t go away even if you set the margins and paddings to zero. The reason it’s happening is that it’s counting the line breaks in the html as whitespace (like spaces between words in a p element). One solution to this is to push the elements right next to each other in the html with no spaces, but obviously this sucks, as you don’t want to affect the presentation via the html.
A better solution is to apply negative margins to the list items.
Now, these are nav links, so we probably do want spacing. So, add use margin-right to make them bigger.
New issue – since the display is inline, when the browser gets narrow it gets all bunched up on itself as they wrap on themselves. It’s wrapping how text would wrap in a paragraph.
To fix this you can give the .main-header a whitespace property of nowrap, but notice how now it breaks out of the container.
To fix this, you can use the inline-block value for the display property. when you do this, notice how there’s more space now there as well, and that they’ll move to the next line if the browser width gets too narrow. This also lets us give these elements a width and a top or bottom margin. It treats elements as inline elements, that can be styled the same way as block level elements, but they’re still distributed as inline with other inline elements.
It’s worth noting, the same whitespace issue for inline elements also happens for inline-block elements. The reason the size of the header container increased when we switched to inline-block is because we’re using a h1 element, and headings by default have a top and bottom margin, which normalize.css does not reset.
If you were to hover, you’d notice the clickable areas for the lines in the logo and list items is small, it’s only on the text itself. To make the link hover target larger and take up the full width and height of its parent container element. To fix this, we need to set the display of the anchor elements to block, because display values are not inherited. We also took the padding from the h1 and list items, and gave it to the anchors as well. Note – even though the anchor elements are block level, since their parent elements are inline-block elements, they don’t display on the next line.
Table is another handy display mode. It emulates the old school table based layouts that were used before css. It allows you to easily vertical align elements to the center of its parent container.
First, in the parent container you need to set the display property to table. You must also give it a width of 100%, so it takes up 100% of its own parent container div. Finally give it a min-height of 150px.
You can see there is a bit of a box model bug appearing here. Because the header has 20px of padding all around, the browser is calculating that padding into the headers total width and height as well. This causes there to be 40 pixels of padding on the right there. To fix this, use the box-sizing property. You can actually declare it once, with the universal selector (*), and set it to border-box
Next, we need the main-logo and main-nav elements to display as table-cell. This will make them display as a table-cell in a table. Now, we can make it align vertically with vertical-align: middle;. We’ll also remove the top margins of the list items and the logo (which I’ve commented out here so you can see where it was before), as our table display mode will take care of the spacing for us. Regardless of the height we set for the header, the logo and nav elements will always be vertically centered now.
To fix the spacing, we’ll add some padding-LEFT to the .main-nav, which is the ul.
Now, if we had used a margin instead, it won’t work because you’re not able to use margins on elements that are displayed as table cells.
Finally, when the viewport is resized, things start to look strange. This is common behavior of table cell layout.
To fix this, use a media query. We’ll set the logo and nav elements back to block with their initial padding and margins, and give the list items a little margin-top so they have some spacing between them. Now, when the browser is resized, everything looks good.
Column layout with inline-block
We’re starting with this layout:
Each div with content has the class col. We’ll start by setting their display to inline-block, and give them some padding, about 20px. We’ll also give them some widths, which add up to 100% of their parent container.
Now, that doesn’t look right. The reason this is happening is due to the whitespace issue discussed before with the list items in the nav. Once again, the fix for that is to give these columns a negative margin-right of -5px. Apparently the negative pixel value depends on the font size we’ve set.
One thing to note with inline-block, is you’ll see some that are vertically aligned bottom by default, like the secondary content div above. The best way to adjust this is to give the columns a vertical-align of top. This works similar to using floats, without the collapsing problem that comes with them.
Sometime we’ll want the footer to be positioned at the very bottom of the viewport, regardless of how tall or short the columns of content are, which it’s not doing right now in the example above. There are a few methods to create what’s called a sticky footer, which will do that. The simplest way is as follows:
First, select all the main containers on the page (html, body, .main-wrapper, .col) and give them a height of 100%. This height of 100% is calculated based on the height of the parent element. So, if the height of all the main containers is set to 100%, we’ll get the desired result.
We’ll then improve out footer by adding some padding styles to make it thicker and center align the text.
Now, if you want the layout to span the full width of the viewport, simply remove the width and margin declarations for the .main-wrapper.
We can now introduce new block elements and still maintain the flow of the document. We’ll add a new div with an h1 and a p element.
We’ll then style it with a background color, some padding and align the text center. Notice how it lines up nicely with the other divs due to how we set this page up.
You can even put it somewhere else, like between the secondary content and the footer, and it will still work. It only won’t work if you put it between the primary and secondary content divs.
Floats come from print design, where a picture would be to the left and the article would flow around it’s right. Using the property let’s you place it along the left or right side of its container, outside of the normal document flow.
Creating a horizontal Menu
Floats are technically block level elements, but they act like inline elements because they don’t exist on a line of their own. With floats, the surrounding content usually wraps around the floated element, because it exists outside of the normal document flow. This is what we’re starting with:
We’ll start with the logo.
That doesn’t look right, but makes sense. Notice how the nav links are ignoring the space that the logo element would normally take up on the page. But the content (Link 1, Link 2, etc) wraps around the logo element, because content wraps around the floated element.
So, we now need to float the list items.
Now, we’ll mess with their margins to help them sit better.
It appears like that because the main-nav ul is still in the normal document flow, even though it’s child elements are not. To fix this, simply make the main-nav ul float as well.
We can also make it float right instead.
The Float Clearfix
The floats added to the header elements above have caused another layout issue. Anytime we float an element, it takes it out of the normal document flow, which can sometimes cause the parent element to collapse, because it no longer honors the space of the content inside it. This is what’s happening in our header, since both of its child elements are floated. The reason it’s not collapsing now though is because it has a min-height set to 100px. If we were to remove that, we get this.
This is an okay workaround, but it’s not future proof and leaves us with an fragile layout. Now, if we were to also float the main-header left, and give it a width of 100%, it would actually fix the issue as well.
But, this leads to a new issue where the elements below the main-header will now start wrapping around it. For example, if the width of the header was less than 100%.
Though we could fix this, floating the main-header actually isn’t the best solution. What you should do is what’s called a clearfix, which forces the parent element to clear its children. We went over this before for the CSS foundations deep dive. We’ll use a ::after psuedo element with blank content, and clear:both here makes it so that it clears both the right and left sides of a float. The .group class will be used to group the child elements within the parent, to prevent it from collapsing. So, now anytime we need to clear a container, we simply need to give it the class .group.
Similar to inline block elements, floats move to the next line as the width of the viewport gets smaller.
As before, we’ll use a media query to make this looks better. We’ll set the float to initial and set the margin-right to 0 for the logo.
Floating Columns and Images
We already have a .col class for the divs of content on the page. If we float them left, nothing really happens (except the footer wrapping around the last div).
To fix this, we need to give the columns a width.
Now, with the extra content div we can see we once again have the collapsing container issue. To fix this, we’ll add the .group class to the content-row container div it’s sitting in, to apply the clearfix.
Notice how the grey part appearing in extra content before was actually the footer jammed up in there. The content in the footer is wrapping around the last floated element, the secondary content div, but it’s grey background is pushing back through the other parts.
Next, we’ll tweak the width of some of the columns. We’ll set the primary one to width: 40%. then use .col:last-child pseudo-class to select the last .col element in its parent container to float it right. I’m not sure why we did that last part though, as setting the width of the primary one have the same results.
You can change the order they display by setting them to float:right;
You can also reorder the content in the markup (like move extra above primary) and it will still fit together well.
Next, we’ll make the layout 100% of the browser, by removing the wrap elements properties.
Next, let’s inset an image into the primary content div.
Next, we’ll make the text wrap around the image by setting it’s width to 50% and float:left;
To create space between the image and text, give the image a margin. If we were to apply the margin to the text instead, it would not have the same effect. Since the image is outside of the normal document flow due to the float, margin for the text would only create space between it and the left side of the blue column it’s sitting in.
Finally, we need to make it look good on mobile.
We’ll use a media query and make it so we remove all the floats (which will make the columns display one after another in the normal document flow), and then also set the image’s width to 100% so that it takes up it’s full parent container.
Even if we add more content, it will still fit well.
Mobile First Layout
It’s better to start with mobile, then build bigger sites, rather than starting big then going smaller. In the examples above, they’re using normalize.css to reset the values, then adding the property values for the site, then adding even more values with the media query. For example, we set a margin-right for one of the elements, then in the media query reset it back to 0 when the site viewport is under 768px. Same with the floats for the columns. This isn’t very efficient, as it forces the browser to load and parse all the css when it doesn’t have to.
So, we provide the minimal styling be default, then build on that with media queries. This will let the mobile site load faster. We’ll refactor the css for the main layout elements, which gets us this far.
Now that we have our mobile view on lock, we can build on that using media queries and the min-width media feature. First, we’ll style the header. Then we’ll style the columns so they float nicely. Then, we’ll add the clearfix rule to prevent the header container from collapsing since all its children are floated. Finally, we’ll restyle the image with a float so the content wraps around it.