Monday, September 28, 2015

CSS LAYOUT TUTORIAL - Absolute Positioning - PART 1

In this article, we're going to learn about absolute positioning in CSS.

What is absolute positioning?
Absolute positioning is a CSS layout technique that allows you to move an element to a precise location by supplying values known as offsets.

For example:

Here, I am using absolute positioning to place the paragraph 53 pixels away from the left edge and 107 pixels away from the top edge of the browser's viewport. This was done by adding a left offset of 53 pixels, and a top offset of 107 pixels.

Let's see how this works by creating our own example. Copy the code below and save it as an HTML document.
<!DOCTYPE html>

<html lang="en">

<head>

 <meta charset="utf-8">

 <title>Absolute Positioning</title>

 <style type="text/css">

  h1 {
   margin: 0;
   background-color: blue;
  }

 </style>

</head>

<body>

 <h1>Heading</h1>

 <p>This is a paragraph.</p>

 <h1>Another heading</h1>

</body>

</html>
What we have here is a basic html page that contains a heading, followed by a paragraph, followed by another heading. And if you take a look at the head element, you will see that there is a style sheet that contains a style rule for the h1 element that sets the margins to 0 and the background color to blue.

So if you view the page in the browser, it will look like this:

NOTE: The existing margins that you see are from the default html, body, and paragraph margins. But the h1 elements themselves have 0 margins.

Now let's go ahead and create a style rule for the paragraph so that we can position it absolutely. To do that, we use the CSS position property and give it a value of absolute. Other values are static, fixed, and relative, but we won't talk about those in this lesson.

So go back to the code and add this new style rule:
p {
 position: absolute;
}
We've just specified a position of absolute for the paragraph style rule, but we haven't added any offsets yet. The offsets are what will change the element's position. We'll add that later. For now, save the document and refresh your browser.

Now, even though we haven't added any offsets yet, we should see some changes just by adding the position:absolute property. The browser should display something like this:

You'll notice that the 2 headings no longer have any space in between them. This is why I added the background color and removed the margins for the h1 elements. I wanted you to clearly see the effect once position:absolute was added. It may look strange, but this is exactly what's supposed to happen when you apply a position of absolute to an element - the element will be taken out of the document's normal flow. Before we added an absolute position to the paragraph, the normal flow was:
  1. heading
  2. followed by the paragraph
  3. followed by another heading
But now that the paragraph was taken out of the normal flow, the new normal flow is now:
  1. heading
  2. followed by another heading
Basically, an element that's taken out of the normal flow will still be visible, but will be ignored by the other elements that still flow normally. That's why the headings are now adjacent to each other. We still see the paragraph, but the headings that used to surround it are now ignoring the space that the paragraph is supposed to be taking up, because to them, the paragraph is no longer part of the normal flow.

So remember: absolutely positioned elements are taken out of the normal flow.

Now that we've demonstrated that, let's go ahead and remove the heading elements, as well as the h1 style rule. So now, the style sheet should look like this:
<style type="text/css">

 p {
  position: absolute;
 }

</style>
And the body should look like this:
<body>

 <p>This is a paragraph.</p>

</body>
And now let's move on to offsets.

Setting the position property to absolute is not quite useful on its own. We'll also need to specify offset values so that we can move the element to a new location. But before we do that, let's set some more properties for the paragraph style:
p {
 width: 200px;
 height: 200px;
 margin: 0;
 padding: 0;
 background-color: aqua;
 position: absolute;
}
Here, I've specified a content area of 200px by 200px, a background color of aqua, and 0 margins and padding. I'm adding these because I want the paragraph to visually stand out more in order to better illustrate the paragraph's position.

Now we're ready to add offsets to our absolutely positioned paragraph. We have the option to add a horizontal offset and a vertical offset. To add a horizontal offset, use either the left or the right property. You'll have to choose just one. You cannot use both on the same element. To add a vertical offset, use either the top or bottom property. You can only choose one as well. You can, however, combine a horizontal and vertical offset. So in those cases, the possible combinations are:
  • top and left
  • top and right
  • bottom and left
  • bottom and right
Go back to the style sheet, and give the paragraph a top offset of 10px and a left offset of 10px:
p {
 width: 200px;
 height: 200px;
 margin: 0;
 padding: 0;
 background-color: aqua;
 position: absolute;
 top: 10px;
 left: 10px;
}
The effect here is that the left edge of the paragraph will be 10 pixels away from the left edge of the browser's viewport, and the top edge of the paragraph will be 10 pixels away from the top edge of the browser's viewport.

NOTE: The offset takes into account the BOX edge of the absolutely positioned INNER element. In this case, the inner element is the paragraph. But since the paragraph has no margins, padding, and borders, then the CONTENT edge is the same as the box edge (see CSS Box Model for a review on this).

Now what happens if we change the offset properties to bottom and right.
p {
 width: 200px;
 height: 200px;
 margin: 0;
 padding: 0;
 background-color: aqua;
 position: absolute;
 bottom: 10px;
 right: 10px;
}
If we do this, then the BOTTOM edge of the paragraph will be 10 pixels away from the BOTTOM edge of the browser's viewport, while the RIGHT edge of the paragraph will be 10 pixels away from the RIGHT edge of the browser's viewport:

You can also specify an offset of 0.
p {
 width: 200px;
 height: 200px;
 margin: 0;
 padding: 0;
 background-color: aqua;
 position: absolute;
 bottom: 0;
 right: 0;
}
In this case, the bottom and right edges of the paragraph will hug the bottom and right edges of the browser's viewport:

Now let's set the offsets back to 10 pixels for both the top and the left:
p {
 width: 200px;
 height: 200px;
 margin: 0;
 padding: 0;
 background-color: aqua;
 position: absolute;
 top: 10px;
 left: 10px;
}
And then let's add a margin of 10 pixels:
p {
 width: 200px;
 height: 200px;
 margin: 10px;
 padding: 0;
 background-color: aqua;
 position: absolute;
 top: 10px;
 left: 10px;
}
This will result in a 20 pixel space in between the top edge of the viewport and the top edge of the paragraph. Same thing goes for the space in between the left edges:


Now keep in mind that the offset for each side is still 10 pixels each, and that the increase in space is simply a result of the added margins.

It's also important to note that in this example, the paragraph is completely ignoring the html and body elements in terms of how it's positioned. What I mean is that if we changed properties such as the width, height, margins, padding, and borders of the html and body elements, they will NOT affect the position of the paragraph whatsoever. Try adding margins and padding to the html and body elements, and you'll see that the paragraph's position will not change:
html {
 margin: 20px;
 padding: 30px;
} 

body {
 margin: 5px;
 padding: 40px;
}
Preview your webpage after adding these new style rules, and you'll see that the paragraph stays in the same place. Also try adding width, height, and borders. You'll see that the paragraph's position will remain unchanged.

So right now, we see that the paragraph is positioning itself based only on the viewport's edges. None of its other ancestor elements (html and body) are influencing its position whatsoever. The reason for this is because none of the paragraph's ancestors are positioned. In other words, none of them have a position property in their style rules that says absolute, relative or fixed (note that I am intentionally omitting the value of static). And if it is the case that a positioned element has no other positioned ancestors, then the positioned element positions itself based on the viewport.

NOTE: A static position is the default. It is the normal way in which elements are positioned. Saying that an element's position is static is the same as saying that an element is NOT positioned.

So what happens if a positioned element has ancestors that are also positioned? In those cases, the position of the inner element will NOT be based on the viewport. Instead, its position will be based on the positioned ancestor that is NEAREST to it (nearest in the HTML code, not nearest in terms of layout position). And we'll take a look at some examples of that in a future post.

Monday, September 14, 2015

The CSS Box Model Explained (or why are my margins all wrong?)

When you're using CSS to specify margin, padding, border, width and height values for your elements, you might run into some issues where the browser doesn't seem to be calculating the values correctly. Sometimes, there might be some actual errors in your code, but in some cases, your code is correct, but the results displayed in the browser are not quite what you expected. For example, you've specified a width of 400 pixels for your paragraph, but when you measured it, it shows a value of 420 pixels instead. Or you're expecting two elements to have a 20 pixel margin space in between them, but the browser only adds 10 pixels.

In cases like these, it's important that you have an understanding of what is called the CSS Box Model.

So what is the CSS box model? First, lets take a look at an example. Here is a very simple HTML code snippet that defines a paragraph:
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent 
pulvinar venenatis posuere. Sed eu ullamcorper enim, a eleifend urna. 
Phasellus in mollis magna, non tristique.</p>
When displayed in a browser, this paragraph will look something like this:


Now try to imagine that this paragraph is contained inside a box.


This is something that your browser will do for each element in your web page's document tree. It puts each element inside a box, and this box is used to determine how the element is laid out. The size of the box is based on the content of the element, along with any values specified for the width, height, margins, paddings, and borders. It's important to note that even if you don't put any borders, each element is always going to have its own box - it could be a box made up of just the content or a box that includes margins and paddings. But whatever the case, there will always be a box, even if you don't see it.

Now in order to make sure that these boxes are rendered correctly, there needs to be a set of rules that the browser can follow in order to be consistent. And that is why the CSS box model was created. The CSS box model provides a standard model that browsers should follow when generating these boxes that determine how the elements in your document are laid out. It provides a set of rules as to how the elements of your document will interact with each other based on each element's content width, content height, margins, paddings, and borders.

This image illustrates the basic CSS box model:


We see that the box is made up of different areas.

Inside, we have the CONTENT AREA, whose dimensions are made up of the content width and content height. The content area, as the term implies, is the space for the element's content. In paragraphs, for example, the content area is the space for the words in the paragraph. In section elements, the content area would be the space for the headings, paragraphs, and all other elements that can be placed insides sections.

Surrounding the edges of the content area would be the PADDING AREA, which is made up of the top, bottom, left, and right paddings.

Then around the padding area, we have the BORDER AREA, also consisting of top, bottom, left, and right segments.

And finally, the outermost part is the MARGIN AREA, which can also be found on all sides.

It's important to note that the CSS borders do not always show the exact size of the box. As you can see, the margins are OUTSIDE of the borders. And because margins are transparent, the box can appear smaller than it actually is. Unless you have a margin of 0 on all sides, the actual box is always bigger than the area illustrated by the CSS borders.

I'd also like to point out that the CSS specification uses the terms content BOX, padding BOX, border BOX, and margin BOX. But to avoid confusion in this article, I'll be using the word BOX to refer to the ENTIRE box, and AREA to refer to the different parts inside it - content AREA, padding AREA, border AREA, and margin AREA.

Ok, so let's take a look at an actual example. Below is a style rule for paragraphs:

p {
width: 400px;
margin-left: 15px;
margin-right: 25px;
}

So what would be the total width of the paragraph's box? Will it be 400 pixels INCLUDING the left and right margins? Or do we have to add the margin values to the existing width value? These are the types of questions that the CSS box model tries to address.

So what does the CSS box model say? According to the CSS box model, width and height refer only to content width and content height. So while the width of the content width is 400 pixels, the width of the actual BOX will be: 400 (content width) + 15 (left margin) + 25 (right margin) + any default left and right paddings that the browser may have added.

Here's another example:

p {
width: 400px;
margin-left: 15px;
margin-right: 25px;
padding: 10px;
border: 5px black solid;
}

Here, we're using shorthand declarations for the padding and the border. The padding statement means that there is a 10 pixel margin for each side. So when calculating for the width of the box, we take into account 10 pixels for the left padding and another 10 pixels for the right padding. Same thing goes for the border. The border statement applies a 5 pixel thick border for each side, so we take into account 5 pixels for the left border, and another 5 pixels for the right. So to compute for the width of the box:
  400 (content width)
+  15 (left margin)
+  25 (right margin)
+  10 (left padding)
+  10 (right padding)
+   5 (left border)
+   5 (right border)
=====
  470 pixels
So our total box width is 470 pixels.

Here is an illustration to help you visualize the box width in this example:


In some cases, you may have element content that doesn't fit inside the box.

For example:

p {
width: 200px;
height: 50px;
margin: 15px;
padding: 10px;
border: 5px black solid;
}

In this example, we are explicitly setting both a content width (200 pixels) and a content height (50 pixels). Usually, the paragraph's box will just automatically adjust its height to fit the content. But here, we're constraining it to 50 pixels. If the paragraph only has 15 or so medium-length words, then the 200 by 50 pixel content area should be enough. But if we have too much, then the content begins to flow out of the box.

Here is how it would look like with a paragraph that has 50 words:


The box height itself would be:
  50 (content height) 
+ 15 (top margin)
+ 15 (bottom margin)
+ 10 (top padding)
+ 10 (bottom padding)
+  5 (top border)
+  5 (bottom border)
====
 110 pixels (total box height)
But clearly, the content of the paragraph is too much to fit inside the box, so we see the overflow spill beyond it. To control the overflow, we can use the overflow property in CSS. By default, this has a value of visible, which means that any overflow will be visible. The other values are hidden (which will hide the overflow) and scroll (which hides the overflow, but adds a scroll bar so we can scroll down to see more).

For example:

p {
width: 200px;
height: 50px;
margin: 15px;
padding: 10px;
border: 5px black solid;
overflow: scroll;
}

Here, a scrollbar will be added for any overflow that the paragraph might contain. However, scrolling controls for overflow is probably better handled using JavaScript, because current cross-browser CSS support for scrollbars is not very good.

Collapsing Margins


In this next part of the lesson, we'll talk about collapsing margins in the CSS box model. But before I define what collapsing margins are, let's take a look at an example. Say we have 2 paragraphs, each one with a different class:
<p class="p1">Paragraph 1</p>

<p class="p2">Paragraph 2</p>
So Paragraph 1 with a class of p1 comes first, and then Paragraph 2 with a class of p2 is right below it. This means that the bottom margin of the first paragraph comes in contact with the top margin of the second paragraph.

So what happens to the margins? Let's say that in our style rule, we specify these values:

p.p1 {
margin-bottom: 50px;
padding: 0;
}

p.p2 {
margin-top: 25px;
padding: 0;
}

Note that we do not have paddings and borders, so none of those will contribute to the box size. As for the margins, the first paragraph has a bottom margin of 50 pixels, while the second paragraph has a top margin of 25 pixels. Does this mean that this will add up to a 75 pixel vertical margin space in between these 2 paragraphs? According to the CSS box model, the answer is: NO. Instead, the margins will collapse. Collapsing margins refers to the process of combining the margins of 2 different elements into one. In the CSS box model:
  • it is only the adjoining VERTICAL margins that have a possibility of collapsing, HORIZONTAL margins NEVER collapse
  • if both values are the same, then the collapsed margins will combine into a single margin of the same value (i.e. we do NOT add the values, two 10 pixel margins that collapse will combine into 10 pixels, not 20)
  • if both margins are positive and have different values, the collapsed margins will combine into a single margin that uses the size of the LARGER margin

So in this example, the vertical margin space between the 2 paragraphs will be 50 pixels, because that is the larger margin between the 2.

And in cases where negative margin values are involved:
  • if there is one negative margin, the absolute value of the negative margin is subtracted from the value of the non-negative margin
  • if there are 2 negative values, the collapsed margins combine into the lower value

Collapsing Margins of Parent and Child Elements


In HTML, many elements can contain other elements. In these instances, the containing element is said to be the parent element, and the element(s) inside it are its children. Let's take a look at this example:
<div>

<p>This is a paragraph inside a div.</p>

</div>
What we have here is a div element, which is a generic container element that can be used to group other elements together, mostly for CSS styling and layout purposes. Inside this div element is a paragraph element. So in this example, the div is the parent element, and the p is the child element.

There will often be instances when the margins of the parent and the child will come in contact with each other. And when that happens, the CSS box model provides rules on how to handle those instances.

Let's say we style the elements like this:

html, body {
padding: 0;
margin: 0;
}

div {
width: 200px;
height: 100px;
padding: 0;
margin: 15px;
background-color: blue;
}

p {
margin: 15px;
padding: 0;
background-color: aqua;
}

First, we see that I have styled the html and the body element to have margins and paddings of 0. The reason is that some browsers might add non-zero values to these properties, and they can interfere with the outcome of this example. So to avoid that, I am resetting their values back to 0.

Now let's look at the div's style. We see that the div has a content area of 200 by 100 pixels. As for the paragraph, we're NOT specifying width and height values. This means that the paragraph will just conform itself to the size of the div. We don't have to worry about any overflow since our paragraph has very few words, and we're not specifying exceedingly large margins, paddings, and borders.

Next, we see that the div has 0 padding on all sides and no borders. However, it does have a 15 pixel margin on each side. Later on, we'll see how its top margin will interact with the top margin of the paragraph inside it.

As for the paragraph, there are also no paddings and borders, but there is a 15 pixel margin on each side.

And lastly, for both elements, I've added background colors to better illustrate how the margins will interact with each other. Remember that an element's background color area includes the paddings, but not the margins (which are transparent).

Let's take a look at this image to better illustrate the box for each of the elements in this example. I'll show them both separately first:


The div is on the left, and the paragraph is on the right. The margins are denoted using the diagonal lines.

Next, let's show the illustration where the paragraph is inside the div. Here, we'll see that their top, left, and right margins come in contact with each other.



So how would the CSS box model treat cases like this? Well, we know that horizontal margins never collapse, so the left and the right margins for both elements will remain. As for both top margins, however, the rule of collapsing adjoining vertical margins will still apply. So the correct behavior would be to collapse both top margins like so:


This is how it would look like in a compliant browser:


Here's what's happening:
  1. The left edge of the div's content area (which is blue) is 15 pixels away from the left edge of the browser's display window.
  2. The left edge of the paragraph's content area (which is aqua) is 15 pixels away from the left edge of the div's content area.
  3. The right edge of the paragraph's content area is 15 pixels away from the right edge of the div's content area (numbers 1 to 3 behave this way, because horizontal margins do not collapse).
  4. The 15 pixel space in between the div's horizontal edges and the paragraph's horizontal edges shows the divs blue background colour. This is because the space is created by the paragraph's margin, and margins are transparent. Therefore, the div's background color will show through.
  5. Because the paragraph's top margin and the div's top margin are touching, they will collapse. The paragraph's top margin will merge into the div's top margin, so there will be no space in between the top edge of the paragraph's content area and the top edge of the div's content are (you will see that the top edge of the paragraph's aqua background aligns itself with the top edge of the div's blue background)
  6. The collapsed margin in number five will be 15 pixels, because both original top margins are 15 pixels. This collapsed margin will create a space in between the top edge of the div's content area and the top edge of the browser's display window.

The CSS box model is not always intuitive, and can often be quite confusing, so take a moment to analyze what you just read, and then let's move on to another example.

Now that you're ready to move on, let's give our previous example a slight modification. What happens if we add a border to the div element?

div {
width: 200px;
height 100px;
padding: 0;
margin: 15px;
background-color: blue;
border: 3px solid red;
}

Now that the div has a border, this means that there is something that separates the div's top margin and the paragraph's top margin. So these margins no longer come in contact with each other. And when vertical margins do NOT touch, then they will also NOT collapse. The thickness of the border does not matter. As long as the parent element has a border of any thickness, then its margins will not touch the margins of its child. So the outcome will now look like this:


So what changed:
  1. The div now has a border.
  2. There is a 15 pixel margin in between the div's border and the top of the browser's display window.
  3. There is now a 15 pixel making in between the top edge of the paragraph's content area, and the top edge of the div's content area.
  4. This new vertical area results in the div's blue background showing through that new 15 pixel space created by the paragraph's uncollapsed top margin.

I'd also like to point out that if you added a top padding to the div instead of a border, you will have the same effect. Adding padding to the div will also serve as a divider between the div's margins and the paragraph's margins. So in that case, the vertical margins will also NOT collapse. The same can be said even if you added a transparent border instead of a colored one.

At this point, I would like to end this article. There are a lot more complex examples that can illustrate how the CSS box model works, but as an introductory lesson, this will suffice. We may come across more examples in future lessons.