Colan Biemer
<h1>Colan's Coffee Blog</h1> <p>Here is some text about the purpose of my made-up blog.</p> <h2>Visited</h2> <ul> <li>Recreo - Dynamite</li> <li>Nero - at least it looks nice.</li> <li>Starbucks - it's fine.</li> </ul>
CSS stands for Cascading Style Sheets.
<p style="color: red;">This paragraph is red.</p>
This paragraph is red.
The style attribute puts CSS directly on one element. This is inline CSS.
style
Write two paragraphs. Make the first one blue and the second one red.
Paragraph 1
Paragraph 2
<p style="color: blue;">Paragraph 1</p> <p style="color: red;">Paragraph 2</p>
Add a third paragraph. Make them red, green, and blue, in that order.
Paragraph 3
<p style="color: red;">Paragraph 1</p> <p style="color: green;">Paragraph 2</p> <p style="color: blue;">Paragraph 3</p>
CSS currently defines 148 named colors: red, blue, green, rebeccapurple, antiquewhite, burlywood, darkgoldenrod, ... MDN lists them all.
We aren't limited to just those, though.
rgb()
<p style="color: rgb(201, 123, 58);">Paragraph 1</p>
rgb(red, green, blue): how much of each, from 0 (none) to 255 (full).
rgb(red, green, blue)
0
255
You will also see hex (#c97b3a) and hsl(...). Same idea, but we won't be covering these in any depth.
#c97b3a
hsl(...)
<p style="color: rgb(255, 0, 0);">Paragraph 1</p> <p style="color: rgb(0, 255, 0);">Paragraph 2</p> <p style="color: rgb(0, 0, 255);">Paragraph 3</p> <p style="color: rgb(0, 0, 0);">Paragraph 4</p> <p style="color: rgb(128, 128, 128);">Paragraph 5</p> <p style="color: rgb(255, 255, 255);">Paragraph 6</p>
Paragraph 1: rgb(255, 0, 0)
Paragraph 2: rgb(0, 255, 0)
Paragraph 3: rgb(0, 0, 255)
Paragraph 4: rgb(0, 0, 0)
Paragraph 5: rgb(128, 128, 128)
Paragraph 6: rgb(255, 255, 255)
red
Make all three paragraphs red with rgb().
<p style="color: rgb(255, 0, 0);">Paragraph 1</p> <p style="color: rgb(255, 0, 0);">Paragraph 2</p> <p style="color: rgb(255, 0, 0);">Paragraph 3</p>
What if we had 40 paragraphs, and we decided we wanted them to be red? We would have to do each paragraph individually:
<p style="color: rgb(255, 0, 0);">Paragraph 1</p> <p style="color: rgb(255, 0, 0);">Paragraph 2</p> <p style="color: rgb(255, 0, 0);">Paragraph 3</p> ... <p style="color: rgb(255, 0, 0);">Paragraph 40</p>
If we ever want to change the color, we have a ton of work ahead of us. (Find and replace all is a great tool but can get messy.)
Inline CSS works for one-off tweaks (e.g. this one link should be a different color). To style a whole group of elements at once, we need a way to select a group.
<style>
A <style> element in the <head> holds rules for the whole page. This is internal CSS.
<head>
<style> [SELECTOR] { [property]: [VALUE]; } </style>
[SELECTOR] { [property]: [VALUE]; }
The selector picks which elements the rule applies to. Any tag name works: p, div, h1, li, a, ...
p
div
h1
li
a
What internal-CSS will make all paragraphs red?
p { color: red; }
<head> <style> p { color: red; } </style> </head> <body> <p style="color: blue;">Paragraph 1</p> <p>Paragraph 2</p> </body>
What will the color of paragraph 1 be? What about paragraph 2?
Inline-CSS will overwrite internal CSS. The only exception is when you use !important.
!important
<head> <style> p { color: red !important; } </style> </head> <body> <p style="color: blue;">Paragraph 1</p> <p>Paragraph 2</p> </body>
Now, what will the output look like?
!important overrides inline-CSS.
How do I make text in every <div> purple?
<div>
div { color: purple; }
How can I make all the text in the whole document blue?
body { color: blue; }
All the text is inside the <body> element. Every element inside <body> inherits the font color.
<body>
How can I make all the font red, except <div> font, which will be purple?
body { color: red; } div { color: purple; }
<body> says red, so any font inside it will be red, unless a tag inside <body> (the <div> in this case) has a color rule of its own.
color
<body> <ul> <li>Recreo</li> <li>Nero</li> </ul> </body>
<li>
<ul>
<html>
Generally, text- and color-related properties inherit (color, font-size, line-height) and box properties do not (border, margin, width). When in doubt, look it up: every MDN property page has an Inherited: yes/no row.
font-size
line-height
border
margin
width
body { color: red; }
<body> <p>Read the <a href="reviews.html">reviews</a>.</p> </body>
<a> is a child of <p>, which is a child of <body>. What color is the word "reviews"?
<a>
<p>
Read the reviews.
Blue. The paragraph inherits red from <body>, but the link does not: a rule matches <a> directly, so inheritance is skipped.
We never wrote a rule for <a>. The browser did.
Every browser ships with its own stylesheet (the user agent stylesheet). It gave the HTML-only page on the first slide its big heading and bullets. A simplified piece of it:
h1 { font-size: 2em; } a { color: blue; text-decoration: underline; } ul { list-style: disc; padding-left: 40px; }
a { color: red; }
<i>
font-family
font-family takes a list. The browser uses the first font it has installed from the list. End the list with a generic family, which every browser has:
serif
sans-serif
monospace
i { font-size: 150%; font-family: Georgia, serif; }
Example italicized text.
background-color
a { background-color: yellow; }
Documentation on MDN is good.
opacity
1
img { opacity: 0.5; }
opacity: 1
opacity: 0.5
You have seen color, font-size, font-family, background-color, and opacity, but there are hundreds more. Few memorize them, but you don't have to. Instead, focus on learning how to use CSS (selector { property: value; }) and then look things up as you go. The MDN CSS reference is an excellent resource for this.
selector { property: value; }
How do we give every paragraph a padding of 100px, but a few of them a padding of 50px?
padding
100px
50px
px
<head> <style> p { padding: 100px; } </style> </head> <body> <p>Paragraph 1</p> <p style="padding: 50px;">Paragraph 2</p> <p>Paragraph 3</p> </body>
This works. Inline beats internal, so paragraph 2 gets 50px.
<p>Paragraph 1</p> <p style="padding: 50px;">Paragraph 2</p> <p>Paragraph 3</p> <p style="padding: 50px;">Paragraph 4</p> ... <p style="padding: 50px;">Paragraph 40</p>
Say that ten of the paragraphs have the 50px styling, but we decide 50px should really be 40px. Then we have to make ten updates, and if we change our mind again, we have to make ten more. There needs to be a better way.
40px
p { padding: 100px; } .compact { padding: 50px; }
<p>Paragraph 1</p> <p class="compact">Paragraph 2</p> <p>Paragraph 3</p>
Name the special paragraphs with the class attribute; .compact (with the dot) selects them. Change 50px to 40px once and all ten follow.
class
.compact
.[CLASS_NAME] { ... }
<span>
I want to make classes for <span> ("bold" and "italicized") that let me make inline text bold and/or italicized. What is the CSS for this?
"bold"
"italicized"
font-weight
normal
bold
100
900
font-style
italic
.bold { font-weight: bold; } .italicized { font-style: italic; }
Why is this not right? "I want to make classes for <span> ("bold" and "italicized") that let me make inline text bold and/or italicized." I want a class that only activates in a <span>.
/* This is a CSS comment */ span.bold { font-weight: bold; } span.italicized { font-style: italic; }
span.bold
span .bold
<p> Recreo is <span class="bold">dynamite</span>, Nero is <span class="italicized">fine</span>, Starbucks is <span class="bold italicized">everywhere</span>. </p>
Recreo is dynamite, Nero is fine, Starbucks is everywhere.
Two classes (<span class="bold italicized">) on one element get both rules.
<span class="bold italicized">
p.bold { font-weight: bold; }
<p class="bold">One</p> <p><span class="bold">Two</span></p>
Which word is bold?
One
Two
No space means and: p.bold is a <p> that also has class bold. "Two" has the class, but on a <span>, so it stays plain.
p.bold
p .underline { text-decoration: underline; }
<p class="underline">One</p> <p><span class="underline">Two</span></p> <p class="underline"><span>Three</span></p>
Which words are underlined?
Three
A space means inside: p .underline is anything with class underline inside a <p>. "One" and "Three" put the class on the <p> itself, and nothing inside them has it, so both stay plain.
p .underline
underline
blackAndYellow
span { background-color: black; color: yellow; } a { background-color: black; color: yellow; } .blackAndYellow { background-color: black; color: yellow; }
This works, but if we ever make a change, we have to change the other two.
span, a, .blackAndYellow { background-color: black; color: yellow; }
<p>This is a <span>span</span> and this is a <a href="#">link</a>.</p> <p class="blackAndYellow">This paragraph has class blackAndYellow.</p>
This is a span and this is a link.
This paragraph has class blackAndYellow.
A comma makes a list: the rule applies to every <span>, every <a>, and everything with class blackAndYellow. So far: no space means and, a space means inside, a comma means each of these.
div, p { background-color: blue; }
<div>This is a div.</div> <p>This is a paragraph.</p> <span>This is a span.</span>
This is a paragraph.
Imagine we have a blog and we want to allow users to jump to the header:
<a [WHAT GOES HERE?]>Visited</a> <!--Code in Between --> <h2 [WHAT GOES HERE?]>Visited</h2>
<a href="#visited">Visited</a> <!--Code in Between --> <h2 id="visited">Visited</h2>
<a> (anchor) lets us navigate to elements in our HTML by id.
id
<h2 id="visited">Visited</h2> <p id="intro">Here is some text about the purpose of my made-up blog.</p> <div id="footer">Copyright 2026</div>
<p id="visited">I have visited a lot of coffee shops.</p> <div id="visited">Visited</div>
Is this valid HTML?
No. An id must be unique: one element per name on a page. Two elements named visited would leave href="#visited" with nowhere definite to go.
visited
href="#visited"
The browser will not stop you (it quietly picks the first one), but the HTML is invalid. This is the difference from class: a class can go on as many elements as you like; an ID goes on exactly one.
#[ID_NAME] { ... }
#visited { color: green; }
<h2 id="visited">Visited</h2>
. selects by class; # selects by ID.
.
#
<p> Read the <a href="reviews.html">reviews</a> or my <a href="about.html">about page</a>. </p>
a { color: green; }
Read the reviews or my about page.
a { color: green; } #about { text-decoration: none; }
<p> Read the <a href="reviews.html">reviews</a> or my <a id="about" href="about.html">about page</a>. </p>
.about { text-decoration: none; }
<p> Read the <a href="reviews.html">reviews</a> or my <a class="about" href="about.html">about page</a>. </p>
Same result as #about. Only the selector and the attribute changed.
#about
span a { text-decoration: none; }
<p> Read the <a href="reviews.html">reviews</a> or my <span><a href="about.html">about page.</a></span> </p>
/* id */ #about { text-decoration: none; } /* class */ .about { text-decoration: none; } /* trick */ span a { text-decoration: none; }
The worst is the "trick." It wouldn't make sense to others reading the HTML. Given the name "about", I'd say id makes the most sense. But a renamed class like "link-no-underline" would be reusable.
.favorite
#header
.section { color: gray; background-color: yellow; } #visited { color: green; }
<a href="#visited">Jump to visited</a>
What does this link look like? What happens when it is clicked?
Neither rule matches: the link has no class and no id, so it keeps the default look. Clicking it scrolls to the element with id="visited". If no element has that id, the address bar still changes to #visited, but the page does not move and no error appears.
id="visited"
#visited
<h2 class="section" id="wishlist">Wishlist</h2>
What does this heading look like?
Matches .section: gray text on yellow. #visited does not match, because this id is wishlist.
.section
wishlist
<h2 class="section" id="visited">Visited</h2>
What does this heading look like? It shares class="section" with line 2: is that valid?
class="section"
Matches both. The ID wins on color, so the text is green. The yellow is from .section. Id visited is unique. (This is where the link would navigate to.)
More than one rule can match the same element. When they disagree, CSS picks one:
ID
span a
.about
The most specific selector wins: ID > class > tag. If two rules are equally specific, the one further down in the CSS wins.
p { font-size: 16px; } p { font-size: 48px; }
<p>Will I have a font size of 16 pixels or 48 pixels?</p>
Will I have a font size of 16 pixels or 48 pixels?
48px. Both selectors are the same, so neither is more specific. The tie goes to the rule further down in the CSS.
48px
p { color: black; } .note { color: blue; } #warning { color: red; }
<p class="note" id="warning">What color am I?</p>
All three rules match this paragraph. What color will it be?
(Hint: ID > class > tag)
What color am I?
Red. The most specific selector wins: ID > class > tag. If two rules are equally specific, the one further down in the CSS wins.
p { text-align: center; /* left, center, right */ text-transform: uppercase; /* uppercase, lowercase, capitalize */ letter-spacing: 2px; /* space between letters */ line-height: 2; /* space between lines (x font size) */ }
Here is some text about the purpose of my made-up blog. It runs onto a second line so the line height shows.
<h2>Visited</h2>
Center the heading and make it all caps.
text-align
left
center
right
text-transform
uppercase
lowercase
capitalize
h2 { text-align: center; text-transform: uppercase; }
<h2>Visited</h2> <p> Here is some text about the purpose of my made-up blog. I drink a lot of coffee, I have opinions about where to drink it, and I wanted somewhere to write those opinions down. That is the whole plan. </p>
Put 2px of space between the heading's letters, and make the paragraph's lines twice as tall as normal.
2px
letter-spacing
1.6
h2 { letter-spacing: 2px; } p { line-height: 2; }
Here is some text about the purpose of my made-up blog. I drink a lot of coffee, I have opinions about where to drink it, and I wanted somewhere to write those opinions down. That is the whole plan.
So far, selectors pick elements by what they are: a tag, a class, an id. A pseudo-class picks elements by what state they are in:
:hover
:visited
:active
There are many more.
[SELECTOR]:[STATE] { [property]: [VALUE]; }
a:hover { color: orange; }
a:hover
a { color: green; text-decoration: none; }
<a href="index.html">Home</a> <p> Read the <a href="reviews.html">reviews</a> or my <a href="about.html">about page</a>. </p>
While the mouse is over a link inside a paragraph, make its text 48px. The Home link is not in a paragraph and should not change on hover.
p a:hover { font-size: 48px; }
Space means inside, colon means state: a link inside a <p> that the mouse is over. Home is not inside a <p>, so it never matches.
<p>Read my <a href="about.html">about page</a>.</p> <ul class="reviews"> <li><a href="recreo.html">Recreo</a></li> <li><a href="nero.html">Nero</a></li> </ul>
Browsers turn a link purple once you have visited it. Our a rule makes every link green, visited or not. In the reviews list only, make links the user has already visited gray, so they can see which reviews they have read.
.reviews a:visited { color: gray; }
Read my about page.
Pretend the user has already read the about page and the Recreo review. Both links are visited, but only Recreo is inside .reviews, so only Recreo turns gray.
.reviews
NOTE: :visited can only change colors. Browsers block anything else for privacy.
While the mouse button is held down on the about link, and only that one, make it red and uppercase.
.about:active { color: red; text-transform: uppercase; }
No space: class about and being clicked. Press and hold on it above; :active matches from mouse down to mouse up.
about
a { color: green; text-decoration: none; } a:hover { color: orange; } a:visited { color: gray; }
<a href="https://www.google.com">Example</a>
a:visited
A pseudo-class (:hover) selects a whole element by its state. A pseudo-element selects part of an element: its first letter, its first line, the text the user has highlighted.
[SELECTOR]::[PART] { [property]: [VALUE]; }
p::first-letter
::first-line
::selection
::before
::after
<p> Here is some text about the purpose of my made-up blog. I drink a lot of coffee, I have opinions about where to drink it, and I wanted somewhere to write those opinions down. That is the whole plan. </p>
Make the first letter of every paragraph 48px, like the oversized letter that starts a book chapter. (Hint: ::first-letter font-size)
::first-letter
p::first-letter { font-size: 48px; }
Make the first line of every paragraph uppercase. (Hint: ::first-line text-transform)
p::first-line { text-transform: uppercase; }
Which words are on the first line depends on the width of the page. Make the window narrower and the uppercase part follows.
<a href="about.html" target="_blank">about page</a> <img src="cup.png" alt="A cup of coffee" /> <p class="compact" id="intro">Here is some text.</p>
name="value"
href
target
src
alt
An attribute selector works for any attribute: alt, href, target, ...
[SELECTOR][[ATTRIBUTE]] { [property]: [VALUE]; }
a[href] { text-transform: capitalize; }
<a href="reviews.html">reviews</a> <a>coming soon</a>
[href] asks one question: does this element have an href attribute? The value does not matter. The first link does, so it matches; the second has no href, so it is left alone.
[href]
<img src="cup.png" alt="A cup of coffee" /> <img src="beans.png" />
Make any image without alt text have 20% opacity.
20%
img { opacity: 0.2; } img[alt] { opacity: 1; }
img:not([alt]) { opacity: 0.2; }
Same result in one rule. :not() is a pseudo-class that flips whatever is inside the parentheses: img:not([alt]) is an image that does not have an alt attribute.
:not()
img:not([alt])
[SELECTOR][[ATTRIBUTE="VALUE"]] { [property]: [VALUE]; }
a[href="index.html"] { font-weight: bold; }
Add ="VALUE" inside the brackets to require an exact value: a link and its href is exactly index.html. Quotes around the value.
="VALUE"
index.html
<p> Read the <a href="reviews.html">reviews</a> or <a href="https://www.google.com" target="_blank">search</a>. </p>
target="_blank" in <a> will cause the link to open in a new page. Say that we want to spread out the letters (5px) of every link that opens in a new tab. What is the CSS to do this?
target="_blank"
5px
a[target="_blank"] { letter-spacing: 5px; }
Read the reviews or search.
[attr]
img[alt]
[attr="v"]
v
a[target="_blank"]
[attr^="v"]
a[href^="https"]
[attr$="v"]
a[href$=".pdf"]
[attr*="v"]
a[href*="google"]
[attr~="v"]
p[class~="compact"]
[attr|="v"]
v-
p[lang|="en"]
CSS gives you many more ways to select attributes: MDN.
<head> <style> body { ... } h1 { ... } h2 { ... } p { ... } ul { ... } li { ... } a { ... } /* 300 more lines */ </style> </head>
The <head> is now mostly CSS, and a second page needs all of it copied again.
Can I separate my HTML from my CSS?
style.css
<head> <link rel="stylesheet" href="style.css" /> </head>
Yes. Put the rules in their own file and <link> it from the <head>. This is external CSS. One file styles every page that links to it.
<link>
<link rel="stylesheet" href="style.css" />
Inline beats the other two (unless !important). Between internal and external, the usual rules apply: more specific wins, and on a tie, whichever comes later in the <head>.
p a
span, a