π¨ Lesson 11: Colors & Typography
You've connected a stylesheet and written your first CSS rules. Now it's time to go deeper into the two things that have the biggest impact on how a page feels: color and type. The right palette and font choices can make a plain page look professional β or playful, or elegant, or bold.
π― Learning Objectives
By the end of this lesson, you will be able to:
- Use named colors, hex codes, RGB, and HSL to set any color in CSS
- Apply color to text, backgrounds, and borders
- Build a font stack with fallback fonts
- Add Google Fonts to your website
- Control font size, weight, style, and line height
- Use
remandemunits for scalable typography - Store and reuse colors with CSS custom properties (variables)
- Create a cohesive color and type system for your project
Estimated Time: 45 minutes
Hands-on: You'll build a color palette and typography system for your website.
In This Lesson
CSS Color Formats
CSS gives you several ways to express a color. They all produce the same result β it's just a matter of which notation you prefer and what you need.
Named Colors
CSS recognizes 148 named colors β simple English words like red, navy, tomato, cornflowerblue, and papayawhip:
h1 {
color: navy;
background-color: papayawhip;
}
Named colors are great for quick prototyping and learning, but they're limited β you can't express every shade. For precise control, you need one of the formats below.
Hex Codes
The most common color format on the web. A hex code starts with # followed by six characters (0β9 and AβF), representing the red, green, and blue channels:
h1 {
color: #1a365d; /* dark blue */
background-color: #f0f4f8; /* light gray-blue */
border-color: #3b82f6; /* bright blue */
}
The format is #RRGGBB β two hex digits per channel. Each channel ranges from 00 (none) to FF (maximum). Some common patterns:
#000000β black (all channels off)#ffffffβ white (all channels full)#ff0000β pure red#00ff00β pure green#0000ffβ pure blue
If both digits in each pair are the same, you can use a shorthand: #aabbcc can be written as #abc. So #ff6600 becomes #f60.
RGB
The rgb() function uses decimal numbers (0β255) for red, green, and blue:
h1 {
color: rgb(26, 54, 93); /* same as #1a365d */
background-color: rgb(240, 244, 248); /* same as #f0f4f8 */
}
RGB and hex express the same thing in different notation. Use whichever you find more readable.
RGBA (with Transparency)
Add an alpha channel (0 to 1) to make a color semi-transparent:
.overlay {
background-color: rgba(0, 0, 0, 0.5); /* 50% transparent black */
}
.glass-panel {
background-color: rgba(255, 255, 255, 0.8); /* 80% opaque white */
}
An alpha of 0 is fully transparent; 1 is fully opaque.
Modern CSS also lets you add transparency with plain rgb() β separate the numbers with spaces and put the alpha after a slash. It means the same thing, and you'll see both styles in the wild:
.overlay {
background-color: rgb(0 0 0 / 50%); /* same as rgba(0, 0, 0, 0.5) */
}
HSL (Hue, Saturation, Lightness)
HSL is the most human-friendly format. Instead of mixing red/green/blue, you think in terms of:
- Hue β the color itself, as a degree on a color wheel (0Β°=red, 120Β°=green, 240Β°=blue)
- Saturation β how vivid the color is (0%=gray, 100%=full color)
- Lightness β how light or dark (0%=black, 50%=normal, 100%=white)
h1 {
color: hsl(215, 56%, 23%); /* dark blue */
}
.accent {
color: hsl(215, 91%, 60%); /* bright blue β same hue, more saturated, lighter */
}
HSL shines when building color palettes β you can keep the same hue and just adjust saturation and lightness to create harmonious variations.
navy, tomato
Easy but limited"] A --> C["Hex
#1a365d
Most common"] A --> D["RGB/RGBA
rgb(26, 54, 93)
Decimal + transparency"] A --> E["HSL/HSLA
hsl(215, 56%, 23%)
Most intuitive"] style A fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style B fill:#fffbeb,stroke:#f59e0b,stroke-width:1px,color:#1e293b style C fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#1e293b style D fill:#fdf4ff,stroke:#a855f7,stroke-width:1px,color:#1e293b style E fill:#fef2f2,stroke:#ef4444,stroke-width:1px,color:#1e293b
π‘ Which Format Should I Use?
Hex is the industry standard β you'll see it everywhere. HSL is best when you're designing a palette and want to create lighter/darker variations of the same color. RGBA/HSLA whenever you need transparency. Named colors for quick experiments. Pick one format for your project and stay consistent.
Applying Color to Elements
CSS has several properties that accept color values. Here are the most common ones:
color β Text Color
p {
color: #333333; /* dark gray text */
}
.error {
color: #dc2626; /* red text for error messages */
}
background-color β Background
body {
background-color: #f8fafc; /* light background for the whole page */
}
.warning-box {
background-color: #fef3c7; /* soft yellow background */
padding: 15px;
}
border-color β Borders
/* You can set border-color separately... */
.card {
border: 2px solid;
border-color: #e2e8f0;
}
/* ...or all at once in the border shorthand */
.card-accent {
border-left: 4px solid #3b82f6;
}
Putting It Together
/* A styled card using multiple color properties */
.info-card {
color: #1e293b;
background-color: #eff6ff;
border: 1px solid #bfdbfe;
border-left: 4px solid #3b82f6;
padding: 20px;
}
π‘ Contrast Matters
Always make sure your text color has enough contrast against its background. Light gray text on a white background might look sleek, but it's hard to read β especially for people with low vision. The Web Content Accessibility Guidelines (WCAG) recommend a contrast ratio of at least 4.5:1 for normal text. Free tools like WebAIM's Contrast Checker let you test your combinations instantly.
Font Families and Font Stacks
The font-family property controls which typeface the browser uses for text. But here's the catch: the font has to be available on the user's device. If you specify a font they don't have, the browser falls back to something else.
That's why we use font stacks β a comma-separated list of fonts, in order of preference:
body {
font-family: "Segoe UI", Roboto, Arial, sans-serif;
}
The browser tries each font in order: first "Segoe UI" (common on Windows), then Roboto (common on Android), then Arial (nearly universal), and finally any sans-serif font as a last resort.
Generic Font Families
Every font stack should end with a generic family β a keyword that tells the browser "use any font of this type." These are the ones you'll use most:
| Generic | Style | Common Examples | Best For |
|---|---|---|---|
serif |
Has small strokes (serifs) at letter ends | Times New Roman, Georgia | Articles, formal content |
sans-serif |
Clean, no serifs | Arial, Helvetica, Segoe UI | UI, headings, modern sites |
system-ui |
The operating system's own interface font | San Francisco, Segoe UI, Roboto | Apps, dashboards, native-feeling sites |
monospace |
Every character is the same width | Courier New, Consolas | Code, technical content |
cursive |
Handwriting-like | Comic Sans MS, Brush Script | Decorative, sparingly |
fantasy |
Decorative, stylized | Impact, Papyrus | Decorative, very sparingly |
Common Professional Font Stacks
/* Clean modern sans-serif */
body {
font-family: system-ui, "Segoe UI", Roboto, sans-serif;
}
/* Elegant serif for articles */
.article-body {
font-family: Georgia, "Times New Roman", Times, serif;
}
/* Code blocks */
code, pre {
font-family: "Fira Code", Consolas, "Courier New", monospace;
}
π‘ System Font Stack
The stack system-ui, "Segoe UI", Roboto, sans-serif is called the system font stack. The system-ui keyword uses each operating system's native UI font β San Francisco on Mac/iOS, Segoe UI on Windows, Roboto on Android. This makes your site feel native on every platform, and the fonts load instantly because they're already on the device. In older code you'll often see the same idea spelled out as -apple-system, BlinkMacSystemFont, "Segoe UI", β¦ β that was the workaround before system-ui was widely supported.
Adding Google Fonts
System fonts are fast and reliable, but sometimes you want a specific typeface. Google Fonts is a free library of over 1,500 web fonts that anyone can use.
Step 1: Choose a Font
- Go to fonts.google.com
- Browse or search for a font (try "Inter", "Poppins", or "Lora")
- Click the font name to view it
- Click "Get font" β "Get embed code"
- Copy the
<link>tags provided
Step 2: Add the Link to Your HTML
Paste the link tags into your <head>, before your own stylesheet:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Website</title>
<!-- Google Fonts β load BEFORE your CSS -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap"
rel="stylesheet">
<!-- Your stylesheet -->
<link rel="stylesheet" href="css/style.css">
</head>
Step 3: Use It in Your CSS
body {
font-family: "Inter", system-ui, sans-serif;
}
Notice we still include fallback fonts after "Inter" β if Google Fonts fails to load (slow connection, firewall, etc.), the browser gracefully falls back to system fonts instead of showing a blank.
fonts.google.com"] B --> C["2. Copy β¨linkβ© tags
into HTML β¨headβ©"] C --> D["3. Use font-family
in your CSS"] D --> E["4. Always include
fallback fonts"] style A fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style B fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#1e293b style C fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#1e293b style D fill:#f0fdf4,stroke:#22c55e,stroke-width:1px,color:#1e293b style E fill:#fdf4ff,stroke:#a855f7,stroke-width:1px,color:#1e293b
π‘ Font Performance Tip
Every Google Font you add is an extra download. Stick to one or two fonts max β one for headings, one for body text. Loading five different fonts will slow your page down. Also, only request the weights you actually use (like 400 and 700) instead of loading every weight.
Font Sizing: px, em, and rem
The font-size property controls how big text appears. CSS offers several units, but three are essential:
px β Pixels
An absolute unit. 16px means 16 pixels, period.
h1 { font-size: 36px; }
p { font-size: 16px; }
Pixels are predictable and easy to understand β great for learning. But they don't scale when users change their browser's default font size (an accessibility concern).
rem β Root em (Recommended)
A relative unit based on the root element's (<html>) font size. By default, browsers set the root font size to 16px, so:
1rem= 16px1.5rem= 24px2rem= 32px0.875rem= 14px
h1 { font-size: 2.25rem; } /* 36px at default root */
h2 { font-size: 1.75rem; } /* 28px */
p { font-size: 1rem; } /* 16px */
small { font-size: 0.875rem; } /* 14px */
Why rem is better: If a user sets their browser's base font size to 20px (for readability), all your rem-based text scales proportionally. With px, it stays stubbornly at 36px.
em β Relative to Parent
Like rem, but relative to the parent element's font size, not the root. This can cause compounding:
.parent { font-size: 20px; }
.child { font-size: 1.5em; } /* 30px (1.5 Γ 20) */
.grandchild { font-size: 1.5em; } /* 45px (1.5 Γ 30) β it compounds! */
Because of compounding, em is best reserved for local adjustments (padding, margins relative to text size) rather than font sizes. Use rem for font sizes.
| Unit | Relative To | Scales with User Settings? | Best For |
|---|---|---|---|
px |
Nothing (absolute) | No | Learning, borders, shadows |
rem |
Root element font size | Yes β | Font sizes, spacing |
em |
Parent element font size | Yes (but compounds) | Local padding/margins |
Other Text Properties
Beyond font family and size, CSS gives you fine-grained control over how text looks:
font-weight β Boldness
p { font-weight: 400; } /* normal β same as "normal" */
strong { font-weight: 700; } /* bold β same as "bold" */
h1 { font-weight: 600; } /* semi-bold (if the font supports it) */
Weight values range from 100 (thin) to 900 (black). The most common are 400 (normal) and 700 (bold). Use named values (normal, bold) or numbers β they mean the same thing.
font-style β Italic
.caption { font-style: italic; }
.correction { font-style: normal; } /* remove italic from em tags, for example */
line-height β Vertical Spacing
Controls the space between lines of text. This is one of the biggest readability improvements you can make:
body {
line-height: 1.6; /* 1.6 times the font size β great for body text */
}
h1 {
line-height: 1.2; /* tighter for large headings */
}
A unitless number (like 1.6) is preferred over a fixed value β it scales with the font size automatically. The default is about 1.2, which is cramped for body text. Bump it to 1.5β1.8 for comfortable reading.
text-align β Horizontal Alignment
h1 { text-align: center; }
p { text-align: left; } /* the default is start β left in left-to-right languages */
.price { text-align: right; }
.justify { text-align: justify; } /* stretches to fill the line β use sparingly */
text-decoration β Underlines and More
a { text-decoration: none; } /* remove default underline */
a:hover { text-decoration: underline; } /* add it back on hover */
del { text-decoration: line-through; } /* strikethrough */
text-transform β Case Changes
.uppercase { text-transform: uppercase; } /* ALL CAPS */
.capitalize { text-transform: capitalize; } /* First Letter Capitalized */
.lowercase { text-transform: lowercase; } /* all lowercase */
letter-spacing and word-spacing
.spaced-heading {
letter-spacing: 2px; /* space between characters */
text-transform: uppercase; /* often paired with uppercase text */
}
.wide-text {
word-spacing: 4px; /* space between words */
}
π‘ A Professional Typography Recipe
Here's a quick formula that instantly improves almost any page:
body {
font-family: "Inter", system-ui, sans-serif;
font-size: 1rem; /* 16px base */
line-height: 1.6; /* comfortable reading */
color: #1e293b; /* near-black, not pure black */
}
h1, h2, h3 {
line-height: 1.2; /* tighter for headings */
color: #0f172a; /* slightly darker than body text */
}
h1 { font-size: 2.25rem; } /* 36px */
h2 { font-size: 1.75rem; } /* 28px */
h3 { font-size: 1.375rem; } /* 22px */
Why #1e293b instead of pure #000000? Pure black on white creates harsh contrast that causes eye strain. A very dark gray is easier to read and looks more sophisticated.
CSS Variables (Custom Properties)
Once you've picked a palette, you'll use the same few colors over and over β the same blue for headings, links, and borders. Typing #1a365d in ten places makes changes painful. Custom properties (usually called CSS variables) let you name a value once and reuse it everywhere.
Declaring a Variable
A custom property's name always starts with two dashes. Declare your variables inside the :root selector β it targets the <html> element, so every element on the page can use them:
:root {
--color-primary: #1a365d;
--color-accent: #3b82f6;
}
Using a Variable
Wherever you'd normally type the value, write var() with the variable's name instead:
h1 {
color: var(--color-primary);
border-bottom: 3px solid var(--color-accent);
}
a {
color: var(--color-accent);
}
Now if you change --color-accent in :root, the heading border and every link update at once.
π‘ Naming Tips
Name variables by their role, not their appearance: --color-accent still makes sense if you switch from blue to green, but --blue doesn't. Names are case-sensitive, so --Color-Accent and --color-accent are two different variables β stick to lowercase with dashes.
Hands-on Exercise
ποΈ Exercise: Build a Color & Type System
Objective: Create a cohesive color palette and typography system in your CSS file.
Instructions:
- Open your
css/style.cssfile - At the top of your file, add CSS custom properties (variables) for your color palette:
:root { --color-primary: #1a365d; --color-accent: #3b82f6; --color-text: #1e293b; --color-text-light: #64748b; --color-bg: #f8fafc; --color-bg-alt: #eff6ff; --color-border: #e2e8f0; } - Replace all hard-coded color values in your CSS with these variables:
body { color: var(--color-text); background-color: var(--color-bg); } h1 { color: var(--color-primary); border-bottom: 3px solid var(--color-accent); } a { color: var(--color-accent); } - Add a Google Font (try "Inter" or "Poppins") and update your
font-family - Switch all
font-sizevalues frompxtorem - Set
line-height: 1.6on the body - Make headings use
line-height: 1.2 - Test by changing just the
--color-accentvariable β every accent color on the page should update at once
π‘ Hint
CSS custom properties (also called CSS variables) are declared inside a :root selector and used with var(--name). The :root selector targets the <html> element, making variables available everywhere. See CSS Variables (Custom Properties) above for a refresher.
β Example Solution
/* css/style.css */
/* ===========================
Color Palette (CSS Variables)
=========================== */
:root {
--color-primary: #1a365d;
--color-accent: #3b82f6;
--color-accent-hover: #1d4ed8;
--color-text: #1e293b;
--color-text-light: #64748b;
--color-bg: #f8fafc;
--color-bg-alt: #eff6ff;
--color-border: #e2e8f0;
--color-success: #22c55e;
--color-warning: #f59e0b;
--color-error: #dc2626;
}
/* ===========================
Typography
=========================== */
body {
font-family: "Inter", system-ui, "Segoe UI", Roboto, sans-serif;
font-size: 1rem;
line-height: 1.6;
color: var(--color-text);
background-color: var(--color-bg);
margin: 0;
padding: 20px;
}
h1, h2, h3, h4 {
line-height: 1.2;
color: var(--color-primary);
}
h1 {
font-size: 2.25rem;
border-bottom: 3px solid var(--color-accent);
padding-bottom: 0.5rem;
}
h2 {
font-size: 1.75rem;
margin-top: 2rem;
}
h3 {
font-size: 1.375rem;
}
/* ===========================
Links
=========================== */
a {
color: var(--color-accent);
text-decoration: none;
}
a:hover {
color: var(--color-accent-hover);
text-decoration: underline;
}
/* ===========================
Utility Classes
=========================== */
.text-small {
font-size: 0.875rem;
color: var(--color-text-light);
}
.text-center {
text-align: center;
}
.highlight {
background-color: #fef08a;
padding: 2px 6px;
border-radius: 3px;
}
/* ===========================
Semantic Sections
=========================== */
header {
background-color: var(--color-primary);
color: white;
padding: 1.25rem;
margin: -20px -20px 1.25rem -20px;
}
header a {
color: #93c5fd;
}
footer {
margin-top: 2.5rem;
padding-top: 1.25rem;
border-top: 1px solid var(--color-border);
color: var(--color-text-light);
font-size: 0.875rem;
}
aside {
background-color: var(--color-bg-alt);
padding: 1rem;
border-left: 4px solid var(--color-accent);
margin: 1.25rem 0;
}
/* ===========================
Code
=========================== */
code {
font-family: "Fira Code", Consolas, "Courier New", monospace;
background-color: var(--color-bg-alt);
padding: 2px 6px;
border-radius: 3px;
font-size: 0.9em;
}
π― Quick Quiz
Question 1: What does the hex code #ff0000 represent?
Question 2: Which unit is best for font sizes?
Question 3: Why do we use a font stack instead of a single font name?
Question 4: What's a good line-height for body text?
Question 5: In HSL, what does the "H" represent?
π Learning Journal
Take five minutes to add to your learning journal. Write down:
- Key concepts you learned
- What clicked β the idea or technique that made sense
- Questions or confusion, so you know what to revisit
- Ideas to try on your own site
- How you feel about your progress
βοΈ This lesson's prompt: Which colors and fonts did you pick, and why? What mood do they give your site? Did you check the text contrast? Note one choice you'd like to revisit later.
Summary
π Key Takeaways
- CSS colors come in four formats: named colors, hex (
#1a365d), RGB/RGBA (rgb(26, 54, 93)), and HSL/HSLA (hsl(215, 56%, 23%)) - Apply color with
color(text),background-color(backgrounds), andborder-color(borders) - Use font stacks with fallbacks:
font-family: "Inter", system-ui, sans-serif; - Google Fonts gives you free access to 1,500+ typefaces β add via
<link>in<head> - Use
remfor font sizes (scales with user settings) and a unitlessline-heightof 1.5β1.8 for body text - CSS custom properties (
--color-accent: #3b82f6;) let you define a palette once and reuse it everywhere - Use near-black text (
#1e293b) instead of pure black for better readability
π Your Project So Far
my-website/
βββ css/
β βββ style.css β now with colors, fonts, and variables
βββ images/
β βββ sunset.jpg
βββ index.html β linked to Google Fonts + CSS
βββ about.html
βββ recipe.html
βββ contact.html
π What's Next?
Your pages have beautiful colors and professional typography β but everything is just stacking top to bottom. Text bumps right up against the edges, images ignore their surroundings, and nothing has breathing room. In the next lesson, you'll learn the CSS Box Model β the foundation of how every element takes up space on the page. Understanding margin, padding, border, and content will give you precise control over spacing and layout.