CSS Visual Design: The Complete Beginner's Guide to Making Websites Look Professional
You built the skeleton in Module 1 — a semantic, accessible, three-page blog. Now it looks like a document from 1994. Plain black text, blue underlined links, zero personality. That's exactly where we want to be.
CSS is not about memorizing 500 properties. It's about understanding five systems that control everything visual on the web: the cascade, selectors, the box model, units, and the rendering pipeline. Once you grasp these, styling becomes predictable instead of guesswork.
This tutorial styles your Module 1 blog step by step. Open your my-blog folder. We're about to make it look like something you'd actually visit.
Part 1: How CSS Actually Works — The Cascade Explained
Before you write a single property, understand where styles come from and why one rule wins over another.
The Three Sources of Styles
Every browser applies styles from three sources, in this order:
-
Browser (User Agent) Stylesheet — Default styles. Headings are bold and large. Links are blue and underlined. Lists have bullets. These are the "ugly defaults" you're seeing now.
-
User Stylesheet — Styles set by the person viewing the page (rare today, but important for accessibility tools like high-contrast modes).
-
Author Stylesheet — Your CSS. This is you.
Your styles override browser defaults because of specificity and source order. But if two of your rules target the same element, the browser needs a tiebreaker. That's the cascade.
The Cascade in Plain English
When two rules conflict, the browser asks four questions in order:
- Origin & Importance — Does one rule use
!important? (Avoid this.) - Specificity — Which selector is more specific? (We'll calculate this.)
- Source Order — Which rule came last in the CSS file?
- Inheritance — Can the value be inherited from a parent?
Inheritance: The Shortcut You Already Use
Some properties automatically pass from parent to child. Text-related properties (color, font-family, line-height) inherit by default. Box-related properties (margin, padding, background) do not.
1body { 2 color: #333; /* All text inherits this */ 3 font-family: system-ui, sans-serif; 4} 5 6/* h1, p, a, li — they all get #333 text color automatically */ 7/* But divs don't inherit padding. You must set it explicitly. */
Pro Tip: Set your base
font-family,color, andline-heighton thebodyorhtmlelement. Let inheritance do the work for every child element.
Part 2: Selectors — Precision Without Chaos
Selectors are how you target HTML elements. Most beginners overuse classes and IDs because they don't know the full toolkit.
The Selector Arsenal
1/* Element selector — targets all instances */ 2p { 3 line-height: 1.6; 4} 5 6/* Class selector — reusable, specific enough */ 7.card { 8 background: #fff; 9 padding: 1.5rem; 10} 11 12/* ID selector — one per page, highest specificity */ 13#main-content { 14 max-width: 800px; 15 margin: 0 auto; 16} 17 18/* Attribute selector */ 19a[target="_blank"] { 20 /* External links only */ 21} 22 23input[type="email"] { 24 /* Email inputs only */ 25} 26 27/* Pseudo-classes — state-based */ 28a:hover { 29 color: #e74c3c; 30} 31 32button:focus { 33 outline: 3px solid #3498db; 34 outline-offset: 2px; 35} 36 37li:nth-child(odd) { 38 background: #f8f9fa; /* Zebra striping */ 39} 40 41/* Pseudo-elements — parts of elements */ 42h2::after { 43 content: ""; 44 display: block; 45 width: 60px; 46 height: 3px; 47 background: #3498db; 48 margin-top: 0.5rem; 49} 50 51blockquote::before { 52 content: """; 53 font-size: 4rem; 54 color: #ddd; 55 line-height: 0; 56}
Combinators: The Power Moves
1/* Descendant: any nav inside header, at any depth */ 2header nav { 3 display: flex; 4} 5 6/* Child: only direct children */ 7ul > li { 8 margin-bottom: 0.5rem; 9} 10 11/* Adjacent sibling: the p immediately after an h2 */ 12h2 + p { 13 font-size: 1.125rem; 14 color: #666; 15} 16 17/* General sibling: all p elements after h2, not just the first */ 18h2 ~ p { 19 margin-left: 1rem; 20}
Practical Example: Styling the Blog Navigation
1/* Target only the nav inside the header */ 2header nav ul { 3 list-style: none; /* Remove bullets */ 4 padding: 0; /* Remove default padding */ 5 display: flex; /* Horizontal layout */ 6 gap: 2rem; /* Space between items */ 7} 8 9header nav a { 10 text-decoration: none; 11 color: #2c3e50; 12 font-weight: 500; 13 padding: 0.5rem 0; 14 border-bottom: 2px solid transparent; 15 transition: border-color 0.2s; 16} 17 18header nav a:hover { 19 border-bottom-color: #3498db; 20} 21 22/* Highlight the current page */ 23header nav a[aria-current="page"] { 24 border-bottom-color: #2c3e50; 25 font-weight: 700; 26}
Part 3: Specificity — The Score That Decides Everything
When two rules target the same element, specificity determines the winner. It's not random. It's math.
How to Calculate Specificity
Think of specificity as a three-digit number: (ID, Class, Element)
| Selector | ID | Class | Element | Score |
|---|---|---|---|---|
p | 0 | 0 | 1 | 0-0-1 |
.card | 0 | 1 | 0 | 0-1-0 |
#main | 1 | 0 | 0 | 1-0-0 |
header nav a:hover | 0 | 1 | 2 | 0-1-2 |
#main .card p::first-line | 1 | 1 | 2 | 1-1-2 |
The browser compares left to right. A single ID beats any number of classes. A single class beats any number of elements.
The Specificity Death Spiral
1/* You start simple */ 2p { color: black; } 3 4/* Then you need to override it */ 5article p { color: #333; } 6 7/* Then that doesn't work because of specificity */ 8#main article p { color: #444; } 9 10/* Then you panic */ 11#main article p.important { color: #555 !important; }
Every time you increase specificity to win a fight, you make the next override harder. This is how CSS becomes unmaintainable.
The Fix: Keep Specificity Low and Flat
1/* Good: flat, low-specificity selectors */ 2.text-primary { color: #2c3e50; } 3.text-muted { color: #7f8c8d; } 4 5/* Good: use the cascade, not specificity wars */ 6article p { color: #333; } 7article p.lead { font-size: 1.25rem; } /* One class beats element selector cleanly */
The !important Trap
!important breaks the cascade. It says "this wins, period." The problem? Once you use it, everything that needs to override it also needs !important. You enter an arms race.
When !important is acceptable:
- Utility classes (
.hidden { display: none !important; }) - Overriding third-party library styles you can't modify
- Accessibility overrides (
.sr-onlyscreen reader text)
When it's not:
- Because you're too lazy to fix your specificity
- Because "it wasn't working and I got frustrated"
Golden Rule: If you reach for
!important, stop. Check your selector specificity first. 99% of the time, the fix is a cleaner selector, not a sledgehammer.
Part 4: The Box Model — Everything Is a Rectangle
Every HTML element is a box. Understanding this box is the single most important concept in CSS layout.
The Four Layers
┌─────────────────────────────┐
│ Margin │ ← Pushes other boxes away
│ ┌─────────────────────┐ │
│ │ Border │ │ ← Visible edge (optional)
│ │ ┌─────────────┐ │ │
│ │ │ Padding │ │ │ ← Space inside the border
│ │ │ ┌─────┐ │ │ │
│ │ │ │Content│ │ │ │ ← Your text, images, etc.
│ │ │ └─────┘ │ │ │
│ │ └─────────────┘ │ │
│ └─────────────────────┘ │
└─────────────────────────────┘
Default vs. box-sizing: border-box
By default, width and height apply only to the content. Padding and border are added on top. So a div with width: 300px, padding: 20px, and border: 2px actually takes up 344px of space.
This breaks layouts. Every. Single. Time.
1/* The fix: apply to everything */ 2*, 3*::before, 4*::after { 5 box-sizing: border-box; 6} 7 8/* Now width includes padding and border */ 9.card { 10 width: 300px; 11 padding: 20px; 12 border: 2px solid #ddd; 13 /* Total width is exactly 300px. Content area is 256px. */ 14}
Always start your CSS with this reset. It's the most copied snippet in CSS history for a reason.
Practical Box Model for the Blog
1/* Reset */ 2*, 3*::before, 4*::after { 5 box-sizing: border-box; 6 margin: 0; 7 padding: 0; 8} 9 10/* Base styles */ 11html { 12 font-size: 16px; /* 1rem = 16px */ 13} 14 15body { 16 font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 17 line-height: 1.6; 18 color: #2c3e50; 19 background-color: #f5f7fa; 20} 21 22/* Container for centered content */ 23.container { 24 max-width: 800px; 25 margin: 0 auto; /* Center horizontally */ 26 padding: 0 1.5rem; /* Breathing room on mobile */ 27} 28 29/* Header styling */ 30header { 31 background: #fff; 32 border-bottom: 1px solid #e1e8ed; 33 padding: 1.5rem 0; 34 margin-bottom: 2rem; 35} 36 37header h1 { 38 font-size: 1.75rem; 39 margin-bottom: 0.25rem; 40} 41 42header p { 43 color: #7f8c8d; 44 font-size: 0.95rem; 45} 46 47/* Navigation */ 48header nav { 49 margin-top: 1rem; 50} 51 52header nav ul { 53 list-style: none; 54 display: flex; 55 gap: 1.5rem; 56} 57 58header nav a { 59 text-decoration: none; 60 color: #34495e; 61 font-weight: 500; 62 font-size: 0.95rem; 63 padding: 0.25rem 0; 64 border-bottom: 2px solid transparent; 65 transition: all 0.2s ease; 66} 67 68header nav a:hover, 69header nav a[aria-current="page"] { 70 color: #2980b9; 71 border-bottom-color: #2980b9; 72} 73 74/* Main content area */ 75main { 76 background: #fff; 77 padding: 2.5rem; 78 border-radius: 8px; 79 box-shadow: 0 1px 3px rgba(0,0,0,0.1); 80 margin-bottom: 2rem; 81} 82 83/* Article cards on homepage */ 84article { 85 padding: 1.5rem 0; 86 border-bottom: 1px solid #ecf0f1; 87} 88 89article:last-child { 90 border-bottom: none; 91} 92 93article h3 { 94 font-size: 1.35rem; 95 margin-bottom: 0.5rem; 96} 97 98article h3 a { 99 color: #2c3e50; 100 text-decoration: none; 101} 102 103article h3 a:hover { 104 color: #2980b9; 105} 106 107article header p { 108 font-size: 0.875rem; 109 color: #95a5a6; 110 margin-bottom: 0.75rem; 111} 112 113article p { 114 color: #555; 115 margin-bottom: 1rem; 116} 117 118/* "Read more" link */ 119article > a { 120 color: #2980b9; 121 text-decoration: none; 122 font-weight: 500; 123 font-size: 0.9rem; 124} 125 126article > a:hover { 127 text-decoration: underline; 128} 129 130/* Footer */ 131footer { 132 text-align: center; 133 padding: 2rem 0; 134 color: #95a5a6; 135 font-size: 0.875rem; 136} 137 138footer a { 139 color: #7f8c8d; 140 text-decoration: none; 141 margin: 0 0.5rem; 142} 143 144footer a:hover { 145 color: #2980b9; 146}
Part 5: Modern CSS Units — Stop Guessing, Start Measuring
Pixels (px) are fine for borders. For everything else, modern units make your site responsive by default.
The Unit Guide
1html { 2 font-size: 16px; /* Base size. 1rem = 16px */ 3} 4 5/* rem — Relative to root font-size */ 6h1 { font-size: 2.5rem; } /* 40px */ 7h2 { font-size: 2rem; } /* 32px */ 8p { font-size: 1rem; } /* 16px */ 9 10/* em — Relative to parent font-size */ 11button { 12 font-size: 1rem; 13 padding: 0.5em 1.5em; /* Scales with button text size */ 14} 15 16/* Viewport units */ 17.hero { 18 height: 50vh; /* 50% of viewport height */ 19 padding: 5vw; /* 5% of viewport width */ 20} 21 22/* vmin / vmax — responsive to orientation */ 23.icon { 24 width: 10vmin; /* 10% of the smaller dimension */ 25} 26 27/* Container query units (new in 2026) */ 28@container (min-width: 400px) { 29 .card { 30 padding: 5cqw; /* 5% of container width */ 31 } 32}
When to Use What
| Unit | Use For | Avoid For |
|---|---|---|
rem | Font sizes, margins, padding that should scale with user preferences | Nothing — it's your default |
em | Component-internal spacing (buttons, badges) that should scale with the component's text | Layout widths (compounding gets confusing) |
px | Borders, hairlines, box-shadows | Font sizes (breaks user zoom preferences) |
vw/vh | Hero sections, full-bleed layouts | Font sizes alone (causes zoom issues) |
% | Widths inside containers | Font sizes (use rem instead) |
Accessibility Note: Users can change their browser's default font size (often to 18px or 20px for readability). If you set
font-size: 16pxon yourbody, you override their preference. Setfont-size: 100%or1remonhtmlto respect user settings.
Part 6: Colors — Beyond Hex Codes
Hex colors (#3498db) are compact but human-unreadable. Modern CSS gives you better tools.
Color Formats Compared
1/* Hex — compact, opaque */ 2.primary { color: #3498db; } 3.primary-alpha { color: #3498db80; } /* 50% opacity (hex + alpha) */ 4 5/* RGB — explicit, good for variables */ 6.primary { color: rgb(52, 152, 219); } 7.primary-faded { color: rgba(52, 152, 219, 0.5); } 8 9/* HSL — human-readable. Hue, Saturation, Lightness */ 10.primary { color: hsl(204, 70%, 53%); } 11.primary-light { color: hsl(204, 70%, 75%); } /* Same hue, lighter */ 12.primary-dark { color: hsl(204, 70%, 35%); } /* Same hue, darker */ 13 14/* OKLCH — perceptually uniform, the future */ 15.primary { color: oklch(62% 0.15 245); } 16.primary-light { color: oklch(75% 0.12 245); } /* Actually looks lighter */ 17 18/* color-mix — blend two colors in any space */ 19.mixed { 20 background: color-mix(in oklch, #3498db 70%, #2ecc71 30%); 21}
Building a Color System for the Blog
1:root { 2 /* Primary palette */ 3 --color-primary: #2980b9; 4 --color-primary-dark: #1f618d; 5 --color-primary-light: #5dade2; 6 7 /* Neutral palette */ 8 --color-text: #2c3e50; 9 --color-text-muted: #7f8c8d; 10 --color-bg: #f5f7fa; 11 --color-surface: #ffffff; 12 --color-border: #e1e8ed; 13 14 /* Semantic colors */ 15 --color-success: #27ae60; 16 --color-warning: #f39c12; 17 --color-error: #e74c3c; 18 19 /* Alpha variations */ 20 --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1); 21 --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); 22} 23 24body { 25 background-color: var(--color-bg); 26 color: var(--color-text); 27} 28 29main { 30 background-color: var(--color-surface); 31 box-shadow: var(--shadow-sm); 32} 33 34a { 35 color: var(--color-primary); 36} 37 38a:hover { 39 color: var(--color-primary-dark); 40}
Why CSS Variables? Define once, use everywhere. Change
--color-primaryin one place and your entire site updates. This is how professional design systems work.
Part 7: Typography — The 95% of Design
Most of what makes a site look "professional" isn't colors or animations. It's typography.
The Complete Type System
1/* Load a web font */ 2@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); 3 4:root { 5 --font-body: 'Inter', system-ui, -apple-system, sans-serif; 6 --font-heading: 'Inter', system-ui, sans-serif; 7 8 /* Fluid type scale using clamp() */ 9 --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem); 10 --text-sm: clamp(0.875rem, 0.8rem + 0.35vw, 1rem); 11 --text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); 12 --text-lg: clamp(1.125rem, 1rem + 0.65vw, 1.35rem); 13 --text-xl: clamp(1.35rem, 1.1rem + 1.2vw, 1.75rem); 14 --text-2xl: clamp(1.75rem, 1.3rem + 2.2vw, 2.5rem); 15} 16 17html { 18 font-size: 100%; /* Respects user's browser preference */ 19} 20 21body { 22 font-family: var(--font-body); 23 font-size: var(--text-base); 24 line-height: 1.6; 25 color: var(--color-text); 26} 27 28/* Headings */ 29h1 { font-size: var(--text-2xl); font-weight: 700; line-height: 1.2; margin-bottom: 1rem; } 30h2 { font-size: var(--text-xl); font-weight: 600; line-height: 1.3; margin: 2rem 0 1rem; } 31h3 { font-size: var(--text-lg); font-weight: 600; line-height: 1.4; margin: 1.5rem 0 0.75rem; } 32 33/* Decorative underline for h2 */ 34h2::after { 35 content: ""; 36 display: block; 37 width: 50px; 38 height: 3px; 39 background: var(--color-primary); 40 margin-top: 0.5rem; 41} 42 43/* Body text */ 44p { 45 margin-bottom: 1.25rem; 46 max-width: 65ch; /* Optimal reading width */ 47} 48 49/* Lead paragraph (first paragraph after h2) */ 50h2 + p { 51 font-size: var(--text-lg); 52 color: var(--color-text-muted); 53 line-height: 1.5; 54} 55 56/* Lists */ 57ul, ol { 58 margin-bottom: 1.25rem; 59 padding-left: 1.5rem; 60} 61 62li { 63 margin-bottom: 0.5rem; 64} 65 66/* Links */ 67a { 68 color: var(--color-primary); 69 text-decoration: underline; 70 text-decoration-color: transparent; 71 text-underline-offset: 0.15em; 72 transition: text-decoration-color 0.2s; 73} 74 75a:hover { 76 text-decoration-color: var(--color-primary); 77} 78 79/* Code and technical text */ 80code { 81 font-family: 'SF Mono', Monaco, monospace; 82 font-size: 0.9em; 83 background: #f4f4f5; 84 padding: 0.15em 0.4em; 85 border-radius: 4px; 86}
Understanding clamp() for Fluid Typography
1font-size: clamp(1rem, 0.9rem + 0.5vw, 1.25rem); 2/* min preferred max */
- Minimum:
1rem(16px) — never smaller than this - Preferred:
0.9rem + 0.5vw— scales with viewport width - Maximum:
1.25rem(20px) — never larger than this
No media queries needed. The text grows smoothly between your minimum and maximum breakpoints.
Line Length Matters
1p, li { 2 max-width: 65ch; /* 65 characters wide */ 3}
The human eye struggles to track lines longer than 75 characters. Capping paragraph width at 65ch creates comfortable reading — the same reason books and newspapers use narrow columns.
Part 8: Backgrounds, Borders, and Effects
Gradients That Don't Look Like 2012
1/* Subtle background gradient */ 2body { 3 background: linear-gradient(135deg, #f5f7fa 0%, #e4e8ec 100%); 4 min-height: 100vh; 5} 6 7/* Card hover effect */ 8article { 9 background: #fff; 10 border-radius: 8px; 11 padding: 1.5rem; 12 border: 1px solid var(--color-border); 13 transition: transform 0.2s, box-shadow 0.2s; 14} 15 16article:hover { 17 transform: translateY(-2px); 18 box-shadow: var(--shadow-md); 19} 20 21/* Hero section with overlay */ 22.hero { 23 background: 24 linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), 25 url('hero-image.jpg') center/cover no-repeat; 26 color: #fff; 27 padding: 5rem 2rem; 28 text-align: center; 29} 30 31/* Focus states that aren't ugly */ 32a:focus-visible, 33button:focus-visible { 34 outline: 3px solid var(--color-primary); 35 outline-offset: 3px; 36 border-radius: 2px; 37} 38 39/* Skip link styling */ 40.skip-link { 41 position: absolute; 42 top: -40px; 43 left: 0; 44 background: var(--color-primary); 45 color: #fff; 46 padding: 0.5rem 1rem; 47 z-index: 100; 48 transition: top 0.2s; 49} 50 51.skip-link:focus { 52 top: 0; 53}
Multiple Backgrounds
1.feature-card { 2 background: 3 linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.8) 100%), 4 url('card-bg.jpg') center/cover no-repeat; 5 padding: 2rem; 6 border-radius: 12px; 7 color: #fff; 8}
Part 9: The Complete Styled Blog — Putting It All Together
Here's the full styles.css file for your blog. Save it in your my-blog folder and link it in each HTML file: <link rel="stylesheet" href="styles.css">
1/* ============================================ 2 CSS RESET & BASE 3 ============================================ */ 4*, 5*::before, 6*::after { 7 box-sizing: border-box; 8 margin: 0; 9 padding: 0; 10} 11 12:root { 13 /* Colors */ 14 --color-primary: #2980b9; 15 --color-primary-dark: #1f618d; 16 --color-primary-light: #5dade2; 17 --color-text: #2c3e50; 18 --color-text-muted: #7f8c8d; 19 --color-bg: #f5f7fa; 20 --color-surface: #ffffff; 21 --color-border: #e1e8ed; 22 23 /* Typography */ 24 --font-body: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 25 --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem); 26 --text-sm: clamp(0.875rem, 0.8rem + 0.35vw, 1rem); 27 --text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); 28 --text-lg: clamp(1.125rem, 1rem + 0.65vw, 1.35rem); 29 --text-xl: clamp(1.35rem, 1.1rem + 1.2vw, 1.75rem); 30 --text-2xl: clamp(1.75rem, 1.3rem + 2.2vw, 2.5rem); 31 32 /* Spacing */ 33 --space-xs: 0.5rem; 34 --space-sm: 1rem; 35 --space-md: 1.5rem; 36 --space-lg: 2.5rem; 37 --space-xl: 4rem; 38 39 /* Shadows */ 40 --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08); 41 --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1); 42} 43 44html { 45 font-size: 100%; 46 scroll-behavior: smooth; 47} 48 49body { 50 font-family: var(--font-body); 51 font-size: var(--text-base); 52 line-height: 1.6; 53 color: var(--color-text); 54 background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%); 55 min-height: 100vh; 56} 57 58/* ============================================ 59 UTILITIES 60 ============================================ */ 61.container { 62 max-width: 800px; 63 margin: 0 auto; 64 padding: 0 var(--space-md); 65} 66 67/* ============================================ 68 SKIP LINK 69 ============================================ */ 70.skip-link { 71 position: absolute; 72 top: -50px; 73 left: 0; 74 background: var(--color-primary); 75 color: #fff; 76 padding: var(--space-xs) var(--space-md); 77 z-index: 1000; 78 text-decoration: none; 79 font-weight: 500; 80 border-radius: 0 0 4px 0; 81 transition: top 0.25s ease; 82} 83 84.skip-link:focus { 85 top: 0; 86} 87 88/* ============================================ 89 HEADER & NAVIGATION 90 ============================================ */ 91header { 92 background: var(--color-surface); 93 border-bottom: 1px solid var(--color-border); 94 padding: var(--space-md) 0; 95 position: sticky; 96 top: 0; 97 z-index: 100; 98 box-shadow: var(--shadow-sm); 99} 100 101header .container { 102 display: flex; 103 flex-wrap: wrap; 104 align-items: baseline; 105 gap: var(--space-xs) var(--space-lg); 106} 107 108header h1 { 109 font-size: var(--text-xl); 110 font-weight: 700; 111 line-height: 1.2; 112} 113 114header h1 a { 115 color: var(--color-text); 116 text-decoration: none; 117} 118 119header > .container > p { 120 color: var(--color-text-muted); 121 font-size: var(--text-sm); 122 flex-basis: 100%; 123 margin: 0; 124} 125 126header nav { 127 margin-left: auto; 128} 129 130header nav ul { 131 list-style: none; 132 display: flex; 133 gap: var(--space-lg); 134} 135 136header nav a { 137 color: var(--color-text-muted); 138 text-decoration: none; 139 font-weight: 500; 140 font-size: var(--text-sm); 141 padding: var(--space-xs) 0; 142 border-bottom: 2px solid transparent; 143 transition: all 0.2s ease; 144} 145 146header nav a:hover, 147header nav a[aria-current="page"] { 148 color: var(--color-primary); 149 border-bottom-color: var(--color-primary); 150} 151 152/* ============================================ 153 MAIN CONTENT 154 ============================================ */ 155main { 156 background: var(--color-surface); 157 padding: var(--space-lg); 158 border-radius: 12px; 159 box-shadow: var(--shadow-sm); 160 margin: var(--space-lg) auto; 161} 162 163/* ============================================ 164 TYPOGRAPHY 165 ============================================ */ 166h2 { 167 font-size: var(--text-xl); 168 font-weight: 600; 169 line-height: 1.3; 170 margin: var(--space-lg) 0 var(--space-md); 171} 172 173h2:first-child { 174 margin-top: 0; 175} 176 177h2::after { 178 content: ""; 179 display: block; 180 width: 50px; 181 height: 3px; 182 background: var(--color-primary); 183 margin-top: var(--space-xs); 184 border-radius: 2px; 185} 186 187h3 { 188 font-size: var(--text-lg); 189 font-weight: 600; 190 line-height: 1.4; 191 margin: var(--space-md) 0 var(--space-sm); 192} 193 194p { 195 margin-bottom: var(--space-md); 196 max-width: 65ch; 197} 198 199h2 + p { 200 font-size: var(--text-lg); 201 color: var(--color-text-muted); 202 line-height: 1.5; 203} 204 205ul, ol { 206 margin-bottom: var(--space-md); 207 padding-left: 1.5rem; 208} 209 210li { 211 margin-bottom: var(--space-xs); 212} 213 214a { 215 color: var(--color-primary); 216 text-decoration: underline; 217 text-decoration-color: transparent; 218 text-underline-offset: 0.15em; 219 transition: text-decoration-color 0.2s; 220} 221 222a:hover { 223 text-decoration-color: var(--color-primary); 224} 225 226/* ============================================ 227 ARTICLE CARDS (Homepage) 228 ============================================ */ 229article { 230 padding: var(--space-md) 0; 231 border-bottom: 1px solid var(--color-border); 232} 233 234article:last-child { 235 border-bottom: none; 236} 237 238article header { 239 position: static; 240 background: none; 241 border: none; 242 box-shadow: none; 243 padding: 0; 244 margin-bottom: var(--space-sm); 245} 246 247article header p { 248 font-size: var(--text-xs); 249 color: var(--color-text-muted); 250 margin: var(--space-xs) 0 0; 251} 252 253article h3 { 254 margin: 0; 255} 256 257article h3 a { 258 color: var(--color-text); 259 text-decoration: none; 260 transition: color 0.2s; 261} 262 263article h3 a:hover { 264 color: var(--color-primary); 265 text-decoration: none; 266} 267 268article > p { 269 color: #555; 270 margin-bottom: var(--space-sm); 271} 272 273article > a { 274 display: inline-flex; 275 align-items: center; 276 gap: 0.25rem; 277 font-size: var(--text-sm); 278 font-weight: 500; 279 text-decoration: none; 280} 281 282article > a::after { 283 content: "→"; 284 transition: transform 0.2s; 285} 286 287article > a:hover::after { 288 transform: translateX(4px); 289} 290 291/* ============================================ 292 ABOUT PAGE 293 ============================================ */ 294article img { 295 border-radius: 50%; 296 margin-bottom: var(--space-md); 297 border: 3px solid var(--color-border); 298} 299 300dl { 301 display: grid; 302 grid-template-columns: 120px 1fr; 303 gap: var(--space-xs) var(--space-md); 304 margin: var(--space-md) 0; 305} 306 307dt { 308 font-weight: 600; 309 color: var(--color-text-muted); 310 font-size: var(--text-sm); 311} 312 313dd { 314 margin: 0; 315} 316 317/* ============================================ 318 CONTACT FORM 319 ============================================ */ 320form { 321 margin-top: var(--space-lg); 322} 323 324fieldset { 325 border: 1px solid var(--color-border); 326 border-radius: 8px; 327 padding: var(--space-md); 328 margin-bottom: var(--space-md); 329} 330 331legend { 332 font-weight: 600; 333 padding: 0 var(--space-xs); 334 color: var(--color-text-muted); 335 font-size: var(--text-sm); 336} 337 338form div { 339 margin-bottom: var(--space-md); 340} 341 342form div:last-child { 343 margin-bottom: 0; 344} 345 346label { 347 display: block; 348 font-size: var(--text-sm); 349 font-weight: 500; 350 margin-bottom: var(--space-xs); 351 color: var(--color-text); 352} 353 354input[type="text"], 355input[type="email"], 356input[type="tel"], 357select, 358textarea { 359 width: 100%; 360 padding: 0.75rem 1rem; 361 border: 1px solid var(--color-border); 362 border-radius: 6px; 363 font-family: inherit; 364 font-size: var(--text-base); 365 color: var(--color-text); 366 background: #fafbfc; 367 transition: border-color 0.2s, box-shadow 0.2s; 368} 369 370input:focus, 371select:focus, 372textarea:focus { 373 outline: none; 374 border-color: var(--color-primary); 375 box-shadow: 0 0 0 3px rgba(41, 128, 185, 0.15); 376 background: var(--color-surface); 377} 378 379input::placeholder, 380textarea::placeholder { 381 color: #b0b8c1; 382} 383 384small { 385 display: block; 386 color: var(--color-text-muted); 387 font-size: var(--text-xs); 388 margin-top: 0.25rem; 389} 390 391/* Radio and checkbox styling */ 392input[type="radio"], 393input[type="checkbox"] { 394 margin-right: 0.5rem; 395 accent-color: var(--color-primary); 396} 397 398fieldset div:has(input[type="radio"]), 399fieldset div:has(input[type="checkbox"]) { 400 display: flex; 401 align-items: center; 402 margin-bottom: var(--space-xs); 403} 404 405fieldset div:has(input[type="radio"]) label, 406fieldset div:has(input[type="checkbox"]) label { 407 margin: 0; 408 font-weight: 400; 409} 410 411/* Buttons */ 412button[type="submit"] { 413 background: var(--color-primary); 414 color: #fff; 415 border: none; 416 padding: 0.875rem 2rem; 417 font-size: var(--text-base); 418 font-weight: 600; 419 border-radius: 6px; 420 cursor: pointer; 421 transition: background 0.2s, transform 0.1s; 422} 423 424button[type="submit"]:hover { 425 background: var(--color-primary-dark); 426} 427 428button[type="submit"]:active { 429 transform: scale(0.98); 430} 431 432button[type="reset"] { 433 background: transparent; 434 color: var(--color-text-muted); 435 border: 1px solid var(--color-border); 436 padding: 0.875rem 1.5rem; 437 font-size: var(--text-base); 438 border-radius: 6px; 439 cursor: pointer; 440 margin-left: var(--space-sm); 441 transition: all 0.2s; 442} 443 444button[type="reset"]:hover { 445 background: #f4f4f5; 446 color: var(--color-text); 447} 448 449/* ============================================ 450 ASIDE (Sidebar) 451 ============================================ */ 452aside { 453 background: #f8f9fa; 454 padding: var(--space-md); 455 border-radius: 8px; 456 margin-top: var(--space-lg); 457 border-left: 3px solid var(--color-primary); 458} 459 460aside h3 { 461 font-size: var(--text-base); 462 margin-top: 0; 463} 464 465aside ul { 466 list-style: none; 467 padding: 0; 468} 469 470aside li { 471 margin-bottom: var(--space-xs); 472} 473 474aside a { 475 font-size: var(--text-sm); 476} 477 478/* ============================================ 479 FOOTER 480 ============================================ */ 481footer { 482 text-align: center; 483 padding: var(--space-lg) 0; 484 color: var(--color-text-muted); 485 font-size: var(--text-sm); 486} 487 488footer p { 489 margin: 0 auto var(--space-xs); 490 max-width: none; 491} 492 493footer a { 494 color: var(--color-text-muted); 495 text-decoration: none; 496 margin: 0 var(--space-xs); 497} 498 499footer a:hover { 500 color: var(--color-primary); 501} 502 503/* ============================================ 504 RESPONSIVE ADJUSTMENTS 505 ============================================ */ 506@media (max-width: 600px) { 507 header .container { 508 flex-direction: column; 509 align-items: flex-start; 510 gap: var(--space-sm); 511 } 512 513 header nav { 514 margin-left: 0; 515 } 516 517 header nav ul { 518 gap: var(--space-md); 519 } 520 521 main { 522 padding: var(--space-md); 523 border-radius: 0; 524 margin: var(--space-sm) 0; 525 } 526 527 dl { 528 grid-template-columns: 1fr; 529 gap: 0; 530 } 531 532 dt { 533 margin-top: var(--space-sm); 534 } 535 536 button[type="reset"] { 537 display: block; 538 margin: var(--space-sm) 0 0; 539 width: 100%; 540 } 541}
Part 10: Testing Your Styled Blog
1. The Naked Test (Again)
Disable your CSS. Your HTML should still make perfect sense. If it doesn't, fix the HTML — never rely on CSS to create meaning.
2. The Resize Test
Drag your browser window from full-width to 320px wide. Nothing should break. Text shouldn't overflow. Buttons shouldn't become unreadable.
3. The Focus Test
Press Tab through your entire page. Every interactive element should have a visible focus ring. If you can't see where you are, add :focus-visible styles.
4. The Color Contrast Test
Use the WebAIM contrast checker. Your text should have at least a 4.5:1 contrast ratio against its background. Our --color-text on --color-surface hits 10.8:1 — well above the minimum.
Key Takeaways
| Concept | What It Means |
|---|---|
| Cascade | Origin → Specificity → Source Order → Inheritance |
| Specificity | (ID, Class, Element) — keep it low and flat |
| Box Model | Content + Padding + Border + Margin; use border-box |
| Units | rem for fonts, em for component spacing, px for borders |
| Colors | HSL for humans, OKLCH for the future, CSS variables for systems |
| Typography | clamp() for fluid type, max-width: 65ch for readability |
| Focus | Never remove outlines; style them to match your design |
What's Next?
You now have a fully styled, responsive blog. But the layout is still basic — single column, stacked elements. In Module 3, we dive into Layout Engineering: Flexbox, CSS Grid, positioning, and Container Queries. We'll transform this blog into a multi-column magazine layout with a sidebar, card grids, and sticky navigation that actually behaves.
Save your work. In the next module, we stop "styling elements" and start "engineering layouts."
Quick Reference Cheat Sheet
1/* Specificity scores */ 2p /* 0-0-1 */ 3.card /* 0-1-0 */ 4#nav /* 1-0-0 */ 5#nav .item /* 1-1-0 */ 6 7/* Box model reset */ 8*, *::before, *::after { box-sizing: border-box; } 9 10/* Fluid type */ 11font-size: clamp(1rem, 0.9rem + 0.5vw, 1.25rem); 12 13/* Color formats */ 14color: #3498db; 15color: hsl(204, 70%, 53%); 16color: oklch(62% 0.15 245); 17color: color-mix(in oklch, blue 70%, green 30%); 18 19/* Focus that works */ 20:focus-visible { outline: 3px solid blue; outline-offset: 2px; } 21 22/* Sticky header */ 23header { position: sticky; top: 0; z-index: 100; }
Your blog now looks professional. But layout is where CSS gets truly powerful. Module 3 is where everything clicks.