Module 5

CSS Fundamentals

Selectors • Specificity • Box Model • Units • Colors

🎨 Making the Web Beautiful: CSS

You have spent four modules building web pages with HTML. Every heading, paragraph, list, form, table, and video is in place. But everything still looks like a plain document from the 1990s — black text on a white background, Times New Roman, no spacing, no color. That is because HTML only describes what content is. It says nothing about how it should look.

CSS — Cascading Style Sheets — is the language that controls appearance. Colors, fonts, spacing, layout, animations — every visual detail you see on any modern website is CSS. In this module, you learn the core mechanics that power it all.

💡
The House Analogy: HTML is the structure of a house — walls, doors, windows, rooms. CSS is the interior design — paint color, flooring, furniture placement, lighting. The structure stays the same, but the appearance changes completely depending on which stylesheet you apply.

🎯 By the end of this module, you will:

  • Link a CSS stylesheet to an HTML page
  • Write selectors that target any element on the page
  • Understand specificity and predict which rule wins
  • Use the box model to control spacing and sizing
  • Choose the right unit for every situation (px, rem, %, vw)
  • Define colors in hex, RGB, and HSL — and know when to use each
  • Create reusable design tokens with CSS custom properties

⚙️ How CSS Works

A CSS rule tells the browser: "Find these elements, then apply these styles." Every rule has two parts: a selector (what to style) and a declaration block (how to style it).

Anatomy of a CSS Rule

CSS Rule Structure

1 SelectorTargets which HTML elements to style (h1, .card, #hero).
2 PropertyThe visual characteristic to change (color, font-size, margin).
3 ValueThe specific setting to apply (blue, 24px, 2rem).
CSS
Try it →
/* selector   { property: value; } */

h1 {
  color: navy;
  font-size: 2rem;
}

Three Ways to Add CSS

There are three places you can write CSS. Each has its place, but one is clearly the best for real projects.

Where to Write CSS

1 InlineThe style attribute directly on an element. Quick but unmaintainable. Avoid in production.
2 InternalA <style> block inside <head>. Good for single-page demos but styles can't be shared across pages.
3 External ✅A separate .css file linked with <link>. The standard approach. One file styles an entire site.

1 — Inline Styles

Write CSS directly in the HTML element's style attribute. This has the highest specificity (it overrides almost everything) and is impossible to reuse. Use it only for quick tests or JavaScript-driven dynamic values.

HTML
Try it →
<p style="color: red; font-weight: bold;">This text is red and bold.</p>

2 — Internal Stylesheet

Place a <style> element inside <head>. The styles apply only to that page. Good for single-page experiments, but the CSS is not reusable across other pages.

HTML
Try it →
<head>
  <style>
    h1 {
      color: navy;
      font-size: 2rem;
    }
    p {
      line-height: 1.6;
    }
  </style>
</head>

3 — External Stylesheet ✅

Create a separate .css file and link it in <head> with the <link> element. This is the standard approach for every real website. One CSS file can style hundreds of HTML pages. The browser also caches it, so returning visitors load it instantly.

HTML
Try it →
<!-- In your HTML file -->
<head>
  <link rel="stylesheet" href="style.css">
</head>
CSS
Try it →
/* style.css — your separate CSS file */

h1 {
  color: navy;
  font-size: 2rem;
}

p {
  line-height: 1.6;
  color: #333;
}
💡
Always use external stylesheets. Inline and internal styles mix presentation with structure, making maintenance harder. External files give you separation of concerns, caching, and reusability. The only exceptions: email HTML (which does not support external CSS) and quick prototyping.

🎯 Selectors

A selector is the targeting system of CSS. It tells the browser which elements to style. Master selectors, and you can reach any element on any page without changing the HTML.

Basic Selectors

The Four Foundation Selectors

* Universal *Selects every element. Used mainly in resets.
E Type h1 p divSelects every element of that tag name.
. Class .card .activeSelects elements with that class attribute. Reusable — many elements can share a class.
# ID #hero #navSelects the one element with that id. Must be unique per page. High specificity — prefer classes instead.
CSS
Try it →
/* Universal — resets margin on everything */
* {
  margin: 0;
  padding: 0;
}

/* Type — styles every <p> on the page */
p {
  line-height: 1.6;
}

/* Class — styles any element with class="card" */
.card {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 1rem;
}

/* ID — styles the one element with id="hero" */
#hero {
  background: #f0f4ff;
}
⚠️
Prefer classes over IDs. IDs have very high specificity, making them hard to override later. They also cannot be reused — only one element per page can have a given id. In practice, experienced developers use classes for styling and reserve IDs for JavaScript hooks and anchor links.

Attribute Selectors

Target elements based on their HTML attributes — or even partial attribute values. This is powerful for styling links, inputs, and data attributes.

CSS
Try it →
/* Any element that has a "title" attribute */
[title] {
  cursor: help;
}

/* Input whose type is exactly "email" */
input[type="email"] {
  border-color: #3b82f6;
}

/* Links that start with "https" */
a[href^="https"] {
  color: green;
}

/* Files that end with ".pdf" */
a[href$=".pdf"] {
  color: crimson;
}

/* Elements whose class contains "btn" anywhere */
[class*="btn"] {
  cursor: pointer;
}

Combinators

Combinators describe the relationship between two selectors. They let you target elements based on their position in the HTML tree.

Four Combinators

Descendant div pAny <p> inside a <div>, at any depth.
> Child ul > liOnly direct children. Does not reach nested descendants.
+ Adjacent Sibling h2 + pThe <p> that comes immediately after an <h2>.
~ General Sibling h2 ~ pAll <p> elements that follow an <h2>, at the same level.
CSS
Try it →
/* Descendant: any <a> inside .nav */
.nav a {
  text-decoration: none;
}

/* Child: only direct <li> of .menu */
.menu > li {
  display: inline-block;
}

/* Adjacent sibling: the first <p> after any <h2> */
h2 + p {
  font-size: 1.1rem;
  color: #555;
}

/* General sibling: all <p> after an <h2> */
h2 ~ p {
  margin-left: 1rem;
}

Pseudo-classes

Pseudo-classes select elements based on their state or position — things you cannot express with a simple attribute. They start with a single colon :.

CSS
Try it →
/* User interaction states */
a:hover {
  color: crimson;
}

input:focus {
  outline: 2px solid #3b82f6;
}

button:active {
  transform: scale(0.97);
}

/* Positional pseudo-classes */
li:first-child {
  font-weight: bold;
}

li:last-child {
  border-bottom: none;
}

tr:nth-child(even) {
  background: #f8f9fa;
}

Pseudo-elements

Pseudo-elements create virtual elements that do not exist in the HTML. They use a double colon :: to distinguish them from pseudo-classes.

CSS
Try it →
/* Add a bullet before each list item */
li::before {
  content: "→ ";
  color: #3b82f6;
}

/* Style the first line of a paragraph */
p::first-line {
  font-weight: bold;
}

/* Enlarge the first letter (drop cap) */
p::first-letter {
  font-size: 2em;
  float: left;
  margin-right: 0.25rem;
}

Grouping Selectors

When multiple selectors share the same styles, separate them with commas. This avoids duplicating declarations.

CSS
Try it →
/* Without grouping — repetitive */
h1 { font-family: 'Inter', sans-serif; }
h2 { font-family: 'Inter', sans-serif; }
h3 { font-family: 'Inter', sans-serif; }

/* With grouping — one rule for all */
h1,
h2,
h3 {
  font-family: 'Inter', sans-serif;
}

⚖️ Specificity & The Cascade

When two CSS rules target the same element with conflicting values, the browser needs a tiebreaker. That tiebreaker is called the cascade — a three-step algorithm that determines which rule wins.

The Cascade — Three Tiebreakers (in order)

1 Origin & ImportanceAuthor styles beat browser defaults. !important rules beat normal rules (but avoid using it).
2 SpecificityMore specific selectors beat less specific ones. #id beats .class beats element.
3 Source OrderIf two rules have equal specificity, the one written last in the stylesheet wins.

Specificity Scoring

Every selector gets a four-digit score: (Inline, IDs, Classes, Elements). Compare from left to right — the first number that differs wins. One ID (0,1,0,0) always beats any number of classes (0,0,99,0).

Specificity Score Examples

Selector Inline IDs Classes Elements Score
p 0 0 0 1 0,0,0,1
.card 0 0 1 0 0,0,1,0
.nav .link 0 0 2 0 0,0,2,0
#hero .title 0 1 1 0 0,1,1,0
div#main .card p 0 1 1 2 0,1,1,2
style="..." 1 0 0 0 1,0,0,0

Cascade in Action

In this example, three rules target the same paragraph. Which color wins?

CSS
Try it →
p {
  color: black;        /* specificity: 0,0,0,1 */
}

.intro {
  color: navy;         /* specificity: 0,0,1,0 — wins! */
}

body p {
  color: gray;         /* specificity: 0,0,0,2 */
}

The paragraph appears navy. Even though body p has two element selectors (0,0,0,2), one class (0,0,1,0) outweighs any number of elements. Specificity compares column by column from left to right.

Inheritance

Some CSS properties are inherited by child elements automatically. Text properties — color, font-family, font-size, line-height — flow down from parent to children. Box properties — margin, padding, border, background — do not.

CSS
Try it →
/* Set font on body — all text inherits it */
body {
  font-family: 'Inter', sans-serif;
  color: #333;
  line-height: 1.6;
}

/* No need to repeat font-family on <p>, <li>, etc. */
/* They inherit it from body automatically. */
🚫
Avoid !important !important forces a rule to win regardless of specificity. It seems convenient, but it creates a specificity arms race — once you use it, the only way to override it is with another !important. If you find yourself reaching for !important, your selectors are probably too complex. Simplify them instead.

📦 The Box Model

Every element on a web page is a rectangular box. Even a round button, a circular avatar, or a line of text — the browser treats them all as boxes. The box model defines how space is calculated around and inside each element.

💡
The Package Analogy: Think of a shipping package. The content is the item inside. The padding is the bubble wrap around it. The border is the cardboard box. And the margin is the empty space between this box and the next one on the shelf.

The Four Layers

margin
border
padding
content
Your Text

Box Model Layers (inside → outside)

1 ContentThe actual text, image, or child elements. Sized by width and height.
2 PaddingSpace inside the border, between the border and the content. Background color fills this area.
3 BorderA visible line around the padding. Has its own width, style, and color.
4 MarginSpace outside the border. Creates distance between this element and its neighbors. Transparent — never has color.
CSS
Try it →
.card {
  width: 300px;           /* content width */
  padding: 20px;          /* inner space */
  border: 2px solid #ddd; /* visible edge */
  margin: 16px;           /* outer space */
}

/* Total rendered width (default box-sizing):
   300 + 20+20 + 2+2 + 16+16 = 376px */

box-sizing: The Math Fix

By default, width: 300px sets only the content width. Padding and border are added on top, making the actual box wider than 300px. This default behavior is called content-box — and it is confusing.

The solution: box-sizing: border-box. It makes width include padding and border. If you set width: 300px with border-box, the box is exactly 300px wide — padding and border are subtracted from the inside.

CSS
Try it →
/* The modern reset — add this to every project */
*,
*::before,
*::after {
  box-sizing: border-box;
}
💡
Always use border-box. The three-line reset above should be the first rule in every stylesheet you write. It makes width calculations intuitive: width: 300px means the box is 300px wide, period. Every CSS framework (Bootstrap, Tailwind, etc.) includes this reset.

Shorthand Properties

Margin and padding accept 1 to 4 values in a single declaration. The values go clockwise: top, right, bottom, left (think of a clock starting at 12).

CSS
Try it →
/* 1 value: all four sides */
.a { padding: 16px; }

/* 2 values: top/bottom  left/right */
.b { padding: 16px 24px; }

/* 3 values: top  left/right  bottom */
.c { padding: 16px 24px 8px; }

/* 4 values: top  right  bottom  left (clockwise) */
.d { padding: 16px 24px 8px 12px; }

Margin Collapse

When two vertical margins touch, they do not add up — they collapse into the larger of the two. If a heading has margin-bottom: 24px and the following paragraph has margin-top: 16px, the space between them is 24px, not 40px.

CSS
Try it →
/* These margins collapse — gap is 24px, not 40px */
h2 {
  margin-bottom: 24px;
}

p {
  margin-top: 16px;
}
⚠️
Margin collapse only happens vertically. Horizontal margins never collapse — they always add up. Vertical margins collapse between siblings and between parent/child when there is no padding, border, or content between them. This catches many beginners off guard.

📏 Units

CSS offers dozens of units for sizing. Some are fixed (absolute), others scale with context (relative). Choosing the right unit is crucial for responsive, accessible design.

Absolute Units

Absolute units have a fixed size regardless of context. In practice, only px (pixels) matters for screen design. The others (cm, mm, in, pt) are for print stylesheets.

CSS
Try it →
.fixed-box {
  width: 320px;
  font-size: 16px;
  border: 1px solid #ccc;
}

Relative Units

Relative units scale based on something else — the parent element, the root font size, or the viewport. They are the key to responsive design.

Relative Units Reference

Unit Relative To Best For
em Parent's font size Padding/margin that scales with text
rem Root (<html>) font size Font sizes, spacing — predictable scaling
% Parent element's size Widths, layout proportions
vw 1% of viewport width Full-width sections, hero text
vh 1% of viewport height Full-screen hero sections
ch Width of the "0" character Optimal reading width (~60-80ch)

em vs rem — The Key Difference

em is relative to the parent element's font size. This means em values compound when nested — a 1.5em inside a 1.5em becomes 2.25× the base size. rem (root em) is always relative to the <html> element, so it stays predictable regardless of nesting.

CSS
Try it →
/* The root font size (browser default: 16px) */
html {
  font-size: 16px;
}

/* rem — always relative to root (16px) */
h1 {
  font-size: 2rem;    /* = 32px, always */
}

h2 {
  font-size: 1.5rem;  /* = 24px, always */
}

/* Spacing with rem — predictable everywhere */
.section {
  padding: 2rem;     /* = 32px */
  margin-bottom: 1.5rem; /* = 24px */
}
💡
Rule of thumb: Use rem for font sizes and spacing. Use % or vw for widths and layout. Use px only for things that should never scale (borders, shadows, tiny details). Use em only when you intentionally want a value to scale with its parent's font size.

Viewport Units

Viewport units let you size elements relative to the browser window. 100vw = full width of the viewport, 100vh = full height.

CSS
Try it →
/* Full-screen hero section */
.hero {
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Responsive text that scales with viewport */
.big-title {
  font-size: 5vw;
}

🎨 Colors

CSS gives you multiple ways to define colors. Each format has strengths — hex is compact, RGB is familiar, and HSL is the most intuitive for design work.

Named Colors

CSS has 140+ built-in color names like red, navy, tomato, cornflowerblue. Handy for quick prototyping but limited for precise design.

CSS
Try it →
h1 { color: navy; }
.warning { color: crimson; }
.success { background: mediumseagreen; }

Hexadecimal

Hex codes are the most common color format on the web. They use #RRGGBB — two digits for red, green, and blue, each from 00 (none) to FF (full). You can shorten to #RGB when each pair is a double digit.

Hex Color Examples

#FF0000Pure red (short: #F00)
#00FF00Pure green (short: #0F0)
#3B82F6A pleasant blue (no shorthand)
#333333Dark gray (short: #333)
CSS
Try it →
body {
  background: #f8f9fa;   /* light gray */
  color: #1a1a2e;       /* near-black */
}

.brand {
  color: #3b82f6;       /* blue */
}

RGB & RGBA

rgb(R, G, B) uses decimal values 0–255 for each channel. Add an alpha channel (0–1) with rgba() or the modern syntax rgb(R G B / A) for transparency.

CSS
Try it →
/* Solid colors */
.heading {
  color: rgb(59, 130, 246);    /* blue */
}

/* With transparency (alpha) */
.overlay {
  background: rgba(0, 0, 0, 0.5);  /* 50% black */
}

/* Modern syntax (same result) */
.overlay-modern {
  background: rgb(0 0 0 / 0.5);
}

HSL — The Designer's Format

hsl(H, S%, L%) uses Hue (0–360° on the color wheel), Saturation (0% gray to 100% vivid), and Lightness (0% black to 100% white). HSL is the most intuitive format because you can create variations of a color by adjusting one number.

HSL Color Wheel

hsl(0, 100%, 50%)Red (0°)
hsl(120, 100%, 40%)Green (120°)
hsl(220, 90%, 56%)Blue (220°)
hsl(220, 90%, 76%)Same hue, lighter (+20% lightness)
hsl(220, 90%, 36%)Same hue, darker (−20% lightness)
CSS
Try it →
/* Create a color palette from one hue */
.btn-primary {
  background: hsl(220, 90%, 56%);  /* base blue */
}

.btn-primary:hover {
  background: hsl(220, 90%, 46%);  /* darker on hover */
}

.btn-light {
  background: hsl(220, 90%, 96%);  /* very light tint */
  color: hsl(220, 90%, 40%);       /* dark text */
}

CSS Custom Properties (Variables)

Custom properties let you define reusable values once and reference them everywhere. Define them with --name and use them with var(--name). The most common use: a centralized color palette you can change in one place.

CSS
Try it →
/* Define your palette on :root */
:root {
  --color-primary: hsl(220, 90%, 56%);
  --color-primary-dark: hsl(220, 90%, 40%);
  --color-text: #1a1a2e;
  --color-bg: #ffffff;
  --spacing: 1rem;
}

/* Use them anywhere */
body {
  color: var(--color-text);
  background: var(--color-bg);
}

.btn {
  background: var(--color-primary);
  padding: var(--spacing);
}

.btn:hover {
  background: var(--color-primary-dark);
}
💡
Dark mode in one move. Define your colors as custom properties, then redefine them inside a [data-theme="dark"] selector. Flip one attribute on <html> and the entire site switches themes. This is exactly how this course page implements dark mode.

currentColor

The keyword currentColor inherits the element's computed color value. It is useful for borders, shadows, and SVG fills that should match the text color automatically.

CSS
Try it →
.tag {
  color: #3b82f6;
  border: 1px solid currentColor;   /* border matches text color */
  box-shadow: 0 0 0 3px currentColor; /* shadow too */
}

✏️ Practice: CSS Fundamentals

Test your understanding of selectors, specificity, the box model, and colors.

Exercise 1: Write the Selector

For each description, write the CSS selector that targets the correct elements.

1. All paragraphs inside an element with class article.

2. Only the direct <li> children of an element with class menu (not nested sublists).

3. All links that open a PDF file.

4. Every other row in a table (even rows).

Exercise 2: Rank the Specificity

Rank these selectors from lowest to highest specificity.

CSS
Try it →
p                       /* A */
.card p                  /* B */
#main .card p            /* C */
.card .content p:hover   /* D */

Exercise 3: Fix the Box Model

This card is supposed to be exactly 300px wide, but it renders wider. Find the three problems.

CSS
Try it →
.card {
  width: 300px;
  padding: 20px;
  border: 3px solid #ccc;
  margin: 16px;
}

Exercise 4: Debug the Cascade

Given this HTML and CSS, what color is the heading? Explain why.

HTML
Try it →
<div id="page">
  <h1 class="title">Hello</h1>
</div>
CSS
Try it →
h1 { color: red; }
.title { color: blue; }
#page h1 { color: green; }
h1.title { color: orange; }

📋 Module Summary

⚙️
How CSS Works

CSS rules = selector + declarations. Use external stylesheets (<link>) for every real project. Inline and internal styles exist but are rarely the right choice.

🎯
Selectors

Type (p), class (.card), ID (#hero), attribute ([type="email"]), pseudo-classes (:hover), pseudo-elements (::before), and combinators (>, +, ~).

⚖️
Specificity

Score: (Inline, IDs, Classes, Elements). Compare column by column. One ID beats any number of classes. Avoid !important.

📦
Box Model

Content → Padding → Border → Margin. Always use box-sizing: border-box so width includes padding and border. Vertical margins collapse.

📏
Units

rem for fonts and spacing, %/vw for widths, px for borders. em compounds when nested — rem does not.

🎨
Colors

Hex (#3b82f6), RGB (rgb(59,130,246)), HSL (hsl(220,90%,56%)). Use HSL for palettes, custom properties for reusable design tokens.

Coming Soon →

Module 6: Typography & Visual Design

Typography, text effects, backgrounds, gradients, shadows, and visual polish. Make your pages look professional.