01

The Box Model

📖 Core Concept Every HTML element is a rectangular box. The box model describes the layers around that box: content → padding → border → margin. Understanding this is fundamental — it controls all spacing and sizing in CSS.
MARGIN
BORDER
PADDING
CONTENT
width × height
/* THE #1 RULE: Always set box-sizing globally */ *, *::before, *::after { box-sizing: border-box; /* width now INCLUDES padding + border */ } .box { width: 20rem; /* content area */ padding: 1rem 2rem; /* inside the border */ border: 2px solid; /* the edge */ margin: 1rem auto; /* outside space, auto = center */ } /* Individual sides */ padding-top: 1rem; /* or: padding: T R B L */ margin-left: auto; /* auto on horizontal = push */ border-radius: 0.5rem; /* rounds corners */ outline: 2px solid blue; /* OUTSIDE border, no layout impact */
box-sizing: border-box vs content-box comparison

box-sizing: border-box ✓

width: 12rem
total = 12rem ✓

box-sizing: content-box ✗

width: 12rem
total = 14rem ✗
02

Relative Units

📖 Why Relative Units? Absolute units (px) are fixed. Relative units scale with context — the root font size, parent element, or viewport. This makes designs accessible, responsive, and maintainable. This entire page uses relative units throughout.
rem

Relative to root font-size (html). Best for font sizes and spacing. Scales with user preferences.

em

Relative to parent font-size. Cascades — can compound! Great for padding relative to text size.

%

Relative to parent dimension. width: 50% = half of parent. Vertical % often relative to parent width!

vw

1% of viewport width. 100vw = full window width. Fluid layouts and hero sections.

vh

1% of viewport height. 100vh = full screen height. Mind the mobile browser chrome!

ch

Width of the "0" character. Perfect for limiting text line length: max-width: 65ch.

fr

Fractional unit — Grid only. Distributes remaining space. 1fr 2fr = one-third + two-thirds.

clamp

clamp(min, ideal, max) — constrains a fluid value. The ideal is usually vw-based for fluid type.

/* Relative Units Reference */ /* rem — scales with root font-size */ html { font-size: 100%; } /* = 16px by default */ h1 { font-size: 2.5rem; } /* 40px at 16px root */ /* em — relative to current/parent font-size */ button { font-size: 1rem; padding: 0.75em 1.5em; /* scales WITH the button's font-size */ } /* Viewport units */ .hero { min-height: 100vh; font-size: clamp(1.5rem, 4vw, 3rem); /* fluid! */ } /* ch — optimal reading width */ .article { max-width: 65ch; } /* ~65 chars per line */ /* fr — grid fractions */ .grid { grid-template-columns: 1fr 2fr 1fr; }
03

Flexbox

📖 1D Layout System Flexbox arranges items in a single direction — either a row or column. It excels at: centering things, distributing space, and aligning items along an axis. Think of it as "smart row or column."
justify-content: space-between
A
B
C
D
flex: 1 (flex-grow) distributes remaining space
fixed
flex:1
flex:2
align-items: center (cross-axis alignment)
short
tall
tiny
/* FLEX CONTAINER */ .container { display: flex; flex-direction: row; /* row | column | row-reverse */ justify-content: space-between; /* main axis: flex-start | center | space-around */ align-items: center; /* cross axis: flex-start | flex-end | stretch */ flex-wrap: wrap; /* allow wrapping to next line */ gap: 1rem; /* space between items (row-gap, column-gap) */ } /* FLEX ITEMS */ .item { flex: 1; /* shorthand: flex-grow:1 flex-shrink:1 flex-basis:0 */ flex-grow: 2; /* take 2x the remaining space */ flex-shrink: 0; /* refuse to shrink */ flex-basis: 10rem; /* starting size before grow/shrink */ align-self: flex-end; /* override parent align-items */ order: -1; /* visual reorder (negative = moves first) */ } /* CENTERING TRICK (classic use case) */ .center { display: flex; align-items: center; justify-content: center; }
04

CSS Grid

📖 2D Layout System Grid works in TWO dimensions simultaneously — rows AND columns. Use it for page-level layouts and complex component structures. Flexbox for components in one direction; Grid for the overall page skeleton or 2D arrangements.
grid-template-columns: repeat(4, 1fr) — with spanning
1×1
span 2 cols
1×1
span 3 cols
row span 2
1×1
1×1
1×1
1×1
/* GRID CONTAINER */ .grid { display: grid; grid-template-columns: repeat(4, 1fr); /* 4 equal columns */ grid-template-columns: 16rem 1fr 1fr; /* fixed sidebar + 2 fluid */ grid-template-rows: auto 1fr auto; /* header, main, footer */ gap: 1rem; /* row and column gap */ grid-template-areas: "header header header" "sidebar main main" "footer footer footer"; } /* GRID ITEMS */ .header { grid-area: header; } .sidebar { grid-area: sidebar; } .main { grid-area: main; } .wide-item { grid-column: 1 / 3; /* from line 1 to line 3 */ grid-column: span 2; /* span 2 columns from current pos */ grid-row: span 2; /* span 2 rows */ } /* RESPONSIVE GRID — no media queries needed! */ .auto-grid { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); /* items wrap automatically when they can't fit */ }
05

Positioning

📖 The Stacking Context & Document Flow position controls how an element is placed. Most values remove the element from the normal document flow. The key insight: absolute elements are positioned relative to the nearest ancestor with position: relative (or absolute/fixed/sticky).
Live positioning example — hover the boxes
static (normal flow) relative (top:1rem left:2rem)
absolute (top:0.75rem right:0.75rem)
/* position values */ .static { position: static; } /* default — in normal flow */ .relative { position: relative; } /* offset but STAYS in flow (creates positioning context!) */ .absolute { position: absolute; } /* OUT of flow, relative to positioned ancestor */ .fixed { position: fixed; } /* OUT of flow, relative to VIEWPORT */ .sticky { position: sticky; } /* in flow UNTIL threshold, then sticks */ /* Offset properties (work with non-static) */ top: 0; right: 0; bottom: 0; left: 0; inset: 0; /* shorthand for all four! */ /* Stacking order */ z-index: 10; /* higher = in front. Only works on positioned elements! */ /* Classic centering with absolute */ .centered { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
06

Selectors & Specificity

📖 Selecting the Right Element CSS selectors are patterns that match elements. When multiple rules conflict, specificity determines the winner. Inline styles > #id > .class/:pseudo > element tag. Count: (inline, IDs, classes, elements) — compare left to right.
Selector Syntax What it targets
Universal*Every element
Typep, h1, divElements by tag name
Class.cardElements with class="card"
ID#heroElement with id="hero" (unique!)
Descendant.nav aAny <a> inside .nav
Child.nav > liDirect <li> children of .nav
Adjacenth2 + pFirst <p> immediately after h2
Siblingh2 ~ pAll <p> siblings after h2
Attribute[type="text"]Elements by attribute value
Pseudo-class:hover :nth-child(2n)Element state or position
Pseudo-element::before ::afterVirtual sub-elements
:is() / :where():is(h1, h2, h3)Match any in list (:where has 0 specificity)
:not()p:not(.special)Exclude matching elements
/* SPECIFICITY: (inline, id, class, element) */ p { color: black; } /* (0,0,0,1) */ .text { color: blue; } /* (0,0,1,0) — wins over tag */ #intro { color: red; } /* (0,1,0,0) — wins over class */ /* style="..." */ /* (1,0,0,0) — wins over #id */ /* !important */ /* nuclear option — avoid! */ /* Modern approach: use :is() to group without specificity issues */ :is(h1, h2, h3, h4) { line-height: 1.2; } /* Attribute selectors */ [href^="https"] { /* starts with */ } [href$=".pdf"] { /* ends with */ } [class*="icon"] { /* contains */ }
07

Animations & Transforms

📖 Performant Animation For smooth 60fps animations, prefer transform and opacity. These run on the GPU compositor thread and don't trigger layout recalculations. Avoid animating width, height, top, left — these cause expensive reflows!

float
translateY

spin
rotate(360deg)

pulse
::after scale

gradient
bg-position

slide in
translateX

/* @keyframes — define the animation sequence */ @keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1rem); } } /* Apply with animation shorthand */ .floating { animation: float 2s ease-in-out infinite; /* name dur timing iterations */ } /* Full animation syntax */ .detailed { animation-name: slide-in; animation-duration: 0.6s; animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); animation-delay: 0.2s; animation-iteration-count: 1; animation-direction: normal; animation-fill-mode: both; /* holds final state */ animation-play-state: running; /* toggle with JS */ } /* TRANSFORM functions */ transform: translateX(2rem); /* move horizontally */ transform: scale(1.1); /* scale up 10% */ transform: rotate(45deg); /* spin clockwise */ transform: skewX(15deg); /* distort */ transform: scale(1.05) rotate(3deg); /* chain them! */ transform-origin: top left; /* rotation pivot point */ /* Transitions for hover effects */ transition: transform 0.3s ease, opacity 0.3s ease;
08

Filters & Visual Effects

📖 GPU-Powered Effects filter applies image processing effects visually — without touching the actual image data. backdrop-filter blurs what's behind a semi-transparent element, creating "frosted glass" effects. Hover each box to see the filter activate!
none
(base)
hover:
blur(3px)
hover:
bright(150%)
hover:
grayscale
hover:
hue-rotate
hover:
invert
hover:
sepia
/* filter — applied to element and its children */ filter: blur(4px); filter: brightness(0.5); /* 0=black, 1=normal, 2=white */ filter: contrast(200%); filter: grayscale(100%); filter: hue-rotate(90deg); filter: invert(100%); filter: saturate(300%); filter: sepia(80%); filter: opacity(50%); filter: drop-shadow(2px 4px 8px rgba(0,0,0,0.5)); /* follows shape! */ /* Chain multiple filters */ filter: brightness(1.2) contrast(1.1) saturate(1.3); /* backdrop-filter — frosted glass */ .glass { background: rgba(255, 255, 255, 0.15); backdrop-filter: blur(12px) saturate(180%); -webkit-backdrop-filter: blur(12px) saturate(180%); /* Safari */ border: 1px solid rgba(255,255,255,0.2); } /* mix-blend-mode — how element blends with background */ mix-blend-mode: multiply | screen | overlay | difference;
09

Custom Properties (Variables)

📖 Dynamic, Cascading Values CSS custom properties (variables) cascade like any CSS property — child elements inherit them. This makes component-level theming trivial: redefine the variable on a parent element and all children update instantly. They're also writable from JavaScript!

Each card below uses the same CSS class, but different --t-accent variables:

Default

--t-accent: #c94a1f

Ocean

--t-accent: #4db8ff

Forest

--t-accent: #5acc5a

Sunset

--t-accent: #ff7a30

Lavender

--t-accent: #b070ff

/* DEFINE on :root for global access */ :root { --color-primary: #c94a1f; --spacing-unit: 1rem; --font-body: 'DM Sans', sans-serif; } /* USE with var() */ .button { background: var(--color-primary); padding: var(--spacing-unit); /* var(--name, fallback) — fallback if undefined */ color: var(--text-color, white); } /* SCOPE to a component — overrides cascade down */ .card-ocean { --color-primary: #4db8ff; /* only affects this card and children */ } /* CHANGE with JavaScript */ // JS document.documentElement.style.setProperty('--color-primary', 'blue'); /* READ with JavaScript */ getComputedStyle(el).getPropertyValue('--color-primary');
10

Responsive Design

📖 Mobile-First Strategy Start with styles for the smallest screen, then use min-width media queries to progressively enhance for larger screens. This keeps base CSS lean and ensures mobile devices don't download unnecessary styles. Modern CSS often eliminates media queries entirely with fluid units and Grid/Flex.
This grid responds to viewport — resize your browser window!
Col 1
Col 2
Col 3
Col 4

4 cols on desktop → 2 cols on tablet → 1 col on mobile

/* MOBILE-FIRST: base styles = mobile */ .grid { display: grid; grid-template-columns: 1fr; /* single column on mobile */ } /* ENHANCE for tablets (min-width = mobile-first) */ @media (min-width: 48rem) { /* 768px */ .grid { grid-template-columns: repeat(2, 1fr); } } /* ENHANCE for desktops */ @media (min-width: 64rem) { /* 1024px */ .grid { grid-template-columns: repeat(4, 1fr); } } /* USER PREFERENCE media queries */ @media (prefers-color-scheme: dark) { :root { --bg: #0e0e0e; --fg: #f5f0e8; } } @media (prefers-reduced-motion: reduce) { * { animation: none !important; } } /* NO MEDIA QUERY needed with auto-fit */ .fluid-grid { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); /* columns appear/disappear automatically! */ }
11

Pseudo-classes & States

📖 Styling Dynamic States Pseudo-classes apply styles based on element state or position in the DOM — without JavaScript. They're the backbone of interactive CSS. Note: :focus-visible is preferred over :focus for accessibility — it only shows focus rings for keyboard navigation.
Interact with these buttons — hover, click, tab, try disabled
:nth-child(2n) = even (red), :nth-child(3n+1) = 1st,4th,7th... (blue)
1
2
3
4
5
6
7
8
9
10
11
12
/* STATE pseudo-classes */ a:hover { /* mouse over */ } a:active { /* being clicked */ } a:visited { /* already clicked */ } :focus { /* any focus */ } :focus-visible { /* keyboard focus only — prefer this! */ } :checked { /* checkbox/radio checked */ } :disabled { /* form element disabled */ } :placeholder-shown { /* input showing placeholder */ } :empty { /* element has no children */ } :target { /* element matching URL hash */ } /* STRUCTURAL pseudo-classes */ :first-child { /* first sibling */ } :last-child { /* last sibling */ } :nth-child(2n) { /* every even element */ } :nth-child(3n+1) { /* 1st, 4th, 7th... */ } :nth-last-child(2) { /* 2nd from end */ } :only-child { /* only child of parent */ } :not(.special) { /* all EXCEPT .special */ } :is(h1, h2, h3) { /* match any — inherits highest specificity */ } :where(h1, h2, h3) { /* match any — always 0 specificity */ } :has(img) { /* parent that contains an img */ }
12

Shapes & clip-path

📖 Pure CSS Shapes clip-path masks elements to any shape using geometry functions. The visible area is defined by the shape — everything outside is invisible. Hover any shape below to see it animate back to a rectangle!
Hover each shape — it animates to rectangle via clip-path transition
circle
tri
hex
star
arrow
/* clip-path values */ clip-path: circle(50% at 50% 50%); /* circle */ clip-path: ellipse(60% 40% at center); /* ellipse */ clip-path: inset(1rem 2rem round 0.5rem); /* inset rectangle */ clip-path: polygon(50% 0%, 0% 100%, 100% 100%); /* triangle */ clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); /* hexagon */ /* Animatable! */ .shape { clip-path: circle(45%); transition: clip-path 0.4s ease; } .shape:hover { clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); /* square */ } /* shape-outside — text wraps around a shape! */ .float-left { float: left; shape-outside: circle(50%); clip-path: circle(50%); }
13

calc() & CSS Math

📖 Mixed-Unit Arithmetic calc() is the only way to mix different CSS units in a computation. This is enormously powerful: you can subtract a fixed header height from 100vh, combine viewport units with rem-based padding, etc. Always put spaces around + and - operators!
Widths computed with calc(), min(), max(), clamp()
calc(100% - 4rem)
min(100%, 30rem)
clamp(8rem, 50%, 22rem)
max(10rem, 25%)
/* calc() — mixed unit arithmetic */ height: calc(100vh - 4rem); /* full height minus header */ width: calc(50% - 1rem); /* half width minus gap */ font-size: calc(1rem + 0.5vw); /* fluid with floor */ padding: calc(var(--space) * 2); /* multiply a variable */ /* Operators: + - * / (spaces required around + and -!) */ calc(1rem + 8px) /* ✓ mixes units */ calc(100% - 30px) /* ✓ subtract fixed from fluid */ calc(3 * 2rem) /* ✓ multiply (unitless * unit) */ calc(100px/2) /* ✓ divide (no spaces needed for /) */ /* min() — picks smallest value */ width: min(100%, 40rem); /* responsive max-width without @media! */ /* max() — picks largest value */ padding: max(1rem, 5%); /* at least 1rem, grows on wide screens */ /* clamp(min, ideal, max) — fluid with guardrails */ font-size: clamp(1rem, 2.5vw, 1.5rem); /* fluid type */ gap: clamp(0.5rem, 2vw, 2rem); /* fluid spacing */
14

Typography Deep Dive

📖 Type as Design Typography accounts for 95% of web design. CSS gives you incredible control: font loading, weight, tracking, leading, optical sizing, and even gradient text. Always use rem for font sizes so they respect user preferences.
font-weight: 100

The quick brown fox

font-weight: 300

The quick brown fox

font-weight: 400

The quick brown fox

font-weight: 700

The quick brown fox

letter-spacing: -0.05em

The quick brown fox

letter-spacing: 0.15em uppercase

The quick brown fox

line-height: 1 (tight)

The quick
brown fox

font-style: italic

The quick brown fox

gradient text

The quick brown fox

/* Typography properties */ font-family: 'Playfair Display', Georgia, serif; /* fallback chain */ font-size: 1.125rem; /* always use rem for accessibility */ font-weight: 700; /* 100-900 or bold/normal */ font-style: italic; /* normal | italic | oblique */ line-height: 1.6; /* unitless! scales with font-size */ letter-spacing: 0.05em; /* tracking — em scales with size */ word-spacing: 0.1em; /* between words */ text-transform: uppercase; /* capitalize | lowercase */ text-decoration: underline wavy var(--accent); text-underline-offset: 0.2em; text-indent: 2em; /* first line indent */ text-align: justify; /* left | center | right | justify */ text-wrap: balance; /* balances line lengths (modern!) */ /* GRADIENT TEXT technique */ .gradient-text { background: linear-gradient(90deg, #c94a1f, #2563a8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } /* @font-face — load custom fonts */ @font-face { font-family: 'MyFont'; src: url('font.woff2') format('woff2'); font-weight: 400 700; /* variable font range */ font-display: swap; /* show fallback until loaded */ }