Every visual decision on the web comes down to CSS. This is where you learn to control it — not guess at it.
The building blocks every front-end developer uses every single day:
box-sizing: border-box rule every real project starts with, and why it saves you from layout headachesMost CSS frustration — elements the wrong size, layouts breaking unexpectedly, styles not applying — comes from not understanding the Box Model and specificity. Once these two concepts click, CSS stops feeling like guesswork and starts feeling like a tool you control.
Make sure you've completed Lecture 2: HTML Foundations. You should be comfortable with:
head, body, etc.)📎 The cheatsheet for this lecture is attached below the video. The Box Model diagram and the Common Properties Quick Reference table are worth keeping open as you code along!
Use this for: quick syntax lookup · copy-paste code · revision before the next class
How to use: scan section 1 for the big ideas. Use section 2 as a syntax reference. Grab code from section 3 when practicing. Check section 4 before submitting anything that feels off. If a term in the lecture confused you, section 5 has a plain-English translation. Section 6 points you to the assignment, quiz, and next lecture. Section 7 has interview questions with brief answers for self-testing.
Scope: this cheatsheet covers exactly what was taught in Lecture 4. Nothing extra. If it's not here, it wasn't in today's class.
style=""), internal (<style> in head), external (<link rel="stylesheet">); external is the production defaultcolor, font, line-height) inherit; layout properties (padding, margin, border, width) don't>) · adjacent sibling (+) · pseudo-class (:hover) · pseudo-element (::before)tomato · #ff6347 · rgb(255 99 71) · rgb(255 99 71 / 0.5) · hsl(9 100% 64%)font-family · font-size · font-weight · line-height (unitless) · letter-spacing · max-width: 60ch:root tokens + var() — declare --name: value; once, consume var(--name) everywhere; theme by overriding on a wrapper classborder-box — content → padding → border → margin; * { box-sizing: border-box; } makes width math sane forever| Method | Example | When to use |
|---|---|---|
| Inline | <h1 style="color: tomato">…</h1> | Prototypes only — highest specificity, hardest to override |
| Internal | <style> h1 { color: tomato; } </style> in <head> | Single-page experiments |
| External | <link rel="stylesheet" href="styles.css"> | Production default — cacheable, shared across pages |
| Selector | Example | Matches |
|---|---|---|
| Type | h1 | All <h1> elements |
| Class | .btn | Every element with class="btn" (reusable, weight 10) |
| ID | #nav | The single element with id="nav" (one-of-a-kind, weight 100) |
| Descendant | .menu a | Any <a> anywhere inside .menu |
| Child | .menu > a | <a> that is a direct child of .menu |
| Adjacent sibling | h2 + p | The <p> that comes immediately after an <h2> |
| Attribute | input[type="email"] | Inputs whose type attribute equals "email" |
| Pseudo-class (state) | :hover, :focus-visible, :nth-child(odd) | Single colon — element state |
| Pseudo-element (part) | ::before, ::after, ::placeholder | Double colon — needs content to render |
| Selector kind | Weight column | Example | Score |
|---|---|---|---|
Inline style="" | 1000 | <p style="color: blue"> | (1, 0, 0, 0) |
| ID | 100 | #alert | (0, 1, 0, 0) |
| Class / pseudo-class / attribute | 10 | .warning, :hover, [type="email"] | (0, 0, 1, 0) |
| Element / pseudo-element | 1 | p, ::before | (0, 0, 0, 1) |
Read scorecards left-to-right as a 4-digit number — (0, 1, 0, 0) beats (0, 0, 9, 9) because the IDs column wins first. Ties go to source order.
| Notation | Example | When to use |
|---|---|---|
| Named | tomato | Prototypes only (~140 names) |
| Hex | #ff6347 | Design handoffs (Figma exports hex) |
rgb() (modern) | rgb(255 99 71) | Anywhere — no commas in modern syntax |
rgb() with alpha | rgb(255 99 71 / 0.5) | When you need transparency (alpha is 0–1, not 0–100) |
hsl() | hsl(9 100% 64%) | When you want to shift hue/lightness algorithmically |
| Property | Example | Effect |
|---|---|---|
font-family | 'Inter', system-ui, sans-serif | Typeface stack — fallback chain if first not loaded |
font-size | 18px / 1rem | rem = root font-size; em = element's own |
font-weight | 400 / 700 | 100–900; pick 1–2 weights only when using web fonts |
line-height | 1.6 | Unitless — scales with font-size automatically |
letter-spacing | 0.01em | Tracks slightly opens/tightens |
max-width | 60ch | ch = width of one 0; ~60ch is ideal reading measure |
| Property | Example | Behavior |
|---|---|---|
width / height | width: 200px; | Sizes the content layer by default (content-box) |
padding | padding: 20px; | Space inside the border, around the content |
border | border: 5px solid steelblue; | Line around the padding |
margin | margin: 16px; | Space outside the border, separating siblings |
box-sizing | box-sizing: border-box; | Make width include padding+border (sane math) |
border-radius | border-radius: 12px; / 50% | Round corners; 50% = circle |
box-shadow | box-shadow: 0 4px 12px rgb(0 0 0 / 0.08); | <x-offset> <y-offset> <blur> <color> |
| Pattern | Example | Purpose |
|---|---|---|
Declare on :root | :root { --primary: #2563eb; } | Global token, available everywhere |
Consume with var() | background: var(--primary); | Read the token's current value |
| Theme override | .theme-dark { --primary: #ff8c66; } | Re-define inside a wrapper class — descendants pick the closer value |
| Naming pattern | --<category>-<role>-<modifier> | e.g., --color-primary-hover, --font-size-lg |
<h1> styled three waysInline beats internal beats external when specificity is otherwise tied.
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <title>Cascade Demo</title>6 <link rel="stylesheet" href="styles.css" />7 <style>8 h1 { color: rebeccapurple; }9 </style>10 </head>11 <body>12 <h1 style="color: tomato;">Hello CSS</h1>13 </body>14</html>
1/* styles.css */2h1 { color: steelblue; }
Renders: "Hello CSS" in tomato — inline wins. Open DevTools → Styles panel; the two losers have a strikethrough.
Selector · { block } · property: value; — three semicolons for three declarations.
1h1 {2 color: tomato;3 font-size: 2rem;4 font-weight: 700;5}
Renders: heading in tomato, 2× the body font-size, bold.
Bare name = element · . = class (reusable) · # = ID (one-of-a-kind).
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <title>Selectors Demo</title>6 <style>7 li { color: gray; }8 .featured { color: tomato; font-weight: 700; }9 #me { color: rebeccapurple; text-decoration: underline; }10 </style>11 </head>12 <body>13 <nav>14 <ul>15 <li>Home</li>16 <li class="featured">About</li>17 <li id="me" class="featured">Contact</li>18 </ul>19 </nav>20 </body>21</html>
Renders: "Home" gray · "About" tomato + bold · "Contact" purple + underlined + bold (it gets .featured AND #me — non-conflicting properties merge).
Space = anywhere inside · > = direct child only · + = the very next sibling.
1/* descendant — any <a> anywhere inside .menu */2.menu a { color: navy; }34/* direct child only — <a> that is an immediate child of .menu */5.menu > a { background: gold; }67/* adjacent sibling — the <p> that comes RIGHT AFTER an <h2> */8h2 + p { font-style: italic; }
1<div class="menu">2 <a href="#">Direct child link</a>3 <ul>4 <li><a href="#">Nested link</a></li>5 </ul>6</div>7<h2>A heading</h2>8<p>This paragraph is italic — it follows an h2.</p>9<p>This one is not — it's the second paragraph.</p>
Renders: "Direct child link" navy + gold background · "Nested link" navy only (descendant, not direct child) · first <p> italic; second <p> plain.
Single colon for state · double colon for parts · pseudo-elements need content.
1/* state */2li { padding: 4px 8px; transition: background 0.15s; }3li:hover { background: lightyellow; cursor: pointer; }4li:focus { outline: 2px solid steelblue; }56/* striping */7li:nth-child(odd) { background: #f7f7f7; }89/* generated content — needs content: */10li.featured::before { content: "★ "; color: gold; }
Renders: hover → light-yellow background · keyboard focus → blue outline · odd rows faint gray stripe · featured items show a gold star prefix.
Production-grade patterns: attribute selectors, :focus-visible, :nth-of-type, :not(), ::placeholder.
1/* attribute selectors */2input[type="email"] { border: 2px solid steelblue; }3input[type="email"]:invalid { border-color: tomato; }4a[target="_blank"]::after { content: " ↗"; color: #888; }56/* focus-visible — keyboard focus only, not mouse */7button:focus-visible {8 outline: 3px solid #2563eb;9 outline-offset: 2px;10}1112/* nth-of-type — every 3rd <p>, ignoring sibling elements */13p:nth-of-type(3n) { color: tomato; }1415/* :not — invert a selector */16button:not(.primary) { opacity: 0.7; }1718/* placeholder text styling */19input::placeholder { color: #aaa; font-style: italic; }
Renders: email input gets blue border (red when invalid) · external links get a ↗ arrow · keyboard tab to a button shows thick blue outline (mouse click does not) · every third <p> tomato · non-primary buttons dimmed.
All five render the same orange — pick by use case.
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <title>Colors</title>6 <style>7 div { width: 200px; height: 60px; margin: 8px; color: white;8 display: inline-block; padding: 16px; font-family: sans-serif; }9 .c1 { background: tomato; }10 .c2 { background: #ff6347; }11 .c3 { background: rgb(255 99 71); }12 .c4 { background: rgb(255 99 71 / 0.5); color: black; }13 .c5 { background: hsl(9 100% 64%); }14 </style>15 </head>16 <body>17 <div class="c1">tomato</div>18 <div class="c2">#ff6347</div>19 <div class="c3">rgb()</div>20 <div class="c4">rgba 50%</div>21 <div class="c5">hsl()</div>22 </body>23</html>
Renders: five tomato-colored boxes side by side · boxes 1, 2, 3, 5 identical opaque orange · box 4 half-transparent.
Six properties layered onto one paragraph.
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <title>Typography</title>6 <style>7 p {8 font-family: Georgia, serif;9 font-size: 18px;10 font-weight: 400;11 line-height: 1.6;12 letter-spacing: 0.01em;13 text-align: left;14 max-width: 60ch;15 }16 </style>17 </head>18 <body>19 <p>The quick brown fox jumps over the lazy dog. This is a paragraph20 long enough to wrap onto multiple lines so you can see line-height21 and letter-spacing actually do something visible.</p>22 </body>23</html>
Renders: a wide paragraph in Georgia serif at 18px, generous 1.6 line-height, slightly opened letter-spacing, capped at ~60 characters per line.
:root tokens + var() + theme overrideSame .btn class, two visual results — variables resolve to the nearest declaration.
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <title>Tokens</title>6 <style>7 :root {8 --primary: #ff6347;9 --bg: white;10 --text: #222;11 --radius: 8px;12 }13 body { background: var(--bg); color: var(--text); padding: 32px;14 font-family: system-ui, sans-serif; }15 .btn {16 background: var(--primary);17 color: white;18 padding: 12px 24px;19 border: none;20 border-radius: var(--radius);21 font-size: 16px;22 cursor: pointer;23 }24 .theme-dark {25 --primary: #ff8c66;26 --bg: #111;27 --text: #eee;28 }29 </style>30 </head>31 <body>32 <button class="btn">Light mode</button>3334 <div class="theme-dark" style="padding: 32px; margin-top: 16px;">35 <button class="btn">Dark mode (override)</button>36 </div>37 </body>38</html>
Renders: top button on white bg with tomato fill · bottom button inside .theme-dark has salmon-pink fill on a dark background · same .btn class, different result.
Paste at the top of every new stylesheet. 8 colors · 4 font sizes · 3 radii · 2 fonts.
1:root {2 /* color palette */3 --color-primary: #2563eb;4 --color-primary-hover: #1d4ed8;5 --color-text: #1f2937;6 --color-text-muted: #6b7280;7 --color-bg: #ffffff;8 --color-bg-alt: #f9fafb;9 --color-border: #e5e7eb;10 --color-danger: #dc2626;1112 /* typography scale */13 --font-size-sm: 0.875rem; /* 14px */14 --font-size-base: 1rem; /* 16px */15 --font-size-lg: 1.25rem; /* 20px */16 --font-size-xl: 1.875rem; /* 30px */1718 /* radii */19 --radius-sm: 4px;20 --radius-md: 8px;21 --radius-lg: 16px;2223 /* font stacks */24 --font-sans: 'Inter', system-ui, -apple-system, sans-serif;25 --font-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;26}2728body {29 font-family: var(--font-sans);30 font-size: var(--font-size-base);31 color: var(--color-text);32 background: var(--color-bg);33 line-height: 1.5;34}
Renders: body picks up Inter (with system fallback), 16px base, slate text on white. No visible change yet — further selectors reference the tokens.
With default content-box, padding and border add outside the declared width.
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <title>Box Model</title>6 <style>7 .card {8 width: 200px;9 height: 200px;10 padding: 20px;11 border: 5px solid steelblue;12 margin: 16px;13 background: lightyellow;14 }15 </style>16 </head>17 <body>18 <div class="card">I declared 200×200. Measure me.</div>19 </body>20</html>
Renders: pale-yellow card with blue border. DevTools box-model widget shows content 200×200, padding 20, border 5, margin 16. Outer edge: 200 + 20 + 20 + 5 + 5 = 250px.
box-sizing: border-boxThe single most important CSS line in the lecture — every stylesheet from now on starts with this.
1* {2 box-sizing: border-box;3}
Renders: re-measure the same card → outer edge is now 200px. Padding and border eat into the declared width. Margin still adds outside.
border-radius + box-shadow — instant polishTwo properties · the cheapest "this looks designed" wins in CSS.
1.card {2 width: 240px;3 height: 240px;4 padding: 24px;5 border: 1px solid #e5e7eb; /* lighter border */6 margin: 24px;7 background: white;8 border-radius: 12px;9 box-shadow: 0 4px 12px rgb(0 0 0 / 0.08);10}
Renders: the heavy yellow square becomes a clean white card with rounded corners and a subtle drop shadow. border-radius: 50% would make it a circle (avatar trick).
Profile card from scratch using :root tokens, box-sizing: border-box, border-radius, and box-shadow.
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <title>Card Challenge</title>6 <style>7 :root {8 --primary: #2563eb;9 --text: #1f2937;10 --bg: #f9fafb;11 --radius: 12px;12 }1314 * { box-sizing: border-box; }1516 body {17 font-family: system-ui, sans-serif;18 background: var(--bg);19 padding: 32px;20 color: var(--text);21 }2223 .card {24 max-width: 280px;25 padding: 24px;26 border: 1px solid #e5e7eb;27 border-radius: var(--radius);28 box-shadow: 0 4px 12px rgb(0 0 0 / 0.08);29 background: white;30 text-align: center;31 }3233 .card img {34 width: 120px;35 height: 120px;36 border-radius: 50%;37 margin-bottom: 16px;38 }3940 .card p {41 font-size: 1.125rem;42 font-weight: 600;43 color: var(--primary);44 margin: 0;45 }46 </style>47 </head>48 <body>49 <div class="card">50 <img src="https://i.pravatar.cc/120" alt="Your avatar" />51 <p>Asad Iqbal</p>52 </div>53 </body>54</html>
Renders: a clean white card on a soft gray background, with a circular avatar, the name in blue beneath it, rounded corners, and a subtle drop shadow.
style="" with internal <style> → inline is an attribute on one tag (specificity 1000); internal is a <head> block targeting many.!important to "fix" cascade conflicts → it becomes its own arms race; raise specificity with a class or restructure instead.color, font, line-height); padding, margin, border, width do not.#nav and .nav as interchangeable → ID = weight 100, class = weight 10; use class for styling, reserve ID for fragment links.::before without a content property → renders nothing; add content: ""; (even empty) so the pseudo-element appears.rgba(0,0,0,50) for "50% transparent" → invalid; alpha is a 0–1 fraction → use rgb(0 0 0 / 0.5) or rgba(0,0,0,0.5).width: 200px element to actually be 200px wide → with default content-box, padding+border add on top → set * { box-sizing: border-box; } at the top of every stylesheet.| Term | Plain-English meaning |
|---|---|
| CSS | Cascading Style Sheets — the language for telling the browser how HTML should look |
| Selector | The "address" part of a CSS rule — points at which HTML elements to style |
| Declaration | A property: value; pair inside a rule (e.g. color: tomato;) |
| Rule | A selector plus its { ... } block of declarations |
| Cascade | The browser's process for picking which rule wins when many target the same element |
| Specificity | A score the cascade uses — higher score wins. Computed as (inline, IDs, classes, elements) |
| Inheritance | When a child element silently picks up its parent's value (only for typographic properties) |
| Pseudo-class | A selector for an element's state (:hover, :focus, :checked) |
| Pseudo-element | A selector for a part of an element (::before, ::after, ::first-line) — needs content |
| Combinator | A symbol that links selectors: descendant (space), child >, adjacent sibling + |
| Box model | The four concentric layers of every element: content → padding → border → margin |
box-sizing | Which layers count toward the declared width — content-box (default) vs border-box (sane) |
| Custom Property (CSS Variable) | A reusable value declared with --name: value; and consumed via var(--name) |
:root | The topmost selector — equivalent to <html> but with one extra weight; where global tokens live |
| HSL | Hue/Saturation/Lightness — a color notation that maps to human intuition |
| Hex code | #RRGGBB color notation — 0–255 in base-16 per channel |
rem / em | Font-relative size units — rem = root's font-size; em = element's own |
| Web font | A typeface loaded over the internet (Google Fonts, Adobe Fonts, self-hosted .woff2) |
| Design token | A named, centralized value (color, spacing, radius) referenced everywhere — single source of truth |
| Specificity inflation | Writing overly long selector chains "to be safe" — locks you in, slows the matcher |
| Margin collapsing | When two vertical margins meeting between siblings merge into the larger of the two |
Practice: assignment.md — build a styled card / landing page using :root tokens, box-sizing: border-box, the selector toolbox, and the box model. Core takes ~30–45 min, stretch another ~30–60. Submit before Lecture 5.
Quiz: quiz.md — 10 questions. ~15 min, open-book. Due before Lecture 5.
Next lecture: Lecture 5 — Layout Mastery: Flexbox & CSS Grid. With the box model fluent and selectors second-nature, you'll learn position, the Flexbox 1D model (navbars, card rows, sticky footers), the CSS Grid 2D model (full-page layouts), and the "1D → Flex, 2D → Grid" decision rule.
box-sizing behavior.rem-based sizing matters; primes the responsive lesson coming next module.Stuck? Post in the class forum or DM the TA.
Quick-reference questions commonly asked about Lecture 4 concepts. Try answering each one before reading the brief answer.
Walk me through the CSS cascade — when two rules target the same element with the same specificity, which one wins? Source order — the rule that appears later in the stylesheet (or in the later-loaded stylesheet) wins. Specificity is the first tie-breaker; source order is the second.
How is specificity calculated? Score #header .nav li a:hover vs nav ul li a.active.
Score as (inline, IDs, classes, elements). #header .nav li a:hover = 1 ID + 1 class + 1 pseudo-class (counts as class) + 2 elements = (0, 1, 2, 2). nav ul li a.active = 1 class + 4 elements = (0, 0, 1, 4). The first wins on the IDs column.
Explain the box model. What does box-sizing: border-box change, and why is it the modern default?
Every element is four concentric layers: content → padding → border → margin. By default (content-box), width sizes only the content, so padding and border add outside it (a width: 200px card with 20px padding + 5px border measures 250px). border-box makes width include padding and border, so 200px stays 200px — predictable math is why every modern reset starts with * { box-sizing: border-box; }.
What's the difference between a class selector and an ID selector — when would you reach for each in production code?
Class (.btn) is reusable and weighs 10; ID (#nav) is one-of-a-kind per page and weighs 100. Use classes for all styling (reusable, easy to refactor); reserve IDs for fragment links (<a href="#section-2">) and <label for=""> associations.
Which CSS properties are inherited by default, and which are not? Give two examples of each.
Typographic properties inherit: e.g., color and font-family (also line-height, visibility). Layout/box properties don't: e.g., padding and margin (also border, width). Rule of thumb: text-stuff inherits, box-stuff doesn't.
What's the practical difference between display: none, visibility: hidden, and opacity: 0?
display: none removes the element from the layout entirely (no space reserved, not focusable). visibility: hidden hides the element but keeps its space in the layout. opacity: 0 makes it fully transparent but it still occupies space and remains interactive (clickable, focusable).
What are CSS Custom Properties and how do they differ from preprocessor variables (Sass $primary)?
CSS custom properties (--primary: #2563eb; consumed via var(--primary)) are runtime values that cascade and inherit through the DOM, so a wrapper class can re-define them and descendants pick the closer value (the basis of theming). Sass $primary is compile-time — values are baked into the output CSS and can't be changed in the browser or scoped via the cascade.