Adding CSS and the cascade
There are three ways to attach CSS to a page. Two of them are almost always wrong, and knowing why is how the cascade starts making sense.
The three ways
<!-- 1. External stylesheet -->
<link rel="stylesheet" href="styles.css">
<!-- 2. Internal, in the head -->
<style>
h1 { color: #1a4d2e; }
</style>
<!-- 3. Inline, on the element -->
<h1 style="color: #1a4d2e;">Sharma Kirana</h1>
Use the external stylesheet. It is cached, so the second page a visitor opens does not download it again. It is one place to change a colour rather than forty. And it keeps your HTML about structure.
<style> in the head is defensible for a genuinely single-page thing, or for a
few critical rules you want to arrive with the HTML.
Inline style is the one to avoid, and not for tidiness. It cannot be reused,
cannot be overridden except by !important, cannot hold a :hover or a media
query, and it makes an element's appearance invisible to a search of your CSS.
There is one legitimate use — a value computed at runtime, like a progress bar's
width — and this course does not have one.
A stylesheet is plain text
/* styles.css */
h1 {
color: #1a4d2e;
font-size: 2rem;
}
That block is a rule. h1 is the selector, and everything in the braces is
a declaration block made of property: value; declarations.
The semicolon after the last declaration is optional and you should write it anyway. Leave it off, add a line below, and you have silently broken both.
Comments are /* … */ only. There is no // in CSS — write one and the browser
discards that declaration, or sometimes the rest of the rule, with no warning:
h1 {
// color: red; <-- this breaks things
font-size: 2rem;
}
CSS never errors
This is the most important sentence in the module.
h1 {
colour: green; /* British spelling. Not a property */
font-size: 2remm; /* typo in the unit */
backgorund: yellow; /* typo in the property */
color: notacolour; /* not a colour */
}
Nothing in the console. No red text. The browser reads each declaration, does not recognise it, throws that one line away, and carries on. Your heading is simply the colour it was before.
So when a style does not apply, the first question is never "what is wrong with my CSS logic" — it is "did the browser accept this line at all?" Devtools shows you: an unaccepted declaration appears struck through with a warning triangle. The devtools lesson later in this module is mostly about this.
And note colour — the property is color, American spelling, always. The rest
of this course uses British spelling in prose, and CSS does not care what the
prose does.
The cascade
When two rules set the same property on the same element, the browser needs a winner. It asks these questions, in order, and stops at the first that decides:
- Origin and importance — where the rule came from, and whether it is
!important. - Specificity — how precisely the selector targets the element.
- Source order — the last one wins.
Most day-to-day CSS is decided by 3, some by 2, and almost none by 1. But 1 beats
2 which beats 3, and getting that order wrong is why people reach for
!important.
Source order
h1 { color: green; }
h1 { color: red; } /* wins — it is later */
Same selector, same specificity, so the later rule wins. This is why the order of
your <link> tags matters, and why your own stylesheet must come after any
framework's:
<link rel="stylesheet" href="bootstrap.css">
<link rel="stylesheet" href="styles.css"> <!-- yours, so it can override -->
Swap those and you will spend an afternoon wondering why your styles do nothing.
Origin
There are three origins, and they are consulted in this order — later beats earlier:
- The browser's own stylesheet. Every browser ships one. It is why an
unstyled
h1is large and bold and a link is blue and underlined. The next lesson is entirely about this. - The author's — yours.
- The user's — a stylesheet or setting the visitor has applied themselves.
So yours beats the browser's, which is why one line of your CSS removes a bullet.
!important
p { color: red !important; }
p#intro.lead { color: green; } /* loses, despite far higher specificity */
!important jumps out of the specificity contest entirely. It is in the
importance step, which is asked first.
The order, fully, from weakest to strongest:
browser normal < author normal < author !important < user !important
Note the last one: a user's !important beats yours. That is deliberate. A
visitor who has forced a larger font because they cannot read small text must win
against a designer, and this is the mechanism.
Do not use !important. When you feel the need, one of three things is true:
your selector is less specific than the one you are fighting, you are fighting an
inline style, or your CSS is disorganised. Fix that instead. An !important is
answered by another !important, and two rules later nobody can predict what any
selector does.
The honest exception: overriding a third-party widget's inline styles, where you have no other lever.
@layer, the modern answer
The cascade has a newer step, and it sits in the origin/importance question — so it beats specificity entirely:
@layer reset, base, components, utilities;
@layer components {
.card { padding: 1rem; }
}
@layer utilities {
.p-0 { padding: 0; } /* wins over .card, regardless of specificity */
}
You declare the order once, and a later layer always beats an earlier one no
matter how specific the earlier selector was. That is the thing !important was
being misused for, done properly.
Anything outside a layer beats everything inside one, which is the opposite of what most people guess. You will use this properly in the best-practices module; for now, know it exists, because it changes how you would organise a real stylesheet.
Where to put the link
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sharma Kirana</title>
<link rel="stylesheet" href="styles.css">
</head>
In the head, as established in module 1: the stylesheet is render-blocking, and the browser deliberately waits for it rather than painting unstyled content and then jumping. Put it at the end of the body instead and you get that flash.
href="styles.css" is a path like any other, so all of the relative-path rules
from module 1 apply. A stylesheet that appears to do nothing is very often a 404 —
check the Network tab before checking your selectors.
Check your work
Which of the three to use. External, because it is cached and it is one place to change.
Why inline style is worse than untidy. No reuse, no :hover, no media
queries, and only !important can override it.
What is in a rule. A selector, then a declaration block of
property: value; declarations.
Why there is no //. CSS comments are /* … */; // silently discards the
declaration.
What happens on a typo. Nothing visible. The browser throws that one declaration away and carries on.
The first question when a style does not apply. Did the browser accept the line at all — check for the strike-through in devtools.
The cascade's three questions, in order. Origin and importance, then specificity, then source order.
Why your stylesheet goes after a framework's. Source order — the later rule wins on a tie.
Why one line of yours beats the browser's bullet. Author origin beats browser origin.
Who beats your !important. A user's !important, deliberately, so somebody
who needs a bigger font wins.
What @layer replaces. The legitimate need !important was being misused
for.
What to check first when a stylesheet seems to do nothing. The Network tab, for a 404.
Practice
- Style an
h1three ways — inline, internal and external — at once. Work out which wins before you look, then look. - Try to write a
:hoveras an inline style. - Write
// a commentinside a rule and see what stops working. - Misspell a property, a value and a unit in one rule. Confirm the console says nothing, then find all three struck through in devtools.
- Write
colour: redand thencolor: red. Note which applies. - Write two identical selectors with different colours. Swap their order.
- Link two stylesheets that both style
h1. Swap the<link>order. - Break the
hrefof your stylesheet and find the 404 in the Network tab. - Use
!importantto win a fight, then win the same fight by making your selector more specific instead. - Set a font size in your browser's own accessibility settings and see whether your page respects it.
- Declare
@layer base, utilities;and make a low-specificity utility class beat a high-specificity rule. - Put a declaration outside any layer and confirm it beats the layered ones.
Official documentation
- MDN — Introducing the CSS cascade — Origin, importance and layers, in the specification's own order.
- MDN — @layer — The syntax, and how unlayered rules relate to layered ones.
- W3C — CSS Cascading and Inheritance Level 5 — The specification, for when a tutorial and reality disagree.
Next: the styles your page already has before you write any.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship