It’s not too late to learn the box-model! An introduction to CSS for engineers
Nulab currently has three front-end engineers, including me. Usually, we write the CSS, but sometimes our engineers also wind up writing some when adding or correcting details.
The problem is that engineers tend to be not-so-good at CSS. Usually they are self-taught and confused about how to use it. So, we held an in-office seminar to teach CSS basics to our engineers. I’d like to share one of the most popular topics from that seminar: the CSS box-model.
Box-model is one of the most basic CSS concepts. If you don’t understand it, you will not be able to realize the layout you envision and want to express.
The Generated Box
Every element of HTML generates a square area called a box. There are two main types of boxes: block and inline. Some of you must have heard these terms before, right? They are the values for the CSS display property.
The default value for the display property (and thus the box-type) of any HTML element is set by the browser’s default style sheet. These can be changed by setting the display property to a different value in your style sheet.
Now let’s look at the characteristics of these boxes.
When its “display” value is “block” (or “table”), an element will generate a block-type box. Elements like h1 through h6, p, div, ul, ol, li, and table generate block boxes by default.
Regardless of their width, block boxes will be laid out vertically, from top to bottom. It may be easier to understand if you imagine that there is a line break before and after a block box, as with a paragraph, so each block has its own vertical space.
Other important characteristics of a block box:
- It can have a specific width and height. If a specific width is not set, it will spread to fill the whole width of parent element.
- It can have margins and padding, on any side – up, down, left, and right.
When its “display” value is “inline” (or “inline-table”), an element will generate an inline-type box. Elements like span, a, em, strong, and img generate inline boxes by default.
Inline boxes are laid out horizontally, from left to right. There is no “line break” before and after them, they are laid out one after another on the same line.
Other important characteristics of an inline box:
- It cannot have a specific width or height. *1
- It cannot have specific margins on the top or bottom.
*1 There are some inline elements that can have specific width and height, for example: img, input, and textarea. These elements are called replaced elements. They have HTML attributes (width, height, size, col etc) that can be used to specify width and height via CSS.
Supplementary: The Inline-block box
A third type of box is the “inline-block.” It is laid out horizontally like an inline box, but can accept specific width, height, margins, and padding like a block box. These boxes are very useful for laying out horizontal navigations, and similar interfaces.
The main values for the display property, and the type of box they generate are as follows:
|Display Property||Box Type|
|list-item||A block box for the content and an inline box for the item marker|
|table, table-row, table-cell, etc.||A box equivalent to the box for the corresponding HTML element|
|none||no box is generated|
Since HTML5, CSS should define how things look, and HTML should define the document structure (“semantics”). So, for example, if you want to make a list with bullet points, it is best to use the HTML element that suits this semantic (ul and li), rather than using a non-semantic div with the display property set to “list-item.”
To lay things out with CSS, you need to understand the box-model, which has four properties, as shown in the diagram, below.
Content Dimensions (Height & Width)
The content itself is the text, images and other things inside an element. Using the height and width properties, you can specify the dimensions of the area that displays the content.
This is the space between the content and its border. With the padding property you can increase or decrease the size of that space.
This is the box’s demarcation line, outside the padding but inside the margin. It is like a “frame.” You can specify the thickness of this frame using the border-width property.
This is the outermost property of the box: the space between it’s border and other boxes. You can specify the size of this space using the margin property.
Look at the example below. Why does the child element (.box__inner) stick out from its parent element (.box) even though its width is set to 100%? (Please check the HTML and CSS by switching the tabs.)
This is very easy for those who know CSS well, but beginners often make mistakes here.
The box size is calculated as the sum total of ‘content + padding + border + margin’. As explained earlier, the width and height properties specify the size of the content area; in the above example the width of “.box” is set to 500px. For“.box__inner,” the width is set to “100%” of its parent’s width (500px). Its padding is set to 10px, and its border is set to 3px. So, the total width of “.box__inner” becomes 500 + 20 (10px padding on each side) + 6 (3px border on each side), 526px.
This is why it is bigger than its parent, despite us having set its width to be 100% of its parent! How can we prevent this? There are 2 approaches:
Method 1: Calculate
With the above understanding, we can calculate the correct width and specify it exactly. If we subtract the padding, border and margin we can specify the correct content area width. In the above example, it would be 500 (the width of the parent content area) – 20 (the total padding width) – 6 (the total border width): so the width of “box__inner” should be “474px” instead of “100%.”
width: 474px; /* 500px - 20px - 6px */
border: 3px solid #ff8410;
You could do it this way, we used to, but it is troublesome. To avoid this trouble, you could use CSS processors like SASS, or calc(), a CSS function that does the calculations for us.
width: calc(100% - 20px - 6px);
border: 3px solid #ff8410;
The calc() function can be used in all major modern browsers now, without the need for a vendor prefix, in most cases. See:
Method 2: Use Box-Sizing
You can use the “box-sizing” property to specify which area will be affected by the dimensions set in the width and height properties.
Valid values for box-sizing are:
- content-box: Width and height specify the dimensions of the content area. This is the default setting.
- padding-box: Width and height include the padding area with the content area.
- border-box: Width and height include the border and the padding area with the content area.
Thus, in the above example you can prevent the child element from sticking out of the parent element just by using “box-sizing: border-box.” There is no need to calculate width.
border: 3px solid #ff8410;
With “box-sizing: border-box”, CSS layout becomes much easier. If you use a pre-written CSS library like sanitize.css, “box-sizing: border-box” will be applied to the root element, cascading to every child element so there’s no need to apply it individually.
Lately, CSS frameworks like Bootstrap and others are making it a norm to apply “box-sizing: border-box” to all elements. It works across most browsers (see: http://caniuse.com/#feat=css3-boxsizing), so I recommend it over taking the trouble to calculate the width each time
As I explained earlier, “block” boxes can have vertical margins. If these margins are adjacent to one another, however, they do not accumulate as you might expect. When your margins are not expanding the way you expect, most of the time this is the problem.
Margin cancellation may seem complicated and hard to use, but there are cases where you can use it effectively to streamline your CSS, so let’s understand it correctly.
When Does it Happen?
Margin cancellation affects the top or bottom margin of boxes that are next to each other, vertically – but it only happens in certain cases:
Vertically Adjacent Boxes
When boxes are vertically adjacent (and on the same level), only the bigger margin will be applied. Look at the example below. Box 1 has a 15px “margin-bottom,” and box 2 below it has a 30px “margin-top.” You may think the margin should become the sum of these two, 45px, but in fact the smaller margin of Box 1 is cancelled, and only the bigger margin of .box2 is applied – resulting in a margin of 30px.
If elements are nested within one another (without intervening content or space) the largest of all the specified margins will be applied. It is difficult to understand only from words, so please look at the example below.
The parent element has a “margin-top” of 30px, but the actual margin applied is 60px, because the child element has a “margin-top” of 60px. The smaller “margin-top” of the parent has been cancelled by the larger “margin-top” of the child. However, if the parent element had a border or padding, or if it contained text or other content, this cancellation would not occur. Please see the example below. It is safe to remember that margin cancellation will happen as a result of nesting only when the parent and child element are immediately adjacent.
There are other cases where margin cancellation will not happen. I listed some below.
In general, margins will not be cancelled…
- For horizontal (left and right) margins
- For “Inline-block” boxes
- For boxes that are set to“float”
Nested boxes will not cancel their margins…
- When the parent has padding or border
- When the parent’s “position” is “absolute,” or “fixed”
- When the parent’s “overflow” is set as “hidden” or “scroll”
For now, I will skip the detailed explanation of each.
Advantages of Margin Cancellation
Some people may think, ‘Why does this cancellation happen?’ It’s just an obstacle. It’s difficult and has no merit.’ Sure, it is a little difficult when margins cancel unexpectedly, until you get used to it. There are counter-measures to avoid unexpected cancellations. For example, you could decide to only use top, or only bottom margins. (Dropbox specifies this in their style guide. They only use margin-bottom, to avoid unintentional margin cancellations) Or, you could use padding instead of margin, since padding does not cancel.
However, as I said earlier, there are cases where the code will be more complicated if you do not make use margin cancellation. Blog posts, for example, have different HTML in every post. If you wanted to make proper margins for every element in every article, it would be difficult to do that with single-direction margin or padding. Margin cancellation would be very effective here. It avoids irregularities, and is very effective when you incorporate design into web applications, so I especially hope that engineers will learn to use margin cancellation to their advantage.
When to use Margin or Padding
We’ve seen that margin and padding, though they can be a bit troublesome, are important to use correctly. They have different uses, which I will summarize now. This may be totally obvious to some, but I hope you will be able to use it as a good reference.
- When you want space outside the element’s border / frame
- When you want transparent space, without the element’s background color or image
- When you want to use “auto” to define that space (which is very useful for things like centering content)
- When you want adjacent spaces to cancel instead of accumulate.
- When you want negative space (margins that have negative value)
- When you want space inside the element’s border / frame
- When you want the space to include the element’s background color or image
- When you want adjacent spaces to accumulate, not cancel
Did you like the article?
What I wrote here is very basic, but for many people it is surprisingly difficult to remember clearly. I hope it will help you advance to a clearer understanding of CSS.
We are hiring in New York, Tokyo, Kyoto and Fukuoka now! For more details