Chapter 03 of 09Part 1 · The page
The cascade and the CSS box model
How CSS decides which rule wins, and how big every box really is once padding, border and margin are added.
11 minBy Zuzo StudioUpdated
In this chapter you will
- work out which of two competing CSS rules wins, and why
- calculate the rendered width of a box with and without border-box
- give your project page its first stylesheet
Before you startThis builds on Semantic HTML: structure that means something.
HTML says what each part of a page is. CSS (Cascading Style Sheets) says how it should look: colours, type, spacing, size. Two ideas explain almost every CSS surprise a beginner runs into. The cascade decides which rule wins when several rules style the same element. The CSS box model decides how big each element really is, once padding, borders and margins are added to it. This chapter covers both, with two small tools you can play with, and ends with your project page getting its first stylesheet.
01How a CSS rule works
A stylesheet is a list of rules. Each rule has a selector, which picks the elements, and a set of declarations, each a property and a value:
h1 {
font-size: 2.5rem;
color: #1f2a37;
}
This says: every <h1>, set the text size to 2.5rem and the colour to a dark blue grey. A rem is a unit relative to the root font size, which is 16px in most browsers unless the visitor has changed it, so 2.5rem is usually 40px and grows if someone sets a larger default text size.
You can select elements in a few ways. The three you will use most:
| Selector | Example | Picks |
|---|---|---|
| Type | p |
Every <p> element |
| Class | .logo |
Every element with class="logo" |
| ID | #contact |
The one element with id="contact" |
Selectors can be combined. nav a means any link inside a <nav>. .services li means list items inside something with the class services.
Some properties are inherited. Set color or font-family on the <body> and every element inside it takes that value unless something overrides it. Others are not: a border on a section does not appear on every paragraph inside it. As a rule of thumb, text properties inherit and box properties do not.
02The cascade: which rule wins
Several rules often try to set the same property on the same element. The cascade is the algorithm that settles it. MDN's introduction to the CSS cascade lists the steps in order. Simplified for a beginner, they come down to three questions, asked one after another until there is a winner.
1. Where does the rule come from, and is it important?
Every browser ships with a user agent stylesheet: its own defaults that make headings large and links blue. Your stylesheet, the author stylesheet, beats those defaults. That is why your page looks like a 1995 document before you add CSS: you are seeing the browser's styles.
A declaration marked !important jumps ahead of normal ones. Treat it as an emergency exit, not a tool. Once one !important is in a stylesheet, the only way to beat it is another one, and the styles get harder to change with each round.
Newer CSS also has cascade layers (@layer), which let you group rules so a whole group outranks another regardless of selectors. They are useful on larger sites and design systems. You can ignore them for now.
2. How specific is the selector?
When rules come from the same place, the more specific selector wins. MDN's page on specificity describes it as a weight with three columns, compared from left to right:
- IDs. Each
#idadds 1 to the first column. - Classes. Each
.class, attribute selector such as[type="email"]and pseudo-class such as:hoveradds 1 to the second. - Elements. Each type selector such as
pand pseudo-element such as::beforeadds 1 to the third.
So #contact a weighs (1, 0, 1) and .footer nav a weighs (0, 1, 2). The first wins, because one ID beats any number of classes. The universal selector * adds nothing. Inline styles, written in a style attribute on the element, beat every selector.
Type two selectors into the tool below to see their weights side by side and which one wins.
Specificity is counted as IDs, then classes, attributes and pseudo-classes, then elements. The selector with the higher count in the first column that differs wins.
3. Which comes last?
If origin and specificity are equal, the declaration that appears later in the stylesheet wins. This is why the order of your CSS file matters, and why a rule you just added at the bottom sometimes "works" for reasons you did not intend.
03The CSS box model
Every element the browser draws is a rectangular box. The CSS box model describes the four layers of that box, from the inside out:
- Content. The text or image itself, sized by
widthandheight. - Padding. Space inside the box, between the content and the border. It takes the element's background colour.
- Border. A line around the padding.
- Margin. Space outside the border that pushes other boxes away. It is always transparent.
.card {
width: 300px;
padding: 24px;
border: 2px solid #1f2a37;
margin: 16px;
}
How wide is that card on screen? Not 300px. By default, width sets only the content. The padding and border are added on top: 300 + 24 + 24 + 2 + 2 makes 352px, and the margin adds another 16px of space on each side outside that. MDN's guide to the box model works through the same sum.
Drag the sliders below to change each layer and watch the total width. Then switch box-sizing and see what changes.
A box is its content plus padding, border and margin. With box-sizing: border-box, the width you set includes the padding and border.
box-sizing: border-box
That default is called content-box. The alternative, border-box, makes width include the padding and border, so a box set to 300px is 300px wide on screen and the content area shrinks to fit. MDN's box-sizing reference has the details.
Most sites switch every element to border-box with a few lines at the top of the stylesheet, because it makes sizes behave the way designers expect:
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
Block and inline boxes
Not every box behaves the same way. Block boxes, such as <p>, <h2> and <section>, start on a new line and stretch to fill the width available. Inline boxes, such as <a>, <strong> and <span>, sit inside a line of text. Width and height do not apply to inline boxes, and their vertical margins do not push other lines away. If you set a width on a link and nothing happens, this is why. The display property changes the type, and in the next chapter it is how we turn on flexbox and grid.
When margins collapse
One more surprise. When the bottom margin of one block meets the top margin of the next, they do not add up. MDN explains that two positive margins collapse into one, the size of the larger. A heading with a 32px bottom margin followed by a paragraph with a 16px top margin leaves a 32px gap, not 48px.
04Keeping CSS manageable as a site grows
A one page site can live in one small stylesheet. A few habits keep it that way as it grows:
- Name colours and sizes once. CSS custom properties, written
--name, hold a value you can reuse everywhere withvar(--name). Change the brand colour in one place and every rule that uses it follows. - Style by class. A class like
.cardsays what something is in your design and can be reused. Styling by ID ties a rule to one element and raises its specificity. - Write general rules first. Base styles for elements at the top, then components, then the rare exception.
:root {
--ink: #1f2a37;
--paper: #faf7f2;
--space: 1.5rem;
}
body { color: var(--ink); background: var(--paper); }
These named values are the seed of design tokens. On a site with many pages and several people building them, they grow into a shared system, which we compare to a plain brand document in design system vs style guide.
*, *::before, *::after { box-sizing: border-box; }
:root { --ink: #1f2a37; --paper: #faf7f2; --space: 1.5rem; }
body {
margin: 0;
padding: var(--space);
font-family: system-ui, sans-serif;
line-height: 1.5;
color: var(--ink);
background: var(--paper);
}
h1 { font-size: 2.5rem; line-height: 1.1; margin: 0 0 var(--space); }
h2 { margin: 0 0 0.5rem; }
section { padding-block: var(--space); border-top: 1px solid var(--ink); }
a { color: inherit; }
Check your understanding
3 questions
Key takeaways
- The cascade settles conflicts by origin and importance first, then specificity, then order of appearance.
- Specificity is compared as IDs, then classes, then elements, so one ID beats any number of classes.
- Every box is content, padding, border and margin, and by default width sets only the content.
- box-sizing: border-box makes width include padding and border, which is why most sites set it on every element.
- Low, even specificity and named custom properties keep a stylesheet easy to change.
Sources
- Introduction to the CSS cascadeMDN Web Docs, checked
- SpecificityMDN Web Docs, checked
- The box modelMDN Web Docs, checked
- box-sizing CSS propertyMDN Web Docs, checked