Skip to main content

๐Ÿ—๏ธ 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.

graph TD A[Portfolio Website] --> B[Header / Nav] A --> C[Hero] A --> D[About] A --> E[Projects] A --> F[Skills] A --> G[Contact] A --> H[Footer] B --> I[Logo] B --> J[Nav Menu] B --> K[Mobile Toggle] E --> L[Project Cards] G --> M[Contact Form] G --> N[Social Links]

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, alt text 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>&copy; 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.

AreaMeets 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

๐Ÿš€ 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.