HTML & CSS From Scratch, Volume 5: Finally! Classification and Box Properties!
This is the 5th post in a series on HTML and CSS. To start from the beginning, see my first post, which provides an introduction to the philosophy behind HTML & CSS and dives into the details of some basic HTML elements and display properties.
In the last post, we covered color and background properties, and we started styling a crazy kitten site in Codepin. We’re going to continue today with classification and box properties—the properties that define the shape and size of an element, as well as how it interacts with other elements.
Classification Properties
Classification properties define the core behavior of an element, such as how it interacts with other elements in the flow of your HTML document. The most important of these is the display
property.
Display Property
Every element in CSS has a display
property by default that defines how it is displayed with respect to other elements. Do you remember Volume 1 of HTML & CSS From Scratch, when I discussed content models? An element’s content model is very closely tied to its display property, but you can always reassign that property for styling purposes. For example, phrasing content almost always has the initial style of display: inline;
assigned to it. (Reminder: phrasing content tends to be text-level semantics such as <em>
, <time>
, or <strong>
.) Let’s look at each display
property in detail:
block
: block elements take up the full width of their container, by default. If you define a width for them, they will still push other items below them, unless you ‘float’ them, which we will get to, shortly.inline
: inline elements are meant to be rendered like text. As such, any white space between elements will be treated like a text space. Text can be rendered alongside inline elements, and you won’t be able to assign all of the box properties like you might when styling ablock
element. As I said above, this tends to be applied to text-level semantics as their initial value.inline-block
: this is an odd marriage of block properties and inline behavior. It can be very useful, but it also has quirks. For a while, the trend among engineers who didn’t fully understand CSS was to usedisplay: inline-block;
for nearly every layout they built. (That trend has now been replaced by usingdisplay: flex;
for everything). The quirks ofinline-block
styling can become problematic, however. Boxes withinline-block
styling get text spaces between them in layouts if there is any white space between elements. They get aline-height
that adds space below the element (because text has descenders in letters likeg
,j
, andy
). If you want to get really technical, you can dive into the details of ‘line boxes’ that effectively exist around all inline content. These aren’t formal elements that you can manipulate, though, so mostly you just need to know that inline-block content gets treated much like text and gets a line-height and font-size. There are fixes around the above issues, but you can only apply the fixes if you know what is wrong.table
,inline-table
,table-row-group
,table-header-group
,table-footer-group
,table-row
,table-column-group
,table-column
,table-cell
,table-caption
: If you couldn’t tell from this list, tables are complex! We’ll cover the nuances, pros, and cons of table layouts in a separate post (or possibly a series of posts). For now, know that each of these has its own display properties, and because tables are so complex, they are also poorly understood…and have many quirks!list-item
: block item with an added box for a marker (e.g. a bullet or number)none
: Do not render the item in the page, and do not leave space for it in the DOM. Note that this differs fromvisibility: hidden;
. Withdisplay:none;
, the element is not rendered, whereas withvisibility: hidden;
the element is loaded into the page and takes up space in your layout, but is invisible. This might be used if you’re hiding and showing a modal or a tooltip.run-in
: a newer addition with CSS3 in which an element can be inline or block, depending on context. If you want to see details, you can check out the CSS specification on run-in boxes, but we won’t go into detail here, since this style is not yet supported by most browsers.flex
: this is a really cool newer standard that was added with CSS3 to accommodate flexible layouts. It has a host of complexities, though, so we’ll cover it in detail in a separate post. If you want to explore on your own, check out the CSS specification on flexbox or this great explanation of flexbox from CSS Tricks.
Boxes in CSS
I want to note that the following concepts may seem really simple at first glance, but one of the most common mistakes that I see—even among self-proclaimed CSS ‘experts’—is that engineers do not understand box properties in HTML and CSS.
In HTML, elements are like packages. They have something inside them (content area), some amount of packing material around the content (padding) and cardboard or paper wrapping everything together (border). Lastly, if the package is something especially volatile, it might have an imposed space around it (margin).
In reality, these terms all come from the graphic design world, but unless you’re a designer (or are already experienced with web development), immediately distinguishing padding, border, and margin may be difficult without some mental models. Luckily, your browser has some great built-in tools. I typically use Chrome for debugging, so screenshots will be from there. Other browsers like Firefox, Safari, and Edge all have similar tools.
There are lots of box properties, but first I want you to understand a few key concepts. These will be important for understanding what each CSS style does:
Content box
An element’s content box is the space taken up by everything inside the element: the content.
Padding box
If we talk about the padding box, we’re talking about the space around the packaging that surrounds the content—but is still inside the element, itself.
Border box
If we talk about a border box, we are talking about the content, padding, and the border around it—the paper or cardboard at the very edge of your package. This example has no border, which is what the hyphens are indicating in developer tools. As such, when I hover the border in developer tools, there’s nothing highlighted on the page. If I inspect the button and hover the border region in developer tools, the browser highlights its 1-pixel-wide border in the viewport.
Margin
This is not part of your element. It’s the clearance space you want around your element. Margin determines how close elements can get to one another.
Box sizing
Please read carefully: this is one of the most basic and foundational concepts to layout in CSS, and at least half of “expert” front-end engineers mess it up.
How styling boxes used to work
When HTML and CSS were first born into the world, if you wanted to define the size of an element, you defined the size of the content box, and padding and border were added onto that content area. This made a lot of sense when the web was largely text with some images that the text would wrap around. In today’s world of flexible browser dimensions and complex layouts, however, it doesn’t work as well. Imagine a layout in which you want to display rows of 4 cards that fill the width of the browser. Let’s say you decide to make them each 25% width, or 23% with 1.33% margin in between. But you also need them to each have a border of 1px. Maybe some padding, too, so that text isn’t pushing up against the border. Under the old model, you would define width as the content area, and padding and border were add-ons. So, if you defined the element width as 23% and added 10px of padding and a 1px border, your elements would be too big! Thus, out of necessity, the box-sizing
property was invented.
The box-sizing Property
box-sizing
can have the following values:
content-box
: the initial value forbox-sizing
, and the original way that containers were sized. This is not typically what you want to use for modern website layouts.padding-box
: allows you to define the size of an element inclusive of its padding and content, but exclusive of its border. This means that if the element has a border, it will add to the element’s overall dimensions.border-box
: allows you to define the size of an element inclusive of its border, padding, and content. You will likely use this 99.99% of the time.
box-sizing Defaults
In my last post, I mentioned that the W3C always strives to ensure that as they update the specifications, old websites do not break. This means that box-sizing
is set to content-box
by default. Many people do not realize this, however, and because of it, they struggle to achieve even basic layouts due to padding or border adding to the size of their elements.
Because box-sizing: border-box;
allows you to size an element inclusive of padding and border, you’ll probably want to use it almost all of the time—it simplifies your life so that you don’t need to do lots of crazy math. In fact, I use it so much that I always assign it as a reset at the start of my CSS, such that I don’t have to remember to assign it on individual elements. Many UI frameworks do the same, which is why a lot of engineers never learn about the property in depth: a framework already did the heavy lifting for them, so they never had to worry about it. Ask them to write something from scratch, however, and you will inspire much head scratching!
Box Properties
We’re finally here! With the following, you will finally have some of the crucial information you need to size elements and play with layouts.
Box properties include:
Padding:
As mentioned above, padding
is the filler around your content, but it is still inside the element. The initial value is none
, but you can assign a value in several ways:
padding-top
,padding-right
,padding-bottom
,padding-left
: Each of these can receive a value in absolute or relative units. For example,padding-top: 5px;
Note that if you use percentages, both width and height are interpreted as a percent of the width of the container.padding: [top] [right] [bottom] [left];
(4 values) This is a shorthand for defining each of the padding properties above. An example of this is:padding: 10px 20px 15px 20px;
padding: [top] [right and left] [bottom];
(3 values) Looking at this example on the line above, you can see it is a bit repetitive. There’s an even shorter shorthand with 3 values when left and right padding are the same, but top and bottom are different:padding: 10px 20px 15px;
This is equivalent to:padding-top: 10px; padding-right: 20px; padding-bottom: 15px; padding-left: 20px;
padding: [top and bottom] [left and right];
(2 values) What if you have matching top and bottom padding and matching left and right padding? You just list 2 values:padding: 10px 20px;
assigns 10px of top and bottom padding and 20px of left and right padding.padding: [single value for all sides]
(1 value) Lastly, there’s a single-value shorthand for when a container has the same padding on every side:padding: 15px;
applies 15 pixels of padding to each side of an element.
Note that padding can be applied to inline elements (text semantics like <strong>
), but vertical padding will actually overlap lines of text if it is greater than the space between lines of text. Play around with this Codepen to better understand how border, padding, and margin work between block and inline elements.
Border
Borders have extra properties beyond padding:
border-top-width
+border-right-width
+border-bottom-width
+border-left-width
: a single value assigned just like padding, in absolute or relative unitsborder-width
: This, too, is assigned in the same way as padding with 4, 3, 2, or 1 value(s), depending on how different the border width is from side to side. Most of the time, people have a single value for border, or they define a border just for one side of an element as a visual divider in a list.border-width
can also accept the following keywords:thin
,medium
, andthick
. I don’t typically use these, though, because they are not mapped to a precise ruleset by the W3C, and therefore could vary with the font size or some other property, as determined by the user agent (a.k.a. the browser). As a product designer, it is my job to think through every detail of an interface, so I don’t leave visual details to chance.border-style
: (or similarly,border-top-style
…) Just likeborder-width
, this can get 4, 3, 2, or 1 value(s). The default/initial value isnone
, but options includehidden
,dotted
,dashed
,solid
,double
,groove
,ridge
,inset
,outset
(and the usual options,initial
andinherit
; see below for an explanation)border-color
: the color—using color units described in my last post—with which to fill the border, e.g.border-color: #ff0080;
If you do not define a border color, the text color (thecolor
property) will be used by default.border
: a shorthand for definingborder-width
,border-style
, andborder-color
. Just enter the properties in that order, separated by spaces. For example:border: 1px solid #ff0080;
border-radius
: used to create rounded corners,border-radius
defines the length of the radius for an imaginary circle (or ellipse!) nestled into each corner. Likeborder-width
, you can defineborder-radius
via a single value for all corners, or you can do some much more complex things, like defining an ellipse by giving two length values, or giving a different value for each corner viaborder-top-left-radius
,border-top-right-radius
,border-bottom-right-radius
,border-bottom-right-radius
, or a shorthand using justborder-radius
. For full details, see the CSS3 specification on border-radius. It has great explanations and diagrams, like the ones to the left. Note thatborder-radius
was added with CSS3 well after theborder
shorthand was created, and it is not something that you can specify via the same shorthand property asborder-width
,border-style
, andborder-color
.border-image
: This was a really cool addition to CSS with CSS3, in which you can divide an image into nine pieces (4 corners, 4 sides, and the middle), and automatically position the 4 corners, repeat the 4 side segments along the width and height of an element, and ignore the center section of your image.border-image
is decently well-supported, but quite complex to explain. I could write a post just on this. Thankfully, the CSS3 specification does a great job documenting border images, too, so check that out for implementation details.
Border has the same limitations as padding in inline elements, so use it carefully. Again, this Codepen plays with border, padding, and margin across inline and block elements.
An Aside About ‘initial’ and ‘inherit’
In the early days of the web, there was no way to assign something back to its initial value except to manually name that value. For example, most elements have border-style: none;
styling by default, but if I create a new style rule that all <h1>
tags get border-style: solid;
I have to remember the initial value of none
to reset to it for a special case. CSS3 came out with an update to simplify this: the initial
value, which resets to browser defaults. The catch? initial
is not supported on any version of Microsoft Internet Explorer (though it is supported on MS Edge). As of April 2018, we still aren’t quite to the point at which you can use this value and trust that all of your users will see the right thing.
The inherit
value was added with CSS3, for similar reasons. Not all properties are inherited from parent element to child element, by default, but sometimes you want that behavior, for example, if different high-level components have styling that should be applied to child components. The inherit
property allows you to do that, and thankfully, it was adopted with IE8, so inherit
is safe to use for most websites!
Margin
Margin is the space you require outside the element’s boundaries. It is declared just like padding: margin: 10px;
is equivalent to
margin-top: 10px;
margin-right: 10px;
margin-bottom: 10px;
margin-left: 10px;
Likewise, the 2-value, 3-value, and 4-value shorthands all apply, as well. Margin is a bit trickier than padding, though, because margins for multiple elements will collapse to use the greater value, if they differ—but only under the right circumstances.
When does margin collapse?
We say ‘collapse,’ but perhaps a better term would be overlap. I say this because that’s effectively what happens: when margins collapse, they effectively overlap, and if the overlapping margins differ, the larger one will determine the space between two elements. So when do they collapse? In the following circumstances:
- 2 elements are stacked. The first element has a margin-bottom applied, and the second element has a margin-top applied. The margins will overlap, and whichever is greater between the two margins will determine the space between them.
- 2 elements are displayed side-by-side. The left one has margin-right applied, and the right one has margin-left applied. The margins will overlap, and the larger margin (if they differ) will determine the space between elements
- An outer element and its child (or further descendent) both have margins applied to them and there is no padding or border declared between them. This last detail is crucial—if there is a border or padding on the outer element, the inner element’s margin will be contained within the outer element, and it will not collapse.
When won’t margins collapse?
- As I said above, inner and outer elements’ margins will not collapse if the outer element (or other ancestor of the inner element) has a border or padding applied to it
Why collapse margins at all?
The mentality around collapsing margins is this: when we create broad margin styles for something like a <p>
tag, we typically mean something like, “a paragraph should have 10 pixels of space above and below it so that the text isn’t squished up against another paragraph or an image.” We do not typically mean, “a paragraph should have 10 pixels of space above and below it in addition to the space around other objects.” As such, 2 paragraphs next to each other have margins of 10px that overlap. If this weren’t the behavior, you would need to either assign only a top or bottom margin and then do a special case for whenever a paragraph is against something different, like an image or a <blockquote>
.
Centering Block Elements with Margin
Remember from above, when I said that block elements take up the full width of their containers, even if you’ve applied a width style to them? Well, if you’ve styled a box to be smaller than its container, you can center it horizontally with margin, by assigning left and right margin to auto
. This does not work for vertical centering, alas! Since the document flow goes from the top, down margin was never given such powers. You’ll often see people centering images with the style margin: 0 auto;
, which means to apply zero top/bottom margin and center the element horizontally (by applying auto
to left/right margins).
Note: setting the margin to auto on inline and inline-block elements. These are meant to fit in the flow of text, so ‘auto’ margin means nothing to them.
Margin and Inline Elements
Horizontal margin (except the auto
centering trick) works on inline elements, but vertical margin is completely ignored. This is because inline elements should be displayed in a line of text, and that vertical alignment trumps other properties. Check out the codepen for a demo of margin, border, and padding.
More Box Properties
To be continued, next time! Stay tuned for Volume 6, next week, where we’ll cover additional properties that you can use to style boxes.