I've built portfolio sites for clients, for developers I've mentored, and for myself multiple times as my work evolved. The pattern that works consistently is simpler than most tutorials make it — six sections, Bootstrap 5.3, one SCSS variables file, and a contact form that actually sends. Here's the complete guide.
Project Setup
# Create project structure
mkdir portfolio && cd portfolio
mkdir -p assets/{css,scss,js,images,fonts}
touch index.html assets/scss/style.scss
Install Bootstrap 5.3 via npm or CDN. For a portfolio — CDN is fine unless you want SCSS variable customization. If you want to customize colors and fonts — npm and SCSS.
npm install bootstrap@5.3
// assets/scss/style.scss — correct import order
// 1. Google Fonts
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
// 2. Your Bootstrap variable overrides — BEFORE Bootstrap import
$primary: #fd4766;
$font-family-base: 'Inter', sans-serif;
$border-radius: 8px;
$border-radius-lg: 12px;
$box-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
$box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
// 3. Bootstrap
@import 'bootstrap/scss/bootstrap';
// 4. Your custom styles after Bootstrap
// Custom styles below
HTML Structure — Six Sections
html
<!DOCTYPE html>
<html lang="en" data-bs-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Your name — Frontend Developer specializing in Angular, Bootstrap, and React">
<title>Your Name — Frontend Developer</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<!-- 1. Navigation -->
<nav class="navbar navbar-expand-lg sticky-top bg-white border-bottom">...</nav>
<!-- 2. Hero -->
<section id="hero" class="hero-section py-100">...</section>
<!-- 3. Projects -->
<section id="projects" class="projects-section py-80 bg-light">...</section>
<!-- 4. Skills -->
<section id="skills" class="skills-section py-80">...</section>
<!-- 5. About -->
<section id="about" class="about-section py-80 bg-light">...</section>
<!-- 6. Contact -->
<section id="contact" class="contact-section py-80">...</section>
<!-- Footer -->
<footer class="footer py-4 bg-dark text-white">...</footer>
<script src="assets/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js"></script>
</body>
</html>
Six sections. That's all a portfolio needs. Resist the urge to add more.
Section 1 — Navigation
<nav class="navbar navbar-expand-lg sticky-top bg-white border-bottom shadow-sm">
<div class="container">
<a class="navbar-brand fw-bold text-primary" href="#">
YourName
</a>
<button class="navbar-toggler border-0" type="button"
data-bs-toggle="collapse" data-bs-target="#navMenu">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navMenu">
<ul class="navbar-nav ms-auto gap-1">
<li class="nav-item">
<a class="nav-link px-3" href="#projects">Projects</a>
</li>
<li class="nav-item">
<a class="nav-link px-3" href="#skills">Skills</a>
</li>
<li class="nav-item">
<a class="nav-link px-3" href="#about">About</a>
</li>
<li class="nav-item">
<a class="btn btn-primary px-4 ms-2" href="#contact">
Hire Me
</a>
</li>
</ul>
</div>
</div>
</nav>
sticky-top keeps the navbar visible while scrolling. border-bottom shadow-sm gives it subtle depth. The "Hire Me" button as a CTA in the navbar gets clicks — more than a plain nav link.
Section 2 — Hero
<section id="hero" class="hero-section py-100">
<div class="container">
<div class="row align-items-center g-5">
<div class="col-lg-6 order-2 order-lg-1">
<div class="hero-badge d-inline-flex align-items-center
gap-2 bg-primary-subtle text-primary
rounded-pill px-3 py-2 mb-4 small fw-semibold">
<span class="dot bg-primary rounded-circle"
style="width:8px;height:8px;"></span>
Available for new projects
</div>
<h1 class="display-5 fw-bold lh-sm mb-3">
Frontend Developer<br>
<span class="text-primary">Building Angular</span><br>
& Bootstrap Apps
</h1>
<p class="lead text-muted mb-4">
I build production-ready admin dashboards, SaaS applications,
and landing pages with Angular 22, Bootstrap 5.3, and React 19.
12+ years of experience shipping clean code.
</p>
<div class="d-flex flex-wrap gap-3">
<a href="#projects" class="btn btn-primary btn-lg px-4">
View My Work
</a>
<a href="#contact" class="btn btn-outline-secondary btn-lg px-4">
Get In Touch
</a>
</div>
<div class="hero-stats d-flex gap-4 mt-5">
<div>
<div class="h4 fw-bold mb-0">12+</div>
<div class="text-muted small">Years Experience</div>
</div>
<div>
<div class="h4 fw-bold mb-0">50+</div>
<div class="text-muted small">Projects Shipped</div>
</div>
<div>
<div class="h4 fw-bold mb-0">20+</div>
<div class="text-muted small">Happy Clients</div>
</div>
</div>
</div>
<div class="col-lg-6 order-1 order-lg-2 text-center">
<div class="hero-image-wrapper position-relative d-inline-block">
<img src="assets/images/your-photo.jpg"
alt="Your Name — Frontend Developer"
class="img-fluid rounded-4 shadow-lg"
style="max-width: 420px;">
<div class="hero-badge-card card border-0 shadow position-absolute
bottom-0 start-0 translate-middle-y ms-n3 p-3">
<div class="d-flex align-items-center gap-2">
<div class="badge-icon bg-success rounded-2 p-2">
<i class="bx bx-check text-white fs-5"></i>
</div>
<div>
<div class="fw-semibold small">Available Now</div>
<div class="text-muted" style="font-size:11px;">
Open to opportunities
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
Photo on right on desktop, above content on mobile via order-2 order-lg-1. Stats below the CTA buttons. Availability badge card floating on the photo — a small detail that makes it feel designed rather than assembled.
Section 3 — Projects
<section id="projects" class="projects-section py-80 bg-light">
<div class="container">
<div class="row mb-50">
<div class="col-lg-6">
<h2 class="h1 fw-bold mb-3">Selected Work</h2>
<p class="text-muted lead">
Projects I've built — what the problem was,
what I did, what shipped.
</p>
</div>
</div>
<div class="row g-4">
<!-- Project Card -->
<div class="col-lg-6">
<div class="project-card card border-0 shadow-sm h-100
overflow-hidden hover-lift">
<div class="project-image position-relative overflow-hidden">
<img src="assets/images/project-1.jpg"
alt="Project Name"
class="img-fluid w-100"
style="height: 240px; object-fit: cover;">
<div class="project-overlay position-absolute inset-0
bg-dark bg-opacity-75 d-flex align-items-center
justify-content-center gap-3 opacity-0
transition-opacity">
<a href="#" class="btn btn-light btn-sm">
Live Demo
</a>
<a href="#" class="btn btn-outline-light btn-sm">
Case Study
</a>
</div>
</div>
<div class="card-body p-4">
<div class="d-flex flex-wrap gap-2 mb-3">
<span class="badge bg-primary-subtle text-primary fw-normal">
Angular 22
</span>
<span class="badge bg-primary-subtle text-primary fw-normal">
Bootstrap 5.3
</span>
<span class="badge bg-primary-subtle text-primary fw-normal">
TypeScript
</span>
</div>
<h3 class="h5 fw-semibold mb-2">SaaS Admin Dashboard</h3>
<p class="text-muted small mb-3">
Built a complete admin dashboard for a SaaS product
with user management, analytics, and billing.
Angular 22 with Signals throughout, Bootstrap 5.3 for UI.
</p>
<div class="d-flex gap-4">
<div>
<div class="fw-bold small">40%</div>
<div class="text-muted" style="font-size:11px;">
Load time improvement
</div>
</div>
<div>
<div class="fw-bold small">12K</div>
<div class="text-muted" style="font-size:11px;">
Monthly active users
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Repeat for 3-4 projects -->
</div>
</div>
</section>
Project overlay appears on hover — CSS handles this. Tech stack badges. Brief description with specific contribution. Outcome metrics where you have them. This structure works for 3-4 featured projects.
Section 4 — Skills
<section id="skills" class="skills-section py-80">
<div class="container">
<div class="row mb-50">
<div class="col-lg-6">
<h2 class="h1 fw-bold mb-3">Skills & Technologies</h2>
<p class="text-muted">
What I work with daily. Grouped by area.
</p>
</div>
</div>
<div class="row g-4">
<div class="col-md-6 col-lg-4">
<div class="skill-group card border-0 bg-light h-100 p-4">
<div class="skill-icon mb-3">
<div class="bg-primary-subtle rounded-3 p-3 d-inline-block">
<i class="bx bxl-angular text-primary fs-3"></i>
</div>
</div>
<h3 class="h6 fw-semibold mb-3">Frontend Frameworks</h3>
<div class="d-flex flex-wrap gap-2">
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
Angular 22
</span>
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
React 19
</span>
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
Next.js 16
</span>
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
TypeScript
</span>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="skill-group card border-0 bg-light h-100 p-4">
<div class="skill-icon mb-3">
<div class="bg-primary-subtle rounded-3 p-3 d-inline-block">
<i class="bx bxl-bootstrap text-primary fs-3"></i>
</div>
</div>
<h3 class="h6 fw-semibold mb-3">CSS & Styling</h3>
<div class="d-flex flex-wrap gap-2">
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
Bootstrap 5.3
</span>
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
SCSS/Sass
</span>
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
Tailwind CSS
</span>
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
CSS Custom Properties
</span>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="skill-group card border-0 bg-light h-100 p-4">
<div class="skill-icon mb-3">
<div class="bg-primary-subtle rounded-3 p-3 d-inline-block">
<i class="bx bx-code-alt text-primary fs-3"></i>
</div>
</div>
<h3 class="h6 fw-semibold mb-3">Tools & Backend</h3>
<div class="d-flex flex-wrap gap-2">
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
Node.js
</span>
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
Git
</span>
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
AWS
</span>
<span class="badge bg-white text-dark border fw-normal py-2 px-3">
PostgreSQL
</span>
</div>
</div>
</div>
</div>
</div>
</section>
Grouped by category. Badge tags instead of progress bars. Clean cards with icon per category. Honest — only list what you actually use.
Section 5 — About
<section id="about" class="about-section py-80 bg-light">
<div class="container">
<div class="row align-items-center g-5">
<div class="col-lg-5">
<img src="assets/images/about-photo.jpg"
alt="About Your Name"
class="img-fluid rounded-4 shadow">
</div>
<div class="col-lg-7">
<h2 class="h1 fw-bold mb-4">About Me</h2>
<p class="text-muted mb-3">
I've been building web applications since 2013.
Started with Bootstrap 3, grew into Angular and React,
now building production systems with Angular 22,
Bootstrap 5.3, and Next.js 16.
</p>
<p class="text-muted mb-4">
I care about code that's readable in six months,
accessible to everyone, and fast on a real device
on a real connection. Not just fast in a Lighthouse
test on a MacBook Pro on fiber.
</p>
<div class="row g-3 mb-4">
<div class="col-6">
<div class="d-flex align-items-center gap-2">
<i class="bx bx-map text-primary"></i>
<span class="small">India</span>
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-center gap-2">
<i class="bx bx-briefcase text-primary"></i>
<span class="small">Available for remote</span>
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-center gap-2">
<i class="bx bx-calendar text-primary"></i>
<span class="small">12+ years experience</span>
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-center gap-2">
<i class="bx bx-code-block text-primary"></i>
<span class="small">Full stack capable</span>
</div>
</div>
</div>
<div class="d-flex gap-3">
<a href="assets/your-resume.pdf"
class="btn btn-primary px-4"
download>
Download Resume
</a>
<a href="https://github.com/yourusername"
class="btn btn-outline-secondary px-4"
target="_blank">
GitHub Profile
</a>
</div>
</div>
</div>
</div>
</section>
Short paragraphs. Specific details — not generic "passionate about clean code." Quick facts in a grid. Resume download and GitHub link as CTAs.
Section 6 — Contact
<section id="contact" class="contact-section py-80">
<div class="container">
<div class="row justify-content-center mb-50">
<div class="col-lg-6 text-center">
<h2 class="h1 fw-bold mb-3">Let's Work Together</h2>
<p class="text-muted">
Available for freelance projects and full-time opportunities.
Response time: within 24 hours.
</p>
</div>
</div>
<div class="row g-4 justify-content-center">
<div class="col-lg-7">
<div class="card border-0 shadow-sm p-4 p-md-5">
<form action="https://formspree.io/f/your-form-id"
method="POST">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label fw-medium small">Name *</label>
<input type="text"
name="name"
class="form-control"
placeholder="Your name"
required>
</div>
<div class="col-md-6">
<label class="form-label fw-medium small">Email *</label>
<input type="email"
name="email"
class="form-control"
placeholder="your@email.com"
required>
</div>
<div class="col-12">
<label class="form-label fw-medium small">
Project Type
</label>
<select name="project_type" class="form-select">
<option value="">Select project type</option>
<option>Admin Dashboard</option>
<option>Landing Page</option>
<option>Full Stack Application</option>
<option>Consultation</option>
<option>Other</option>
</select>
</div>
<div class="col-12">
<label class="form-label fw-medium small">Message *</label>
<textarea name="message"
class="form-control"
rows="5"
placeholder="Tell me about your project..."
required></textarea>
</div>
<div class="col-12">
<button type="submit"
class="btn btn-primary btn-lg w-100">
Send Message
</button>
</div>
</div>
</form>
</div>
</div>
<div class="col-lg-3">
<div class="d-flex flex-column gap-3">
<div class="card border-0 bg-light p-4">
<div class="d-flex align-items-center gap-3">
<div class="bg-primary-subtle rounded-3 p-3">
<i class="bx bx-envelope text-primary fs-5"></i>
</div>
<div>
<div class="fw-semibold small">Email</div>
<a href="mailto:your@email.com"
class="text-muted small text-decoration-none">
your@email.com
</a>
</div>
</div>
</div>
<div class="card border-0 bg-light p-4">
<div class="d-flex align-items-center gap-3">
<div class="bg-primary-subtle rounded-3 p-3">
<i class="bx bxl-linkedin text-primary fs-5"></i>
</div>
<div>
<div class="fw-semibold small">LinkedIn</div>
<a href="https://linkedin.com/in/yourprofile"
class="text-muted small text-decoration-none"
target="_blank">
linkedin.com/in/yourprofile
</a>
</div>
</div>
</div>
<div class="card border-0 bg-light p-4">
<div class="d-flex align-items-center gap-3">
<div class="bg-primary-subtle rounded-3 p-3">
<i class="bx bxl-github text-primary fs-5"></i>
</div>
<div>
<div class="fw-semibold small">GitHub</div>
<a href="https://github.com/yourusername"
class="text-muted small text-decoration-none"
target="_blank">
github.com/yourusername
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
Formspree for the form — no backend needed. Project type dropdown helps qualify enquiries. Contact info cards beside the form for people who prefer email directly.
Dark Mode Toggle
// assets/js/main.js
// Theme toggle
const themeToggle = document.getElementById('themeToggle')
const savedTheme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark' : 'light')
document.documentElement.setAttribute('data-bs-theme', savedTheme)
themeToggle?.addEventListener('click', () => {
const current = document.documentElement
.getAttribute('data-bs-theme')
const next = current === 'dark' ? 'light' : 'dark'
document.documentElement.setAttribute('data-bs-theme', next)
localStorage.setItem('theme', next)
})
// Smooth scroll for nav links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', (e) => {
e.preventDefault()
const target = document.querySelector(
anchor.getAttribute('href')
)
target?.scrollIntoView({ behavior: 'smooth', block: 'start' })
})
})
// Active nav link on scroll
const sections = document.querySelectorAll('section[id]')
const navLinks = document.querySelectorAll('.navbar-nav .nav-link')
window.addEventListener('scroll', () => {
const scrollY = window.scrollY
sections.forEach(section => {
const sectionTop = section.offsetTop - 100
const sectionHeight = section.offsetHeight
const sectionId = section.getAttribute('id')
if (scrollY >= sectionTop &&
scrollY < sectionTop + sectionHeight) {
navLinks.forEach(link => {
link.classList.remove('active')
if (link.getAttribute('href') === `#${sectionId}`) {
link.classList.add('active')
}
})
}
})
})
Three JavaScript behaviors — theme toggle, smooth scroll, active nav link on scroll. No jQuery. Pure vanilla JavaScript. Everything Bootstrap handles through data-bs-* attributes.
CSS for Custom Touches
// assets/scss/style.scss additions
// Spacing utilities
.py-80 { padding-top: 80px; padding-bottom: 80px; }
.py-100 { padding-top: 100px; padding-bottom: 100px; }
.mb-50 { margin-bottom: 50px; }
// Project card hover
.hover-lift {
transition: transform 0.2s ease, box-shadow 0.2s ease;
&:hover {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12) !important;
.project-overlay {
opacity: 1 !important;
}
}
}
.project-overlay {
transition: opacity 0.2s ease;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
// Dark mode custom properties
[data-bs-theme="dark"] {
--bs-body-bg: #0f1117;
--bs-secondary-bg: #1a1f2e;
.bg-light {
background-color: #1a1f2e !important;
}
.card {
background-color: #1e2130;
border-color: #2d3748;
}
.skill-group {
background-color: #1e2130 !important;
}
}
Minimal custom CSS on top of Bootstrap. Hover lift effect for project cards. Dark mode overrides for custom sections. Everything else is Bootstrap utilities.
If you want a Bootstrap 5.3 portfolio template already built with this structure — browse LettStart Design. Zero jQuery, Bootstrap 5.3, Lighthouse above 85. Use FIRST30 for 30% off.
Top comments (0)