Introduction to HTML & CSS · Lesson 5 of 5

Project: Personal Portfolio Site

Build a Portfolio Website — HTML & CSS Project

This project brings together everything from the HTML and CSS lessons. You'll build a complete personal portfolio with a navigation, hero section, projects grid, skills, and contact form.


Project Structure

portfolio/
├── index.html
├── style.css
└── assets/
    └── profile.jpg (optional)

Step 1: HTML Structure

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Your Name — Developer</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>

  <!-- Navigation -->
  <nav class="nav">
    <a href="#" class="nav-logo">YourName.dev</a>
    <ul class="nav-links">
      <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>
    <button class="theme-toggle" aria-label="Toggle dark mode">🌙</button>
  </nav>

  <!-- Hero -->
  <section class="hero" id="about">
    <div class="hero-content">
      <span class="hero-badge">Available for work</span>
      <h1>Hi, I'm <span class="highlight">Your Name</span></h1>
      <p class="hero-subtitle">Full-Stack Developer specialising in .NET and React</p>
      <p class="hero-desc">
        I build fast, accessible, production-ready web applications.
        Based in Oslo, Norway.
      </p>
      <div class="hero-actions">
        <a href="#projects" class="btn btn-primary">View Work</a>
        <a href="#contact" class="btn btn-outline">Get in Touch</a>
      </div>
    </div>
  </section>

  <!-- Projects -->
  <section class="section" id="projects">
    <div class="container">
      <h2 class="section-title">Projects</h2>
      <p class="section-desc">Things I've built</p>
      <div class="projects-grid">

        <article class="project-card">
          <div class="project-header">
            <span class="project-tag">Web App</span>
          </div>
          <h3>Task Manager API</h3>
          <p>RESTful API built with ASP.NET Core 8 and PostgreSQL. Includes JWT auth, EF Core, and Swagger documentation.</p>
          <div class="project-tech">
            <span>.NET 8</span>
            <span>PostgreSQL</span>
            <span>Docker</span>
          </div>
          <div class="project-links">
            <a href="#" class="link-btn">GitHub</a>
            <a href="#" class="link-btn link-btn-primary">Live Demo</a>
          </div>
        </article>

        <article class="project-card featured">
          <div class="project-header">
            <span class="project-tag">Featured</span>
          </div>
          <h3>E-Commerce Platform</h3>
          <p>Full-stack e-commerce app with React frontend, .NET API backend, Stripe payments, and real-time inventory.</p>
          <div class="project-tech">
            <span>React</span>
            <span>TypeScript</span>
            <span>ASP.NET Core</span>
          </div>
          <div class="project-links">
            <a href="#" class="link-btn">GitHub</a>
            <a href="#" class="link-btn link-btn-primary">Live Demo</a>
          </div>
        </article>

        <article class="project-card">
          <div class="project-header">
            <span class="project-tag">CLI Tool</span>
          </div>
          <h3>DevLog CLI</h3>
          <p>Command-line tool for developers to log daily progress. Built in Go, stores to local SQLite database.</p>
          <div class="project-tech">
            <span>Go</span>
            <span>SQLite</span>
            <span>CLI</span>
          </div>
          <div class="project-links">
            <a href="#" class="link-btn">GitHub</a>
          </div>
        </article>

      </div>
    </div>
  </section>

  <!-- Skills -->
  <section class="section section-alt" id="skills">
    <div class="container">
      <h2 class="section-title">Skills</h2>
      <div class="skills-grid">
        <div class="skill-group">
          <h3>Backend</h3>
          <ul>
            <li>C# / .NET 8</li>
            <li>ASP.NET Core</li>
            <li>Entity Framework Core</li>
            <li>SQL / PostgreSQL</li>
          </ul>
        </div>
        <div class="skill-group">
          <h3>Frontend</h3>
          <ul>
            <li>React / TypeScript</li>
            <li>Next.js</li>
            <li>HTML / CSS</li>
            <li>Tailwind CSS</li>
          </ul>
        </div>
        <div class="skill-group">
          <h3>DevOps</h3>
          <ul>
            <li>Docker</li>
            <li>GitHub Actions</li>
            <li>Azure</li>
            <li>Linux</li>
          </ul>
        </div>
      </div>
    </div>
  </section>

  <!-- Contact -->
  <section class="section" id="contact">
    <div class="container contact-container">
      <h2 class="section-title">Get in Touch</h2>
      <p class="section-desc">Have a project in mind? Let's talk.</p>
      <form class="contact-form" id="contact-form">
        <div class="form-row">
          <div class="form-group">
            <label for="name">Name</label>
            <input type="text" id="name" name="name" placeholder="Your name" required />
          </div>
          <div class="form-group">
            <label for="email">Email</label>
            <input type="email" id="email" name="email" placeholder="you@example.com" required />
          </div>
        </div>
        <div class="form-group">
          <label for="subject">Subject</label>
          <input type="text" id="subject" name="subject" placeholder="Project inquiry" />
        </div>
        <div class="form-group">
          <label for="message">Message</label>
          <textarea id="message" name="message" rows="5" placeholder="Tell me about your project..." required></textarea>
        </div>
        <button type="submit" class="btn btn-primary btn-full">Send Message</button>
      </form>
    </div>
  </section>

  <footer class="footer">
    <p>Built with HTML &amp; CSS · &copy; 2026 Your Name</p>
  </footer>

  <script>
    // Dark mode toggle
    const toggle = document.querySelector('.theme-toggle');
    toggle.addEventListener('click', () => {
      document.documentElement.classList.toggle('dark');
      toggle.textContent = document.documentElement.classList.contains('dark') ? '☀️' : '🌙';
    });

    // Contact form
    document.getElementById('contact-form').addEventListener('submit', (e) => {
      e.preventDefault();
      alert('Message sent! (In a real site, this would call an API)');
      e.target.reset();
    });
  </script>
</body>
</html>

Step 2: CSS

CSS
/* style.css */

/* ─── CSS Variables (theme system) ─── */
:root {
  --bg: #ffffff;
  --bg-alt: #f9fafb;
  --fg: #111827;
  --fg-muted: #6b7280;
  --primary: #4f46e5;
  --primary-hover: #4338ca;
  --border: #e5e7eb;
  --card-bg: #ffffff;
  --shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
  --radius: 12px;
}

.dark {
  --bg: #0f172a;
  --bg-alt: #1e293b;
  --fg: #f1f5f9;
  --fg-muted: #94a3b8;
  --border: #334155;
  --card-bg: #1e293b;
}

/* ─── Reset ─── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--bg);
  color: var(--fg);
  line-height: 1.6;
  transition: background 0.2s, color 0.2s;
}
a { color: var(--primary); text-decoration: none; }
ul { list-style: none; }

/* ─── Navigation ─── */
.nav {
  position: sticky; top: 0; z-index: 100;
  display: flex; align-items: center; gap: 24px;
  padding: 0 24px; height: 64px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.nav-logo { font-weight: 700; font-size: 1.1rem; color: var(--fg); }
.nav-links { display: flex; gap: 4px; margin-left: auto; }
.nav-links a {
  padding: 8px 12px;
  border-radius: 8px;
  color: var(--fg-muted);
  font-size: 0.9rem;
  font-weight: 500;
  transition: color 0.2s, background 0.2s;
}
.nav-links a:hover { color: var(--fg); background: var(--bg-alt); }
.theme-toggle {
  background: none; border: 1px solid var(--border);
  border-radius: 8px; padding: 6px 10px;
  cursor: pointer; font-size: 1rem;
}

/* ─── Hero ─── */
.hero {
  min-height: 80vh;
  display: flex; align-items: center;
  padding: 80px 24px;
}
.hero-content { max-width: 640px; margin: 0 auto; }
.hero-badge {
  display: inline-block;
  padding: 4px 12px;
  background: #dcfce7; color: #15803d;
  border-radius: 999px; font-size: 0.8rem; font-weight: 600;
  margin-bottom: 16px;
}
.dark .hero-badge { background: #14532d; color: #86efac; }
h1 { font-size: clamp(2rem, 5vw, 3.5rem); font-weight: 800; line-height: 1.2; margin-bottom: 12px; }
.highlight { color: var(--primary); }
.hero-subtitle { font-size: 1.2rem; color: var(--fg-muted); margin-bottom: 12px; }
.hero-desc { color: var(--fg-muted); margin-bottom: 32px; }
.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; }

/* ─── Buttons ─── */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 24px;
  border-radius: var(--radius); font-weight: 600; font-size: 0.95rem;
  cursor: pointer; transition: all 0.2s; border: 2px solid transparent;
}
.btn-primary {
  background: var(--primary); color: white;
}
.btn-primary:hover { background: var(--primary-hover); color: white; }
.btn-outline {
  border-color: var(--border); color: var(--fg); background: transparent;
}
.btn-outline:hover { background: var(--bg-alt); }
.btn-full { width: 100%; justify-content: center; }

/* ─── Sections ─── */
.section { padding: 80px 24px; }
.section-alt { background: var(--bg-alt); }
.container { max-width: 1100px; margin: 0 auto; }
.section-title { font-size: 2rem; font-weight: 800; margin-bottom: 8px; }
.section-desc { color: var(--fg-muted); margin-bottom: 40px; }

/* ─── Projects Grid ─── */
.projects-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 24px;
}
.project-card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px;
  display: flex; flex-direction: column; gap: 12px;
  transition: box-shadow 0.2s, transform 0.2s;
}
.project-card:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.project-card.featured { border-color: var(--primary); }
.project-header { display: flex; justify-content: space-between; }
.project-tag {
  font-size: 0.75rem; font-weight: 700;
  padding: 2px 8px; border-radius: 4px;
  background: var(--bg-alt); color: var(--fg-muted);
}
.project-card.featured .project-tag { background: var(--primary); color: white; }
.project-card h3 { font-size: 1.1rem; font-weight: 700; }
.project-card p { color: var(--fg-muted); font-size: 0.9rem; flex: 1; }
.project-tech {
  display: flex; flex-wrap: wrap; gap: 6px;
}
.project-tech span {
  font-size: 0.75rem; font-weight: 600;
  padding: 2px 8px; border-radius: 4px;
  background: var(--bg-alt); color: var(--fg-muted);
}
.project-links { display: flex; gap: 8px; margin-top: auto; }
.link-btn {
  padding: 6px 14px; border-radius: 8px;
  font-size: 0.85rem; font-weight: 600;
  border: 1px solid var(--border); color: var(--fg);
  transition: background 0.2s;
}
.link-btn:hover { background: var(--bg-alt); color: var(--fg); }
.link-btn-primary { background: var(--primary); border-color: var(--primary); color: white; }
.link-btn-primary:hover { background: var(--primary-hover); color: white; }

/* ─── Skills ─── */
.skills-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 24px;
}
.skill-group {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
}
.skill-group h3 { font-weight: 700; margin-bottom: 12px; }
.skill-group ul { display: flex; flex-direction: column; gap: 6px; }
.skill-group li { color: var(--fg-muted); font-size: 0.9rem; }

/* ─── Contact Form ─── */
.contact-container { max-width: 640px; }
.contact-form { display: flex; flex-direction: column; gap: 16px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 500px) { .form-row { grid-template-columns: 1fr; } }
.form-group { display: flex; flex-direction: column; gap: 6px; }
.form-group label { font-size: 0.9rem; font-weight: 600; }
.form-group input, .form-group textarea {
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--fg);
  font-size: 1rem;
  font-family: inherit;
  outline: none;
  transition: border-color 0.2s;
}
.form-group input:focus, .form-group textarea:focus { border-color: var(--primary); }

/* ─── Footer ─── */
.footer {
  padding: 24px;
  text-align: center;
  color: var(--fg-muted);
  font-size: 0.875rem;
  border-top: 1px solid var(--border);
}

/* ─── Responsive ─── */
@media (max-width: 768px) {
  .nav-links { display: none; }
  .hero { padding: 60px 20px; }
}

What This Project Demonstrates

  • Semantic HTML — nav, section, article, footer, form elements
  • CSS custom properties — theme system for dark/light mode
  • CSS Grid — project cards, skills layout
  • Flexbox — navigation, hero buttons
  • Responsive design — media queries, fluid typography with clamp()
  • CSS transitions — hover effects on cards and buttons
  • Dark mode — toggled with a class on <html>, everything via CSS variables

Deploy it free to GitHub Pages, Netlify, or Vercel — all three support static HTML/CSS with zero configuration.