PP Portfolio Build Lesson

Frontend Lesson • HTML + CSS + JavaScript

Build a Portfolio Website
from Point 0

This page teaches students how to build a complete portfolio website step by step. Each step adds one useful feature. Every step includes code, explanation, a check, and a copy button.

Beginner Friendly Semantic HTML Responsive CSS Interactive JS

Lesson Goal

A portfolio website is a personal website that presents who you are, what you can do, and what you have built. It is one of the best beginner frontend projects because it combines page structure, styling, responsiveness, and interactivity.

What students should learn

  • How to structure a page with semantic HTML
  • How CSS turns plain content into a polished layout
  • How JavaScript adds interaction
  • How to build a project in small steps

Before starting

  • Create a file named index.html
  • Open it in a browser
  • Open the live editor below
  • After each step, save and refresh the page

Live HTML Preview Editor

Students can paste the code from any lesson step into this editor and preview it immediately. This is useful for testing layout, experimenting with styles, and understanding how the page changes as each step is added.

Embedded HTML Preview Editor

Paste HTML, CSS, and JavaScript here and test your portfolio live.
Open in New Tab

How to use it

  • Copy code from any step below.
  • Paste it into the embedded editor.
  • Run or preview the result.
  • Make changes and compare how the output improves step by step.
0

Step 0 — Create the basic HTML page

We begin with the smallest valid page.

Explanation: Every website starts with a basic HTML skeleton. This includes <!doctype html>, <html>, <head>, and <body>. The browser uses this structure to understand and display your page.
Step 0 Code — Basic HTML Page
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>My Portfolio</title>
</head>
<body>
  <h1>My Portfolio</h1>
  <p>This is the beginning of my website.</p>
</body>
</html>

Check

After saving and refreshing, you should see a heading that says My Portfolio and a short paragraph below it.

1

Step 1 — Add page sections

Now we organize the page into meaningful parts.

Explanation: A portfolio is easier to build and maintain when it is divided into sections. We use semantic tags like <header>, <main>, <section>, and <footer>. These make the page more readable for both humans and browsers.
Step 1 Code — Add Header, Main Sections, and Footer
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>My Portfolio</title>
</head>
<body>
  <header>
    <h1>My Portfolio</h1>
    <p>Frontend Student</p>
  </header>

  <main>
    <section>
      <h2>About Me</h2>
      <p>I am learning HTML, CSS, and JavaScript.</p>
    </section>

    <section>
      <h2>Skills</h2>
      <ul>
        <li>HTML</li>
        <li>CSS</li>
        <li>JavaScript</li>
      </ul>
    </section>

    <section>
      <h2>Projects</h2>
      <p>My projects will go here.</p>
    </section>

    <section>
      <h2>Contact</h2>
      <p>Email: student@example.com</p>
    </section>
  </main>

  <footer>
    <p>© 2026 My Portfolio</p>
  </footer>
</body>
</html>

Check

Your page should now have 4 content areas: About Me, Skills, Projects, and Contact.

2

Step 2 — Add navigation links

This makes the portfolio easier to move around.

Explanation: Navigation links point to section IDs on the same page. For example, a link with href="#about" jumps to the element with id="about". This is a clean way to create a one-page portfolio.
Step 2 Code — Add a Navigation Menu
<header>
  <h1>My Portfolio</h1>
  <p>Frontend Student</p>

  <nav>
    <a href="#about">About</a>
    <a href="#skills">Skills</a>
    <a href="#projects">Projects</a>
    <a href="#contact">Contact</a>
  </nav>
</header>

<main>
  <section id="about">
    <h2>About Me</h2>
    <p>I am learning HTML, CSS, and JavaScript.</p>
  </section>

  <section id="skills">
    <h2>Skills</h2>
    <ul>
      <li>HTML</li>
      <li>CSS</li>
      <li>JavaScript</li>
    </ul>
  </section>

  <section id="projects">
    <h2>Projects</h2>
    <p>My projects will go here.</p>
  </section>

  <section id="contact">
    <h2>Contact</h2>
    <p>Email: student@example.com</p>
  </section>
</main>

Check

Clicking each navigation link should move you to the matching section.

3

Step 3 — Add CSS to make it look like a real site

Now we style the structure we built.

Explanation: HTML gives us the structure. CSS gives us layout, color, spacing, and polish. In this step, we add a warm light theme, section cards, spacing, and button styles. This is the moment where the page starts to feel professional.
Step 3 Code — CSS Starter Styling
<style>
  :root{
    --bg:#fff8ef;
    --panel:#ffffff;
    --ink:#1f2937;
    --muted:#6b7280;
    --brand:#d97706;
    --line:#ead7c4;
  }

  *{box-sizing:border-box}
  body{
    margin:0;
    font-family:Arial, Helvetica, sans-serif;
    background:var(--bg);
    color:var(--ink);
    line-height:1.6;
  }

  .container{
    width:min(1000px, calc(100% - 32px));
    margin:0 auto;
  }

  header{
    background:#fff;
    border-bottom:1px solid var(--line);
    padding:20px 0;
  }

  nav a{
    margin-right:14px;
    text-decoration:none;
    color:var(--brand);
    font-weight:bold;
  }

  section{
    background:var(--panel);
    border:1px solid var(--line);
    border-radius:18px;
    padding:24px;
    margin:24px 0;
  }

  h1,h2,h3{
    margin-top:0;
  }

  .btn{
    display:inline-block;
    background:var(--brand);
    color:#fff;
    text-decoration:none;
    padding:12px 16px;
    border-radius:12px;
    font-weight:bold;
  }

  footer{
    text-align:center;
    padding:24px 0 40px;
    color:var(--muted);
  }
</style>

How to use this CSS

Put this inside the <head> section of your page, and wrap your content inside a div with class container.

Check

Your page should now have padding, section cards, soft colors, and a cleaner layout.

4

Step 4 — Add a hero section and project cards

This makes the portfolio feel complete and modern.

Explanation: The hero section is the first thing visitors see. It should quickly tell them who you are. The projects area should show what you have built. Cards are a simple and effective way to present project information.
Step 4 Code — Hero Section and Projects Grid
<section id="home">
  <h2>Hello, I am Your Name</h2>
  <p>I build beginner-friendly frontend projects using HTML, CSS, and JavaScript.</p>
  <a class="btn" href="#projects">View My Projects</a>
</section>

<section id="projects">
  <h2>Projects</h2>

  <div class="projects-grid">
    <article class="project-card">
      <h3>Portfolio Website</h3>
      <p>A personal website to showcase my profile and work.</p>
    </article>

    <article class="project-card">
      <h3>Quiz App</h3>
      <p>A multiple-choice quiz app built with JavaScript.</p>
    </article>

    <article class="project-card">
      <h3>Mini Shop UI</h3>
      <p>A frontend shopping page with product cards and cart ideas.</p>
    </article>
  </div>
</section>
Extra CSS for Step 4
#home{
  text-align:left;
}

.projects-grid{
  display:grid;
  grid-template-columns:repeat(3, 1fr);
  gap:16px;
}

.project-card{
  border:1px solid var(--line);
  border-radius:16px;
  padding:18px;
  background:#fffdf9;
}

@media (max-width: 800px){
  .projects-grid{
    grid-template-columns:1fr;
  }
}

Check

You should now see a top introduction area and three project cards arranged in a grid.

5

Step 5 — Add a contact form

A portfolio should make it easy for people to reach you.

Explanation: Forms are an important part of web development. In a portfolio, a contact form gives visitors a clear way to send a message. Even if the form does not send data to a server yet, it is still useful for learning layout and validation.
Step 5 Code — Contact Form HTML
<section id="contact">
  <h2>Contact Me</h2>

  <form id="contactForm">
    <p>
      <label for="name">Name</label><br />
      <input id="name" type="text" placeholder="Enter your name" />
    </p>

    <p>
      <label for="email">Email</label><br />
      <input id="email" type="email" placeholder="Enter your email" />
    </p>

    <p>
      <label for="message">Message</label><br />
      <textarea id="message" rows="5" placeholder="Write your message"></textarea>
    </p>

    <button class="btn" type="submit">Send Message</button>
    <p id="formMessage"></p>
  </form>
</section>
Extra CSS for Step 5
input, textarea{
  width:100%;
  padding:12px;
  border:1px solid var(--line);
  border-radius:12px;
  font:inherit;
}

label{
  font-weight:bold;
}

#formMessage{
  color:green;
  font-weight:bold;
}

Check

Your contact section should now contain inputs for name, email, and message.

6

Step 6 — Add JavaScript interaction

Now the page starts reacting to user actions.

Explanation: JavaScript can validate forms, toggle themes, show messages, and save preferences. In this step, we add:
  • form validation
  • a success message
  • a theme toggle button
  • theme saving with localStorage
Step 6 Code — Theme Toggle Button HTML
<button id="themeToggle" class="btn" type="button">Toggle Theme</button>
Extra CSS for Dark Theme
body.dark{
  --bg:#111827;
  --panel:#1f2937;
  --ink:#f9fafb;
  --muted:#d1d5db;
  --brand:#f59e0b;
  --line:#374151;
}
Step 6 Code — JavaScript
<script>
  const contactForm = document.getElementById("contactForm");
  const formMessage = document.getElementById("formMessage");
  const themeToggle = document.getElementById("themeToggle");

  function applyTheme(theme) {
    if (theme === "dark") {
      document.body.classList.add("dark");
    } else {
      document.body.classList.remove("dark");
    }
  }

  const savedTheme = localStorage.getItem("portfolio-theme") || "light";
  applyTheme(savedTheme);

  themeToggle.addEventListener("click", function () {
    const isDark = document.body.classList.contains("dark");
    const nextTheme = isDark ? "light" : "dark";
    localStorage.setItem("portfolio-theme", nextTheme);
    applyTheme(nextTheme);
  });

  contactForm.addEventListener("submit", function (event) {
    event.preventDefault();

    const name = document.getElementById("name").value.trim();
    const email = document.getElementById("email").value.trim();
    const message = document.getElementById("message").value.trim();

    if (!name || !email || !message) {
      formMessage.textContent = "Please fill in all fields.";
      return;
    }

    if (!email.includes("@") || !email.includes(".")) {
      formMessage.textContent = "Please enter a valid email address.";
      return;
    }

    formMessage.textContent = "Your message has been saved on the frontend.";
    contactForm.reset();
  });
</script>

Check

  • Click the theme button and the page colors should change.
  • Refresh the page and the theme should stay saved.
  • Submit an empty form and you should see a warning.
  • Submit a filled form and you should see a success message.

Final Build — Complete Single HTML Page

This is the complete finished lesson result. It includes HTML, CSS, and JavaScript in one file. Students can copy this into index.html, save it, and run it directly.

What the final site contains:

Student Portfolio

Hello, I am Your Name

I build websites using HTML, CSS, and JavaScript.

Profile Image Placeholder
About
Who you are
Skills
What you know
Projects
What you built
Final Complete Code — index.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Student Portfolio</title>
  <meta name="description" content="A beginner portfolio website built with HTML, CSS, and JavaScript." />

  <style>
    :root{
      --bg:#fff8ef;
      --panel:#ffffff;
      --soft:#fff2df;
      --ink:#1f2937;
      --muted:#6b7280;
      --brand:#d97706;
      --brand-dark:#b45309;
      --line:#ead7c4;
      --shadow:0 10px 28px rgba(0,0,0,.08);
    }

    body.dark{
      --bg:#111827;
      --panel:#1f2937;
      --soft:#172033;
      --ink:#f9fafb;
      --muted:#d1d5db;
      --brand:#f59e0b;
      --brand-dark:#d97706;
      --line:#374151;
      --shadow:0 10px 28px rgba(0,0,0,.28);
    }

    *{box-sizing:border-box}
    html{scroll-behavior:smooth}

    body{
      margin:0;
      font-family:Arial, Helvetica, sans-serif;
      background:var(--bg);
      color:var(--ink);
      line-height:1.6;
    }

    img{
      max-width:100%;
      display:block;
      border-radius:20px;
    }

    a{
      text-decoration:none;
      color:inherit;
    }

    .container{
      width:min(1100px, calc(100% - 32px));
      margin:0 auto;
    }

    .site-header{
      position:sticky;
      top:0;
      z-index:1000;
      background:rgba(255,248,239,.92);
      backdrop-filter:blur(10px);
      border-bottom:1px solid var(--line);
    }

    body.dark .site-header{
      background:rgba(17,24,39,.92);
    }

    .nav-wrap{
      display:flex;
      justify-content:space-between;
      align-items:center;
      gap:14px;
      min-height:72px;
    }

    .logo{
      color:var(--brand);
      font-size:1.35rem;
      font-weight:800;
    }

    .nav{
      display:flex;
      gap:16px;
      align-items:center;
      flex-wrap:wrap;
    }

    .nav a{
      font-weight:700;
      color:var(--muted);
    }

    .nav a:hover{
      color:var(--brand);
    }

    .menu-btn{
      display:none;
      border:none;
      background:var(--panel);
      color:var(--ink);
      padding:10px 12px;
      border-radius:12px;
      cursor:pointer;
      box-shadow:var(--shadow);
      font-size:1.2rem;
    }

    .btn{
      border:none;
      background:var(--brand);
      color:#fff;
      padding:12px 16px;
      border-radius:12px;
      cursor:pointer;
      font-weight:800;
      transition:transform .2s ease, background .2s ease;
    }

    .btn:hover{
      transform:translateY(-2px);
      background:var(--brand-dark);
    }

    .btn-outline{
      background:transparent;
      color:var(--brand);
      border:2px solid var(--brand);
    }

    .btn-outline:hover{
      color:#fff;
    }

    .section{
      padding:72px 0;
    }

    .section-soft{
      background:var(--soft);
    }

    .hero-grid{
      display:grid;
      grid-template-columns:1.15fr .85fr;
      gap:24px;
      align-items:center;
    }

    .hero h1{
      margin:0 0 10px;
      font-size:clamp(2rem,5vw,3.5rem);
      line-height:1.12;
    }

    .eyebrow{
      color:var(--brand);
      font-weight:800;
      margin-bottom:8px;
    }

    .accent{
      color:var(--brand);
    }

    .hero-desc,
    .section-text{
      color:var(--muted);
      max-width:720px;
    }

    .hero-actions{
      display:flex;
      gap:12px;
      flex-wrap:wrap;
      margin-top:20px;
    }

    .hero-card,
    .skill-card,
    .project-card,
    .contact-form{
      background:var(--panel);
      border:1px solid var(--line);
      border-radius:22px;
      box-shadow:var(--shadow);
    }

    .hero-card{
      padding:18px;
      min-height:320px;
      display:grid;
      place-items:center;
      text-align:center;
      background:
        linear-gradient(135deg, rgba(245,158,11,.10), rgba(255,255,255,.55)),
        var(--panel);
    }

    .skills-grid,
    .projects-grid{
      display:grid;
      gap:20px;
    }

    .skills-grid{
      grid-template-columns:repeat(2,1fr);
    }

    .projects-grid{
      grid-template-columns:repeat(3,1fr);
    }

    .skill-card,
    .project-card{
      padding:20px;
    }

    .bar{
      height:12px;
      background:#e5e7eb;
      border-radius:999px;
      overflow:hidden;
      margin-top:10px;
    }

    .bar span{
      display:block;
      height:100%;
      background:linear-gradient(90deg,var(--brand),#fbbf24);
      border-radius:999px;
    }

    .project-card p{
      color:var(--muted);
    }

    .project-link{
      display:inline-block;
      margin-top:10px;
      color:var(--brand);
      font-weight:800;
    }

    .contact-form{
      padding:24px;
      max-width:760px;
    }

    .form-row{
      display:grid;
      gap:8px;
      margin-bottom:16px;
    }

    label{
      font-weight:800;
    }

    input,
    textarea{
      width:100%;
      padding:14px;
      border-radius:12px;
      border:1px solid var(--line);
      background:transparent;
      color:var(--ink);
      font:inherit;
    }

    input:focus,
    textarea:focus{
      outline:2px solid rgba(217,119,6,.18);
      border-color:var(--brand);
    }

    .form-message{
      margin-top:14px;
      font-weight:800;
      color:var(--brand);
    }

    .site-footer{
      border-top:1px solid var(--line);
      padding:22px 0;
      text-align:center;
      color:var(--muted);
    }

    @media (max-width: 860px){
      .hero-grid,
      .skills-grid,
      .projects-grid{
        grid-template-columns:1fr;
      }

      .menu-btn{
        display:inline-block;
      }

      .nav{
        position:absolute;
        top:72px;
        left:16px;
        right:16px;
        display:none;
        flex-direction:column;
        align-items:stretch;
        background:var(--panel);
        border:1px solid var(--line);
        padding:16px;
        border-radius:18px;
        box-shadow:var(--shadow);
      }

      .nav.show{
        display:flex;
      }
    }
  </style>
</head>
<body>
  <header class="site-header">
    <div class="container nav-wrap">
      <a href="#" class="logo">MyPortfolio</a>

      <button class="menu-btn" id="menuBtn" aria-label="Open navigation">☰</button>

      <nav class="nav" id="nav">
        <a href="#home">Home</a>
        <a href="#about">About</a>
        <a href="#skills">Skills</a>
        <a href="#projects">Projects</a>
        <a href="#contact">Contact</a>
        <button id="themeToggle" class="btn" type="button">🌙 Theme</button>
      </nav>
    </div>
  </header>

  <main>
    <section class="section hero" id="home">
      <div class="container hero-grid">
        <div>
          <p class="eyebrow">Student Portfolio</p>
          <h1>Hello, I am <span class="accent">Your Name</span></h1>
          <p class="hero-desc">
            I am learning frontend development and building websites using HTML, CSS, and JavaScript.
          </p>

          <div class="hero-actions">
            <a href="#projects" class="btn">View Projects</a>
            <a href="#contact" class="btn btn-outline">Contact Me</a>
          </div>
        </div>

        <div class="hero-card">
          <div>
            <img src="https://via.placeholder.com/420x320.png?text=Your+Photo" alt="Student placeholder image" />
          </div>
        </div>
      </div>
    </section>

    <section class="section" id="about">
      <div class="container">
        <h2>About Me</h2>
        <p class="section-text">
          I enjoy creating websites, solving problems, and learning modern frontend development.
        </p>
      </div>
    </section>

    <section class="section section-soft" id="skills">
      <div class="container">
        <h2>My Skills</h2>

        <div class="skills-grid">
          <div class="skill-card">
            <h3>HTML</h3>
            <div class="bar"><span style="width:90%"></span></div>
          </div>

          <div class="skill-card">
            <h3>CSS</h3>
            <div class="bar"><span style="width:82%"></span></div>
          </div>

          <div class="skill-card">
            <h3>JavaScript</h3>
            <div class="bar"><span style="width:72%"></span></div>
          </div>

          <div class="skill-card">
            <h3>Problem Solving</h3>
            <div class="bar"><span style="width:85%"></span></div>
          </div>
        </div>
      </div>
    </section>

    <section class="section" id="projects">
      <div class="container">
        <h2>My Projects</h2>

        <div class="projects-grid">
          <article class="project-card">
            <h3>Portfolio Website</h3>
            <p>A personal website to present my profile, skills, and work.</p>
            <a href="#" class="project-link">View Project</a>
          </article>

          <article class="project-card">
            <h3>Quiz App</h3>
            <p>An interactive quiz project built with JavaScript.</p>
            <a href="#" class="project-link">View Project</a>
          </article>

          <article class="project-card">
            <h3>Mini Shop UI</h3>
            <p>A product listing page with cards and frontend interactions.</p>
            <a href="#" class="project-link">View Project</a>
          </article>
        </div>
      </div>
    </section>

    <section class="section section-soft" id="contact">
      <div class="container">
        <h2>Contact Me</h2>

        <form class="contact-form" id="contactForm">
          <div class="form-row">
            <label for="name">Name</label>
            <input id="name" type="text" placeholder="Enter your name" />
          </div>

          <div class="form-row">
            <label for="email">Email</label>
            <input id="email" type="email" placeholder="Enter your email" />
          </div>

          <div class="form-row">
            <label for="message">Message</label>
            <textarea id="message" rows="5" placeholder="Write your message"></textarea>
          </div>

          <button class="btn" type="submit">Send Message</button>
          <p class="form-message" id="formMessage"></p>
        </form>
      </div>
    </section>
  </main>

  <footer class="site-footer">
    <div class="container">
      <p>© 2026 Your Name | Student Portfolio</p>
    </div>
  </footer>

  <script>
    const menuBtn = document.getElementById("menuBtn");
    const nav = document.getElementById("nav");
    const themeToggle = document.getElementById("themeToggle");
    const contactForm = document.getElementById("contactForm");
    const formMessage = document.getElementById("formMessage");

    menuBtn.addEventListener("click", () => {
      nav.classList.toggle("show");
    });

    document.querySelectorAll(".nav a").forEach((link) => {
      link.addEventListener("click", () => {
        nav.classList.remove("show");
      });
    });

    function applyTheme(theme) {
      if (theme === "dark") {
        document.body.classList.add("dark");
        themeToggle.textContent = "☀️ Light";
      } else {
        document.body.classList.remove("dark");
        themeToggle.textContent = "🌙 Theme";
      }
    }

    const savedTheme = localStorage.getItem("portfolio-theme") || "light";
    applyTheme(savedTheme);

    themeToggle.addEventListener("click", () => {
      const isDark = document.body.classList.contains("dark");
      const nextTheme = isDark ? "light" : "dark";
      localStorage.setItem("portfolio-theme", nextTheme);
      applyTheme(nextTheme);
    });

    contactForm.addEventListener("submit", (event) => {
      event.preventDefault();

      const name = document.getElementById("name").value.trim();
      const email = document.getElementById("email").value.trim();
      const message = document.getElementById("message").value.trim();

      if (!name || !email || !message) {
        formMessage.textContent = "Please fill in all fields.";
        return;
      }

      if (!email.includes("@") || !email.includes(".")) {
        formMessage.textContent = "Please enter a valid email address.";
        return;
      }

      formMessage.textContent = "Thank you. Your message has been recorded on the frontend.";
      contactForm.reset();
    });
  </script>
</body>
</html>

Final Check

  • The page has a header, hero, about, skills, projects, contact, and footer.
  • The site is responsive on mobile screens.
  • The theme toggle works and stays saved after refresh.
  • The contact form validates user input.

Practice Tasks for Students

Basic tasks

  • Replace placeholder name and text
  • Add a real profile image
  • Add one more project card
  • Add social links

Upgrade tasks

  • Add a certificates section
  • Add hover animation on cards
  • Add a download CV button
  • Add a skills progress animation