๐๏ธ Weekend Project: Build a Responsive Portfolio Website
This is your first full build โ the moment everything from Week 1 comes together. You'll create a personal portfolio site with semantic HTML, mobile-first CSS using Flexbox and Grid, and vanilla JavaScript that makes the page respond to the user. Follow the stages, tick off the checklist, and by Sunday night you'll have a real website you can put your name on and deploy.
Week 1 · Weekend Project · Capstone Build
๐ฏ Learning Objectives
By completing this project, you will be able to:
- Structure a multi-section page with semantic HTML5 landmarks
- Style a layout mobile-first and make it responsive with media queries
- Build flexible layouts using CSS Flexbox and Grid
- Wire up interactivity: a mobile menu toggle, smooth scrolling, and form validation
- Apply an accessibility and testing checklist before you call it "done"
Estimated Time: 4โ6 hours across the weekend
Project: A deployable, responsive portfolio website that's uniquely yours.
In This Project
The Goal
Build a single-page portfolio that a real visitor โ a recruiter, a client, a future collaborator โ could land on and immediately understand who you are, what you can do, and how to reach you. It should look sharp on a phone and a laptop, and it should feel alive: menus that open, links that glide, a form that checks itself.
That tree is your blueprint. Each node becomes a section of HTML, and you'll build it top to bottom. Think of this like Polya's problem-solving loop: understand what you're making, plan the pieces, build them one stage at a time, then look back and test against the rubric.
Required Features Checklist
These are the non-negotiables. Everything here is achievable with what you learned in Week 1 โ no libraries, no frameworks. Tick each one off as you go.
โ Must-have features
- โ Semantic HTML structure (
<header>,<nav>,<main>,<section>,<footer>) - โ Six sections: hero, about, projects, skills, contact, footer
- โ Mobile-first, responsive layout with at least one media query breakpoint
- โ A projects grid built with CSS Grid
- โ A working mobile menu toggle (hamburger)
- โ Smooth-scrolling in-page navigation links
- โ A contact form with client-side validation (required fields + email format)
- โ Accessible: labels on inputs,
alttext on images, good color contrast - โ External CSS and external JS files (no inline styles/scripts)
Starter Project Structure
Create these folders and files first. Keeping HTML, CSS, and JS in separate files is the professional default โ it keeps each concern in its own place and makes your code cacheable.
portfolio/
โโโ index.html
โโโ css/
โ โโโ styles.css
โโโ js/
โ โโโ main.js
โโโ images/
โ โโโ profile.jpg
โ โโโ project1.jpg
โ โโโ project2.jpg
โโโ assets/
โโโ resume.pdf
๐ก Tip: start with placeholders
Don't block yourself waiting on real images. Use a placeholder service or solid-color blocks now, and swap in real screenshots and a profile photo later. Momentum beats perfection on a weekend build.
Stage 1 โ HTML Skeleton
Start with structure, no styling. Write the semantic markup for every section so the whole page exists as plain, readable content first. Here's a solid starting point for index.html โ extend it with your own copy and projects.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Portfolio of [Your Name], web developer">
<title>[Your Name] | Web Developer</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<header>
<nav class="navbar">
<div class="container">
<a href="#home" class="logo">YN</a>
<button class="nav-toggle" aria-label="Toggle navigation" aria-expanded="false">
<span class="hamburger"></span>
</button>
<ul class="nav-menu">
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#projects">Projects</a></li>
<li><a href="#skills">Skills</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</div>
</nav>
</header>
<main>
<section id="home" class="hero">
<div class="container">
<h1>[Your Name]</h1>
<p class="tagline">Full-Stack Web Developer</p>
<a href="#contact" class="btn btn-primary">Get In Touch</a>
<a href="assets/resume.pdf" class="btn btn-secondary" download>Download Resume</a>
</div>
</section>
<section id="about" class="about">
<div class="container">
<h2>About Me</h2>
<div class="about-content">
<img src="images/profile.jpg" alt="Portrait of [Your Name]" class="profile-img">
<p>Write two or three sentences about who you are and what you love to build.</p>
</div>
</div>
</section>
<section id="projects" class="projects">
<div class="container">
<h2>Projects</h2>
<div class="project-grid">
<article class="project-card">
<img src="images/project1.jpg" alt="Screenshot of Project One">
<div class="project-info">
<h3>Project One</h3>
<p>One sentence on what it does and what you built it with.</p>
<div class="project-links">
<a href="#" class="btn btn-small">Live Demo</a>
<a href="#" class="btn btn-small btn-secondary">GitHub</a>
</div>
</div>
</article>
<!-- Duplicate the article for more projects -->
</div>
</div>
</section>
<section id="skills" class="skills">
<div class="container">
<h2>Skills</h2>
<div class="skills-grid">
<div class="skill-category">
<h3>Frontend</h3>
<ul><li>HTML5</li><li>CSS3</li><li>JavaScript</li></ul>
</div>
<div class="skill-category">
<h3>Tools</h3>
<ul><li>Git & GitHub</li><li>VS Code</li></ul>
</div>
</div>
</div>
</section>
<section id="contact" class="contact">
<div class="container">
<h2>Contact Me</h2>
<form id="contact-form" class="contact-form" novalidate>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
</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>
<p id="form-status" role="status"></p>
</form>
</div>
</section>
</main>
<footer>
<div class="container">
<p>© 2026 [Your Name]. All rights reserved.</p>
</div>
</footer>
<script src="js/main.js" defer></script>
</body>
</html>
๐ Why semantic tags matter
Using <header>, <nav>, <main>, and <section> instead of a pile of <div>s gives screen readers real landmarks to navigate, helps search engines understand your page, and makes your own CSS easier to target. Semantics are accessibility and SEO for free.
Stage 2 โ Mobile-First CSS
Style for the smallest screen first, then add complexity for larger screens inside a min-width media query. This keeps your base CSS simple and your layout robust. Custom properties (:root variables) keep your colors consistent and easy to change.
/* --- Base (mobile-first) --- */
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #007bff;
--text: #333;
--light-bg: #f8f9fa;
--white: #fff;
--space: 1rem;
}
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
line-height: 1.6;
color: var(--text);
}
.container {
width: 90%;
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--space);
}
section { padding: 4rem 0; }
h2 { text-align: center; margin-bottom: 2rem; }
/* Nav: menu hidden on mobile, revealed by JS or the media query below */
.navbar { position: fixed; top: 0; width: 100%; background: var(--white);
box-shadow: 0 2px 4px rgba(0,0,0,.1); z-index: 1000; }
.navbar .container { display: flex; justify-content: space-between;
align-items: center; padding: 1rem; }
.nav-menu { display: none; list-style: none; gap: 1.5rem; }
.nav-menu.active { display: flex; flex-direction: column; }
.nav-toggle { background: none; border: none; cursor: pointer; }
.hero { background: linear-gradient(135deg, #667eea, #764ba2);
color: var(--white); text-align: center; padding: 8rem 0 4rem; }
/* Flexible, wrapping grids with no media query needed */
.project-grid, .skills-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
}
.btn { display: inline-block; padding: .75rem 1.5rem; border-radius: 4px;
text-decoration: none; margin: .5rem; transition: transform .2s ease; }
.btn-primary { background: var(--primary); color: var(--white); }
.btn:hover { transform: translateY(-2px); }
.form-group { margin-bottom: 1.5rem; }
.form-group label { display: block; margin-bottom: .5rem; font-weight: 600; }
.form-group input, .form-group textarea {
width: 100%; padding: .75rem; border: 1px solid #ced4da;
border-radius: 4px; font-family: inherit;
}
.form-group .invalid { border-color: #dc3545; }
/* --- Larger screens: show the horizontal nav, hide the toggle --- */
@media (min-width: 768px) {
.nav-toggle { display: none; }
.nav-menu { display: flex; flex-direction: row; }
}
โ ๏ธ Grid does the heavy lifting
Notice the projects and skills grids use repeat(auto-fit, minmax(280px, 1fr)). That one line makes cards flow into as many columns as fit โ no media queries required. Let CSS Grid handle the responsiveness where it can.
Stage 3 โ JavaScript Interactivity
Now bring the page to life. Every feature here is built from what you learned this week: DOM selection, event listeners, conditionals, and functions. Put this in js/main.js.
Mobile menu toggle
const navToggle = document.querySelector(".nav-toggle");
const navMenu = document.querySelector(".nav-menu");
navToggle.addEventListener("click", () => {
const isOpen = navMenu.classList.toggle("active");
// Keep the accessibility state in sync with what the user sees
navToggle.setAttribute("aria-expanded", String(isOpen));
});
Smooth in-page scrolling
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener("click", (e) => {
const target = document.querySelector(anchor.getAttribute("href"));
if (!target) return; // guard: skip if the target is missing
e.preventDefault();
navMenu.classList.remove("active"); // close the mobile menu
navToggle.setAttribute("aria-expanded", "false");
target.scrollIntoView({ behavior: "smooth", block: "start" });
});
});
Contact form validation
const form = document.getElementById("contact-form");
const status = document.getElementById("form-status");
// A small, readable email check โ good enough for client-side UX
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
form.addEventListener("submit", (e) => {
e.preventDefault();
const name = form.name.value.trim();
const email = form.email.value.trim();
const message = form.message.value.trim();
if (!name || !email || !message) {
status.textContent = "Please fill in every field.";
return;
}
if (!isValidEmail(email)) {
status.textContent = "Please enter a valid email address.";
return;
}
// A real site would send this to a server or a form service here
console.log("Submitting:", { name, email, message });
status.textContent = "Thanks! Your message has been recorded.";
form.reset();
});
๐ก Progressive enhancement
The page works as plain content without a single line of JavaScript โ the links still jump, the form still has native validation from required and type="email". Your JS enhances that baseline with a smoother experience. Building in this order is a core professional habit.
Stretch Goals
Finished the required build with time to spare? Level it up. Pick whichever excites you โ none of these are required to pass the rubric.
- ๐ Dark-mode toggle that remembers the choice with
localStorage - ๐๏ธ Project filtering โ buttons that show/hide cards by
data-category - โจ Reveal-on-scroll animations using
IntersectionObserver - โจ๏ธ Inline field errors that appear next to each input instead of one status line
- ๐ผ๏ธ Lazy-loaded images with
loading="lazy" - ๐ Deploy it to Netlify, Vercel, or GitHub Pages and share the live URL
Here's a taste of the dark-mode stretch goal to get you started:
const themeBtn = document.getElementById("theme-toggle");
function applyTheme(theme) {
document.body.classList.toggle("dark", theme === "dark");
localStorage.setItem("theme", theme);
}
// Restore the saved choice on load
applyTheme(localStorage.getItem("theme") || "light");
themeBtn?.addEventListener("click", () => {
const next = document.body.classList.contains("dark") ? "light" : "dark";
applyTheme(next);
});
Self-Check Rubric
Before you call this done, grade yourself against the rubric below. Aim to answer "yes" to everything in the first two columns โ the stretch column is bonus.
| Area | Meets expectations (required) | Exceeds (stretch) |
|---|---|---|
| Structure | Semantic landmarks; all six sections present; valid HTML | Clear heading hierarchy; ARIA where it genuinely helps |
| Responsiveness | Looks good on phone and desktop; no horizontal scroll on mobile | Fluid typography; multiple thoughtful breakpoints |
| Layout | Projects/skills use Grid; nav uses Flexbox | Consistent spacing scale via CSS variables |
| Interactivity | Mobile menu toggles; links smooth-scroll; form validates | A working stretch feature (dark mode, filtering, etc.) |
| Accessibility | Labels on inputs; alt on images; readable contrast |
Keyboard-navigable; aria-expanded synced on the toggle |
| Code quality | External CSS/JS; no console errors; consistent formatting | Deployed live with a shareable URL |
๐งช Final testing checklist
- โ Resize the browser from ~360px to full width โ layout stays clean
- โ Every nav link scrolls to the right section
- โ Submitting an empty form shows an error, not a page reload
- โ An invalid email is rejected; a valid one succeeds
- โ Tab through the page with the keyboard only โ focus is visible and logical
- โ No red errors in the browser console
Summary
๐ What You Built
- A semantic, multi-section portfolio page with proper HTML5 landmarks
- A mobile-first responsive layout using Flexbox for nav and Grid for cards
- Real interactivity: a menu toggle, smooth scrolling, and a validated contact form
- An accessible, tested result you graded against a professional rubric
This project is the proof that Week 1 stuck. You took HTML structure, CSS styling, responsive design, and JavaScript logic and combined them into something a stranger could actually use. That is web development.
๐ Additional Resources
- MDN โ Responsive design
- MDN โ CSS Grid layout
- MDN โ Client-side form validation
- The A11Y Project โ Accessibility checklist
๐ What's Next?
Week 1 is complete โ congratulations! Next week we go deeper into the data structures that power real apps. First up: Working with Arrays, where you'll learn the methods (map, filter, reduce and friends) that replace most of the loops you wrote by hand.
๐ You finished Week 1!
You went from "what is a variable?" to shipping a responsive website in a single week. Take a screenshot โ this is the first entry in your portfolio.