🚀 The Grand Finale
Congratulations — you've made it to the capstone! Over the past 11 modules, you've built a complete toolkit: semantic HTML, CSS styling & layout, Flexbox & Grid, responsive design, animations, accessibility, and SEO. Now it's time to put it ALL together in one real project.
In this module, you'll build a multi-page responsive portfolio website from scratch — no templates, no frameworks, just pure HTML & CSS — and deploy it live on the internet using GitHub Pages. By the end, you'll have a real URL you can share with anyone.
Skills You'll Apply
| Module | Skill Applied |
|---|---|
| 0–1 | HTML fundamentals, page structure, semantic tags |
| 2–3 | Advanced HTML, forms, tables |
| 4 | Images, multimedia, embedding |
| 5–6 | CSS styling, box model, typography |
| 7–8 | Flexbox & Grid layouts |
| 9 | Responsive design & media queries |
| 10 | Transitions, animations, transforms |
| 11 | Accessibility (ARIA, keyboard nav) & SEO |
📋 Project Planning
Every professional project starts with a plan. Before writing a single line of code, let's define what we're building and how it will be structured.
Page Structure
Our portfolio will have 4 pages, each with a shared header/footer and its own unique content:
Hero section with your name & tagline, featured projects grid, call-to-action.
Bio, skills list, education/experience timeline.
Project cards grid with images, descriptions, and links.
Contact form (styled, accessible), social links, location info.
File Structure
Organize your project with a clean, standard folder structure:
my-portfolio/
├── index.html # Home page
├── about.html # About page
├── projects.html # Projects page
├── contact.html # Contact page
├── css/
│ ├── style.css # Main stylesheet
│ └── responsive.css # Media queries
├── images/
│ ├── profile.jpg # Your photo
│ ├── project-1.jpg # Project screenshots
│ ├── project-2.jpg
│ └── favicon.ico # Browser tab icon
└── README.md # Project description
Wireframe Your Pages
Before coding, sketch a rough layout for each page. You don't need fancy tools — pen and paper works perfectly. Focus on:
Where does the header, main content, and footer go? How many columns?
How will the layout change on mobile vs. tablet vs. desktop?
Pick 2–3 primary colors and a neutral. Use CSS custom properties for consistency.
⚙️ Setting Up the Project
Let's create the project folder and set up the initial files. Open your terminal (or VS Code's built-in terminal) and run:
# Create project folder and navigate into it
mkdir my-portfolio
cd my-portfolio
# Create subfolders
mkdir css images
# Create initial files
touch index.html about.html projects.html contact.html
touch css/style.css css/responsive.css
touch README.md
HTML Boilerplate
Start each HTML file with this boilerplate. Customize the <title> and <meta> tags for each page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Your Name — Portfolio</title>
<meta name="description" content="Portfolio of [Your Name] — web developer">
<link rel="icon" href="images/favicon.ico">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/responsive.css">
</head>
<body>
<!-- Shared header (same on all pages) -->
<header class="site-header">
<!-- Navigation goes here -->
</header>
<!-- Page-specific content -->
<main>
<!-- Unique content per page -->
</main>
<!-- Shared footer (same on all pages) -->
<footer class="site-footer">
<!-- Footer content -->
</footer>
</body>
</html>
CSS Custom Properties
Set up a design system with CSS variables at the top of your style.css. This makes your entire site easy to re-theme:
/* ===== Design Tokens ===== */
:root {
/* Colors */
--color-primary: #2563eb;
--color-primary-dark: #1d4ed8;
--color-accent: #f59e0b;
--color-text: #1f2937;
--color-text-light: #6b7280;
--color-bg: #ffffff;
--color-bg-alt: #f9fafb;
--color-border: #e5e7eb;
/* Typography */
--font-heading: 'Playfair Display', serif;
--font-body: 'Inter', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
/* Spacing */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 2rem;
--space-xl: 4rem;
/* Transitions */
--transition-fast: 150ms ease;
--transition-base: 300ms ease;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.15);
/* Border Radius */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 16px;
--radius-full: 9999px;
}
/* ===== Reset ===== */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-body);
color: var(--color-text);
background: var(--color-bg);
line-height: 1.6;
}
🏗️ Building the HTML Structure
Let's build the HTML for each page. Remember: semantic HTML first, styling later. Use proper elements like <header>, <nav>, <main>, <section>, <article>, and <footer>.
Shared Navigation
The navigation will be identical on all 4 pages. Include a logo/name, nav links, and a mobile hamburger button:
<header class="site-header">
<nav class="navbar" aria-label="Main navigation">
<a href="index.html" class="logo">Your Name</a>
<ul class="nav-links">
<li><a href="index.html" class="nav-link active">Home</a></li>
<li><a href="about.html" class="nav-link">About</a></li>
<li><a href="projects.html" class="nav-link">Projects</a></li>
<li><a href="contact.html" class="nav-link">Contact</a></li>
</ul>
<button class="menu-toggle" aria-label="Toggle menu" aria-expanded="false">
<span class="hamburger"></span>
</button>
</nav>
</header>
Home Page — Hero Section
The hero is the first thing visitors see. Make it bold and clear:
<section class="hero">
<div class="hero-content">
<h1 class="hero-title">
Hi, I'm <span class="highlight">Your Name</span>
</h1>
<p class="hero-subtitle">Web Developer & Designer</p>
<div class="hero-cta">
<a href="projects.html" class="btn btn-primary">View My Work</a>
<a href="contact.html" class="btn btn-outline">Get in Touch</a>
</div>
</div>
</section>
Projects Page — Card Grid
Each project gets a card with an image, title, description, tech tags, and a link. This is where you use <article> semantics:
<section class="projects-grid">
<h2>My Projects</h2>
<div class="grid">
<article class="project-card">
<img src="images/project-1.jpg"
alt="Screenshot of weather app"
class="project-image"
loading="lazy">
<div class="project-info">
<h3>Weather App</h3>
<p>A responsive weather dashboard with location search.</p>
<div class="project-tags">
<span class="tag">HTML</span>
<span class="tag">CSS</span>
<span class="tag">Responsive</span>
</div>
<a href="#" class="btn btn-small">View Project →</a>
</div>
</article>
<!-- Repeat for more projects -->
</div>
</section>
Contact Page — Accessible Form
Apply everything from Module 3 (forms) and Module 11 (accessibility). Every input needs a <label>, and the form needs proper aria attributes:
<section class="contact-section">
<h2>Get in Touch</h2>
<form class="contact-form" action="#" method="POST">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name"
required autocomplete="name">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email"
required autocomplete="email">
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message"
rows="5" required></textarea>
</div>
<button type="submit" class="btn btn-primary">
Send Message
</button>
</form>
</section>
Shared Footer
<footer class="site-footer">
<div class="footer-content">
<p>© 2026 Your Name. All rights reserved.</p>
<div class="social-links">
<a href="#" aria-label="GitHub">GitHub</a>
<a href="#" aria-label="LinkedIn">LinkedIn</a>
<a href="mailto:you@email.com" aria-label="Email">Email</a>
</div>
</div>
</footer>
🎨 Base Styles & Typography
With the HTML structure in place, let's add the base styles. We already defined custom properties — now let's build the component styles on top of them.
Navigation Styles
/* ===== Navigation ===== */
.site-header {
position: sticky;
top: 0;
z-index: 100;
background: var(--color-bg);
border-bottom: 1px solid var(--color-border);
}
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
padding: var(--space-md) var(--space-lg);
}
.logo {
font-family: var(--font-heading);
font-size: 1.5rem;
font-weight: 700;
color: var(--color-primary);
text-decoration: none;
}
.nav-links {
display: flex;
gap: var(--space-lg);
list-style: none;
}
.nav-link {
text-decoration: none;
color: var(--color-text-light);
font-weight: 500;
transition: color var(--transition-fast);
}
.nav-link:hover,
.nav-link.active {
color: var(--color-primary);
}
Hero Section
/* ===== Hero ===== */
.hero {
min-height: 80vh;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: var(--space-xl);
}
.hero-title {
font-family: var(--font-heading);
font-size: clamp(2rem, 5vw, 4rem);
margin-bottom: var(--space-md);
}
.hero-title .highlight {
color: var(--color-primary);
}
.hero-subtitle {
font-size: clamp(1rem, 2.5vw, 1.5rem);
color: var(--color-text-light);
margin-bottom: var(--space-lg);
}
/* ===== Buttons ===== */
.btn {
display: inline-block;
padding: var(--space-sm) var(--space-lg);
border-radius: var(--radius-md);
font-weight: 600;
text-decoration: none;
transition: all var(--transition-base);
cursor: pointer;
border: 2px solid transparent;
}
.btn-primary {
background: var(--color-primary);
color: white;
}
.btn-primary:hover {
background: var(--color-primary-dark);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.btn-outline {
border-color: var(--color-primary);
color: var(--color-primary);
}
.btn-outline:hover {
background: var(--color-primary);
color: white;
}
Project Cards
/* ===== Project Cards ===== */
.project-card {
background: var(--color-bg);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-sm);
transition: transform var(--transition-base), box-shadow var(--transition-base);
}
.project-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
}
.project-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.project-info {
padding: var(--space-lg);
}
.project-tags {
display: flex;
gap: var(--space-xs);
flex-wrap: wrap;
margin: var(--space-sm) 0;
}
.tag {
background: var(--color-bg-alt);
padding: var(--space-xs) var(--space-sm);
border-radius: var(--radius-full);
font-size: 0.8rem;
color: var(--color-text-light);
}
📱 Responsive Layout
Now let's make everything responsive using CSS Grid and Flexbox. Remember: mobile-first — start with the single-column layout and add complexity at larger breakpoints.
Projects Grid with CSS Grid
/* Mobile-first: single column */
.grid {
display: grid;
gap: var(--space-lg);
grid-template-columns: 1fr;
}
/* Tablet: 2 columns */
@media (min-width: 768px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop: 3 columns */
@media (min-width: 1024px) {
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
Mobile Navigation
On mobile, the nav links collapse into a hamburger menu. This is a CSS-only approach using a checkbox hack (no JavaScript needed!):
/* === responsive.css === */
/* Hide hamburger on desktop */
.menu-toggle {
display: none;
}
@media (max-width: 768px) {
/* Show hamburger */
.menu-toggle {
display: block;
background: none;
border: none;
cursor: pointer;
padding: var(--space-sm);
}
.hamburger,
.hamburger::before,
.hamburger::after {
display: block;
width: 24px;
height: 2px;
background: var(--color-text);
transition: transform var(--transition-base);
position: relative;
}
.hamburger::before,
.hamburger::after {
content: '';
position: absolute;
}
.hamburger::before { top: -7px; }
.hamburger::after { top: 7px; }
/* Mobile nav layout */
.nav-links {
position: fixed;
top: 60px;
right: 0;
width: 100%;
background: var(--color-bg);
flex-direction: column;
padding: var(--space-lg);
box-shadow: var(--shadow-lg);
transform: translateY(-100%);
opacity: 0;
transition: transform var(--transition-base), opacity var(--transition-base);
}
.nav-links.open {
transform: translateY(0);
opacity: 1;
}
}
🧩 Reusable Components
A well-structured site uses reusable components — patterns that appear across multiple pages. Here are the key components for your portfolio:
Form Styles
Style your contact form inputs to be clean, accessible, and consistent:
/* ===== Forms ===== */
.form-group {
margin-bottom: var(--space-lg);
}
.form-group label {
display: block;
margin-bottom: var(--space-xs);
font-weight: 600;
color: var(--color-text);
}
.form-group input,
.form-group textarea {
width: 100%;
padding: var(--space-sm) var(--space-md);
border: 2px solid var(--color-border);
border-radius: var(--radius-md);
font-family: var(--font-body);
font-size: 1rem;
transition: border-color var(--transition-fast);
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
Footer Styles
/* ===== Footer ===== */
.site-footer {
background: var(--color-text);
color: var(--color-bg);
padding: var(--space-xl) var(--space-lg);
text-align: center;
}
.footer-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: var(--space-md);
}
.social-links {
display: flex;
gap: var(--space-lg);
}
.social-links a {
color: var(--color-bg);
text-decoration: none;
opacity: 0.8;
transition: opacity var(--transition-fast);
}
.social-links a:hover {
opacity: 1;
}
About Page — Skills & Timeline
Use Flexbox for the skills list and a pseudo-element timeline for experience/education:
/* ===== Skills Grid ===== */
.skills-list {
display: flex;
flex-wrap: wrap;
gap: var(--space-sm);
list-style: none;
}
.skills-list li {
background: var(--color-primary);
color: white;
padding: var(--space-xs) var(--space-md);
border-radius: var(--radius-full);
font-size: 0.9rem;
}
/* ===== Timeline ===== */
.timeline {
position: relative;
padding-left: var(--space-xl);
}
.timeline::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 2px;
background: var(--color-border);
}
.timeline-item {
position: relative;
margin-bottom: var(--space-lg);
}
.timeline-item::before {
content: '';
position: absolute;
left: calc(-1 * var(--space-xl) - 5px);
top: 4px;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--color-primary);
}
✨ Animations & Polish
Time to add the finishing touches from Module 10. Subtle animations make your portfolio feel professional and polished.
Card Hover Effects
We already added basic hover to project cards. Let's enhance with image zoom and overlay:
/* Image zoom on hover */
.project-card .project-image {
transition: transform var(--transition-base);
}
.project-card:hover .project-image {
transform: scale(1.05);
}
/* Button lift effect */
.btn:active {
transform: translateY(0);
}
Scroll Reveal Animation
Add a fade-in-up effect for elements as they enter the viewport. Define the animation in CSS:
/* Scroll reveal animation */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.reveal {
opacity: 0;
transform: translateY(30px);
}
.reveal.visible {
animation: fadeInUp 0.6s ease forwards;
}
/* Respect reduced motion preference */
@media (prefers-reduced-motion: reduce) {
.reveal {
opacity: 1;
transform: none;
animation: none;
}
}
prefers-reduced-motion media query to disable animations for users who have motion sensitivity. This is essential for accessibility (Module 11).
♿ Accessibility Audit
Before deploying, run through this accessibility checklist. Every item comes from what you learned in Module 11:
| Check | What to Verify | Module |
|---|---|---|
| ✅ | All images have descriptive alt text |
4, 11 |
| ✅ | Semantic HTML: <header>, <nav>, <main>, <footer> |
2, 11 |
| ✅ | Heading hierarchy: one <h1> per page, logical order |
2, 11 |
| ✅ | All form inputs have associated <label> elements |
3, 11 |
| ✅ | Color contrast ratio meets WCAG AA (4.5:1 text, 3:1 large) | 11 |
| ✅ | Full keyboard navigation: Tab, Enter, Escape all work | 11 |
| ✅ | :focus-visible styles on all interactive elements |
11 |
| ✅ | aria-label on icon-only buttons (hamburger, social links) |
11 |
| ✅ | prefers-reduced-motion disables animations |
10, 11 |
| ✅ | Skip navigation link as first focusable element | 11 |
Add a Skip Link
Add this as the very first element inside <body> on every page:
<a href="#main-content" class="skip-link">Skip to main content</a>
.skip-link {
position: absolute;
top: -100%;
left: 50%;
transform: translateX(-50%);
background: var(--color-primary);
color: white;
padding: var(--space-sm) var(--space-md);
border-radius: var(--radius-md);
z-index: 1000;
transition: top var(--transition-fast);
}
.skip-link:focus {
top: var(--space-md);
}
🔍 SEO & Meta Tags
Apply everything from Module 11's SEO section to make your portfolio discoverable by search engines and social media.
Essential Meta Tags
Add these to every page's <head>:
<head>
<!-- Basic Meta -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Your Name — Portfolio | Web Developer</title>
<meta name="description" content="Portfolio of [Name], a web developer specializing in...">
<!-- Open Graph (Facebook, LinkedIn) -->
<meta property="og:title" content="Your Name — Portfolio">
<meta property="og:description" content="Web developer portfolio showcasing...">
<meta property="og:image" content="https://yourdomain.com/images/og-image.jpg">
<meta property="og:url" content="https://yourdomain.com/">
<meta property="og:type" content="website">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Your Name — Portfolio">
<meta name="twitter:image" content="https://yourdomain.com/images/og-image.jpg">
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="images/favicon.ico">
</head>
JSON-LD Structured Data
Add structured data so search engines understand your portfolio is a personal website. Place this in the <head> of your home page:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Your Name",
"url": "https://yourdomain.com",
"jobTitle": "Web Developer",
"sameAs": [
"https://github.com/yourusername",
"https://linkedin.com/in/yourusername"
]
}
</script>
SEO Checklist
| Item | Done? |
|---|---|
Unique <title> on every page (50–60 characters) | ☐ |
Unique <meta description> per page (150–160 chars) | ☐ |
| Open Graph tags for social sharing | ☐ |
All images have alt text | ☐ |
| Proper heading hierarchy (h1 → h2 → h3) | ☐ |
| JSON-LD structured data on home page | ☐ |
| All links are descriptive (not "click here") | ☐ |
🌐 Deploy to GitHub Pages
The moment of truth! Let's put your portfolio live on the internet using GitHub Pages — a free hosting service for static websites.
Step 1: Initialize Git
If you haven't already, initialize a Git repository in your project folder:
# Navigate to your project
cd my-portfolio
# Initialize git
git init
# Add all files
git add .
# Create first commit
git commit -m "Initial commit — portfolio website"
Step 2: Create a GitHub Repository
Sign in to github.com and click the + button → New repository.
Name it yourusername.github.io for a personal site, or any name for a project site. Keep it public.
Follow the instructions GitHub shows to push your existing repository.
# Add GitHub as remote origin
git remote add origin https://github.com/yourusername/yourusername.github.io.git
# Push to GitHub
git branch -M main
git push -u origin main
Step 3: Enable GitHub Pages
Go to your repo → Settings → Pages (in left sidebar).
Under "Source", select main branch and / (root) folder. Click Save.
After a few minutes, your site will be live at https://yourusername.github.io. Share it with the world!
yourname.com) in the GitHub Pages settings. GitHub provides free HTTPS for custom domains too!
Making Updates
Every time you want to update your live site, just commit and push:
# After making changes:
git add .
git commit -m "Update projects section"
git push
🎓 Summary & Next Steps
Congratulations! You've completed the entire Web Foundations course. You've gone from understanding how the web works to building and deploying a complete, professional website. Here's everything you've accomplished:
You built a multi-page site with proper structure: headers, navs, sections, articles, forms, and footers.
You created a complete design system with custom properties, typography, colors, and reusable components.
You used Flexbox and CSS Grid to create responsive, flexible layouts that work on any screen size.
You added smooth transitions, hover effects, and scroll animations while respecting user preferences.
You built for everyone: keyboard navigation, screen reader support, skip links, and ARIA attributes.
You optimized for search engines with meta tags, Open Graph, JSON-LD, and deployed to a live URL.
What's Next?
Now that you have solid HTML & CSS foundations, here are the natural next steps in your web development journey:
Add interactivity: dynamic content, form validation, API calls, and single-page apps.
Learn Sass/SCSS for advanced CSS, and build tools like Vite for development workflows.
The best way to solidify skills is through practice. Rebuild your portfolio, contribute to open source, or take on freelance projects.