CSS Basics
Table of Contents
- Three Ways to Add CSS
- Anatomy of a Rule
- Selectors
- Combining Selectors
- Pseudo-classes
- The Cascade: Who Wins?
- Inheritance
- Style the Profile Page
- Try It Yourself
1. Three Ways to Add CSS
In chapter 1 you added CSS inside a <style> element. There are three
places CSS can live:
| Way | Looks like | When to use |
|---|---|---|
| Inline | <p style="color: red"> | Almost never — it can't be reused and is hard to override |
| Internal | <style> in the <head> | Quick experiments, single-file demos |
| External | <link rel="stylesheet" href="style.css"> | Almost always |
External stylesheets win because one file styles every page, the browser
caches it (as in Caching, CDNs, and
Speed), and your
HTML stays about structure only. Set it up now: create style.css in
the project folder, move your <style> rules into it, and link it in
the <head> of every page:
<link rel="stylesheet" href="style.css">
2. Anatomy of a Rule
p {
color: #333333;
line-height: 1.6;
}
- Selector (
p) — which elements the rule applies to. - Declaration block (
{ … }) — one or more declarations. - Declaration (
color: #333333;) — a property and its value, separated by a colon, ended with a semicolon.
Whitespace doesn't matter, but one declaration per line is the convention. A missing semicolon or colon makes the browser throw away that declaration — and sometimes the next one too. If a style isn't applying, check the punctuation, then check DevTools: invalid declarations appear struck through.
Comments in CSS use /* … */:
/* Page-wide defaults */
3. Selectors
A selector says what to style. The basic ones:
| Selector | Matches | Example |
|---|---|---|
| Type | Every element of that name | h2 { … } |
| Class | Elements with that class | .card { … } |
| ID | The one element with that id | #contact { … } |
| Universal | Everything | * { … } |
| Attribute | Elements with an attribute | a[target="_blank"] { … } |
Classes are the workhorse. In HTML you add class="…" — an element can
have several, separated by spaces — and in CSS you target it with a dot:
<article class="card featured">…</article>
.card {
border: 1px solid #d6d3d1;
padding: 16px;
}
.featured {
border-color: #b45309;
}
Prefer classes over IDs for styling. IDs are unique, so they can't be
reused, and as section 6 shows they're harder to override. Save IDs for
fragment links and form labels. Choose class names that describe what
the thing is (.card, .nav-link) and not how it looks (.red-box),
since looks change.
You can group selectors with a comma to share one rule:
h1, h2, h3 {
font-family: Georgia, serif;
}
4. Combining Selectors
Combinators target elements by their relationship in the tree:
| Selector | Name | Matches |
|---|---|---|
nav a | Descendant | Any <a> anywhere inside a <nav> |
ul > li | Child | <li>s that are direct children of <ul> |
h2 + p | Adjacent sibling | The <p> immediately after an <h2> |
h2 ~ p | General sibling | Every <p> after an <h2>, same parent |
a.button | Compound | An <a> that also has class button |
nav a {
text-decoration: none;
}
.card > h3 {
margin-top: 0;
}
Keep selectors short. main section article div p span works but is
fragile — it breaks the moment you rearrange the HTML. One class on the
element is usually better.
5. Pseudo-classes
A pseudo-class matches an element in a particular state. It's written with a single colon:
| Pseudo-class | When |
|---|---|
:hover | The pointer is over the element |
:focus | The element has keyboard focus |
:focus-visible | Focus that should be shown (mainly keyboard) |
:active | While it's being pressed |
:first-child, :last-child | Position among siblings |
:nth-child(2n) | Every even child |
:not(.x) | Elements that don't match .x |
a:hover {
color: #b45309;
}
a:focus-visible {
outline: 3px solid #1d4ed8;
outline-offset: 2px;
}
li:not(:last-child) {
margin-bottom: 8px;
}
Anything you do with :hover you should do with :focus-visible as
well, so keyboard users get the same feedback. There are also
pseudo-elements (double colon) like ::before and ::after, which
add generated content, and ::first-line.
6. The Cascade: Who Wins?
What if two rules set the same property on the same element?
p { color: black; }
.intro { color: green; }
The cascade decides. That's the "C" in CSS. The browser considers, in order:
- Origin and importance. Your styles beat the browser defaults.
(A declaration marked
!importantbeats nearly everything — avoid it.) - Specificity. A more specific selector beats a less specific one.
- Source order. If everything else is equal, the last rule wins.
Specificity is a score from the parts of a selector, compared left to right:
| Selector part | Weight |
|---|---|
Inline style="" | Highest |
IDs (#contact) | Heavy |
Classes, attributes, pseudo-classes (.card, :hover) | Medium |
Types, pseudo-elements (p, ::before) | Light |
So .intro (one class) beats p (one type) regardless of the order you
wrote them, and #contact p beats .intro.
The practical advice:
- Write flat, class-based selectors. Then specificity is rarely a problem and source order does the work.
- When a rule isn't applying, open DevTools. The Styles pane shows losing declarations crossed out, and which rule beat them.
- Resist
!important. It only works until you need to override it, then you need another one.
7. Inheritance
Some properties pass down from parent to child automatically. Set
color on <body> and every paragraph inside it gets that color unless
something overrides it:
body {
font-family: system-ui, sans-serif;
color: #292524;
}
Text properties — color, font-family, font-size, line-height,
text-align — are inherited. Box properties — margin, padding,
border, background — are not, which makes sense: a border on a
container shouldn't draw a border around every child.
Setting the base typography on body once is a core habit. It means
you'll write far less CSS.
You can control inheritance explicitly with these values:
inherit— take the parent's value.initial— reset to the property's default.unset— inherit if it's an inherited property, otherwise reset.
8. Style the Profile Page
Apply what you've learned. Add this to style.css:
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
color: #292524;
background-color: #fafaf9;
}
a {
color: #b45309;
}
a:hover {
color: #7c2d12;
}
a:focus-visible {
outline: 3px solid #1d4ed8;
outline-offset: 2px;
}
nav ul {
list-style: none;
}
nav a {
text-decoration: none;
font-weight: 600;
}
.card {
border: 1px solid #d6d3d1;
background-color: #ffffff;
}
table {
border-collapse: collapse;
}
th, td {
border: 1px solid #d6d3d1;
}
Then add class="card" to each project <article>. Spacing is missing
on purpose: the next chapter explains it.
9. Try It Yourself
- Link the stylesheet. Check that both pages use
style.css. Edit a color and watch both pages change. - Specificity battle. Add
.card h3 { color: green; }andh3 { color: red; }. Which wins? Swap the order — does it change? Check DevTools to see the struck-through rule. - Selector practice. Write one rule for each of: every
<a>inside<footer>, every<li>that is a direct child ofnav ul, and every external link (a[href^="https"]). - Hover and focus. Style
button:hoverandbutton:focus-visibleon the form. Test with the mouse and with theTabkey. - Play with inheritance. Move
colorfrombodytomain— what stops being colored? Why?
Next, the most important idea in CSS: every element is a box, and understanding that box lets you control spacing and size.
Buy Me a Coffee
Found this tutorial helpful? Consider buying me a coffee to support more free content!