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 & CSS · © 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.