Bootstrap 5 Tutorial
v5.3.0Bootstrap 5 Tutorial
Portfolio Website Project
Project Goal: Create a modern, responsive portfolio website to showcase work, skills, and experience.
Project Overview
This project demonstrates how to build a professional portfolio website using Bootstrap 5. The portfolio includes a hero section, about me, skills, projects gallery, testimonials, and contact form.
Portfolio Sections
- Hero section with profile
- About me section
- Skills with progress bars
- Projects portfolio gallery
- Testimonials carousel
- Contact form
- Social media links
- Download resume button
Design Features
- Dark/Light mode toggle
- Smooth scrolling navigation
- Project filtering system
- Image lightbox gallery
- Interactive skill bars
- Responsive image grid
- Animated scroll effects
- Print-friendly resume
Complete Portfolio Structure
HTML Layout
<!-- Portfolio Website -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top shadow-sm">
<div class="container">
<a class="navbar-brand fw-bold" href="#">
<span class="text-primary">Port</span>folio
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link active" href="#home">Home</a></li>
<li class="nav-item"><a class="nav-link" href="#about">About</a></li>
<li class="nav-item"><a class="nav-link" href="#skills">Skills</a></li>
<li class="nav-item"><a class="nav-link" href="#portfolio">Portfolio</a></li>
<li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
<li class="nav-item">
<button class="btn btn-outline-primary ms-2" id="themeToggle">
<i class="fas fa-moon"></i>
</button>
</li>
</ul>
</div>
</div>
</nav>
<!-- Hero Section -->
<section id="home" class="hero-section py-5 mt-5">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-6">
<h6 class="text-primary mb-3">Hello, I'm</h6>
<h1 class="display-4 fw-bold mb-3">Alex Johnson</h1>
<h2 class="text-muted mb-4">Full Stack Developer & UI/UX Designer</h2>
<p class="lead mb-4">I create beautiful, functional websites and applications that deliver exceptional user experiences.</p>
<div class="d-flex flex-wrap gap-3">
<a href="#contact" class="btn btn-primary btn-lg">
<i class="fas fa-paper-plane me-2"></i> Hire Me
</a>
<a href="#" class="btn btn-outline-primary btn-lg">
<i class="fas fa-download me-2"></i> Download CV
</a>
</div>
<div class="mt-5">
<h6 class="mb-3">Follow Me:</h6>
<div class="d-flex gap-3">
<a href="#" class="social-icon">
<i class="fab fa-github fa-2x"></i>
</a>
<a href="#" class="social-icon">
<i class="fab fa-linkedin fa-2x"></i>
</a>
<a href="#" class="social-icon">
<i class="fab fa-twitter fa-2x"></i>
</a>
<a href="#" class="social-icon">
<i class="fab fa-dribbble fa-2x"></i>
</a>
</div>
</div>
</div>
<div class="col-lg-6 text-center">
<div class="profile-image-container">
<img src="https://via.placeholder.com/400x400" class="img-fluid rounded-circle shadow-lg" alt="Alex Johnson">
<div class="experience-badge">
<span class="badge bg-primary p-3">5+ Years Experience</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- About Section -->
<section id="about" class="py-5 bg-light">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-5">
<img src="https://via.placeholder.com/500x600" class="img-fluid rounded shadow" alt="About Me">
</div>
<div class="col-lg-7">
<h2 class="display-5 fw-bold mb-4">About Me</h2>
<p class="lead mb-4">I'm passionate about creating digital experiences that are both beautiful and functional.</p>
<p class="mb-4">With over 5 years of experience in web development and design, I specialize in creating responsive websites, web applications, and user interfaces that deliver exceptional user experiences.</p>
<div class="row mt-4">
<div class="col-md-6">
<ul class="list-unstyled">
<li class="mb-3">
<strong><i class="fas fa-calendar-alt text-primary me-2"></i> Age:</strong>
<span class="ms-2">28</span>
</li>
<li class="mb-3">
<strong><i class="fas fa-envelope text-primary me-2"></i> Email:</strong>
<span class="ms-2">alex@example.com</span>
</li>
<li class="mb-3">
<strong><i class="fas fa-phone text-primary me-2"></i> Phone:</strong>
<span class="ms-2">+1 234 567 8900</span>
</li>
</ul>
</div>
<div class="col-md-6">
<ul class="list-unstyled">
<li class="mb-3">
<strong><i class="fas fa-map-marker-alt text-primary me-2"></i> Location:</strong>
<span class="ms-2">San Francisco, CA</span>
</li>
<li class="mb-3">
<strong><i class="fas fa-briefcase text-primary me-2"></i> Freelance:</strong>
<span class="ms-2">Available</span>
</li>
<li class="mb-3">
<strong><i class="fas fa-language text-primary me-2"></i> Languages:</strong>
<span class="ms-2">English, Spanish</span>
</li>
</ul>
</div>
</div>
<div class="mt-4">
<a href="#portfolio" class="btn btn-primary">
<i class="fas fa-briefcase me-2"></i> View My Work
</a>
</div>
</div>
</div>
</div>
</section>
<!-- Skills Section -->
<section id="skills" class="py-5">
<div class="container">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold">My Skills</h2>
<p class="lead text-muted">Technical skills and expertise</p>
</div>
<div class="row">
<div class="col-lg-6">
<div class="skill-item mb-4">
<div class="d-flex justify-content-between mb-2">
<span class="fw-bold">HTML/CSS</span>
<span>95%</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-primary" role="progressbar" style="width: 95%"></div>
</div>
</div>
<div class="skill-item mb-4">
<div class="d-flex justify-content-between mb-2">
<span class="fw-bold">JavaScript</span>
<span>90%</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-success" role="progressbar" style="width: 90%"></div>
</div>
</div>
<div class="skill-item mb-4">
<div class="d-flex justify-content-between mb-2">
<span class="fw-bold">React.js</span>
<span>85%</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-info" role="progressbar" style="width: 85%"></div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="skill-item mb-4">
<div class="d-flex justify-content-between mb-2">
<span class="fw-bold">Node.js</span>
<span>80%</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-warning" role="progressbar" style="width: 80%"></div>
</div>
</div>
<div class="skill-item mb-4">
<div class="d-flex justify-content-between mb-2">
<span class="fw-bold">UI/UX Design</span>
<span>88%</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-danger" role="progressbar" style="width: 88%"></div>
</div>
</div>
<div class="skill-item mb-4">
<div class="d-flex justify-content-between mb-2">
<span class="fw-bold">Bootstrap</span>
<span>95%</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-primary" role="progressbar" style="width: 95%"></div>
</div>
</div>
</div>
</div>
<div class="row mt-5">
<div class="col-md-4 mb-4">
<div class="card h-100 border-0 shadow-sm text-center p-4">
<div class="card-body">
<div class="display-3 text-primary mb-3">50+</div>
<h5 class="card-title">Projects Completed</h5>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card h-100 border-0 shadow-sm text-center p-4">
<div class="card-body">
<div class="display-3 text-success mb-3">30+</div>
<h5 class="card-title">Happy Clients</h5>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card h-100 border-0 shadow-sm text-center p-4">
<div class="card-body">
<div class="display-3 text-info mb-3">5+</div>
<h5 class="card-title">Years Experience</h5>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Portfolio Section -->
<section id="portfolio" class="py-5 bg-light">
<div class="container">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold">My Portfolio</h2>
<p class="lead text-muted">A selection of my recent work</p>
</div>
<!-- Portfolio Filter -->
<div class="text-center mb-4">
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<button class="btn btn-outline-primary active" data-filter="all">All</button>
<button class="btn btn-outline-primary" data-filter="web">Web Design</button>
<button class="btn btn-outline-primary" data-filter="app">Web App</button>
<button class="btn btn-outline-primary" data-filter="mobile">Mobile</button>
</div>
</div>
<!-- Portfolio Grid -->
<div class="row portfolio-grid">
<div class="col-lg-4 col-md-6 mb-4" data-category="web">
<div class="portfolio-item card border-0 shadow-sm overflow-hidden">
<img src="https://via.placeholder.com/400x300" class="card-img-top" alt="Project 1">
<div class="portfolio-overlay">
<div class="overlay-content">
<h5 class="text-white">E-commerce Website</h5>
<p class="text-white-50">Full online store with cart and checkout</p>
<a href="#" class="btn btn-light btn-sm">View Details</a>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-md-6 mb-4" data-category="app">
<div class="portfolio-item card border-0 shadow-sm overflow-hidden">
<img src="https://via.placeholder.com/400x300" class="card-img-top" alt="Project 2">
<div class="portfolio-overlay">
<div class="overlay-content">
<h5 class="text-white">Task Management App</h5>
<p class="text-white-50">React-based productivity application</p>
<a href="#" class="btn btn-light btn-sm">View Details</a>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-md-6 mb-4" data-category="mobile">
<div class="portfolio-item card border-0 shadow-sm overflow-hidden">
<img src="https://via.placeholder.com/400x300" class="card-img-top" alt="Project 3">
<div class="portfolio-overlay">
<div class="overlay-content">
<h5 class="text-white">Fitness Mobile App</h5>
<p class="text-white-50">React Native fitness tracking application</p>
<a href="#" class="btn btn-light btn-sm">View Details</a>
</div>
</div>
</div>
</div>
<!-- Add more portfolio items here -->
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="py-5">
<div class="container">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold">Get In Touch</h2>
<p class="lead text-muted">Feel free to contact me for any projects or opportunities</p>
</div>
<div class="row">
<div class="col-lg-8 mx-auto">
<form id="contactForm">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Your Name</label>
<input type="text" class="form-control" id="name" required>
</div>
<div class="col-md-6 mb-3">
<label for="email" class="form-label">Email Address</label>
<input type="email" class="form-control" id="email" required>
</div>
</div>
<div class="mb-3">
<label for="subject" class="form-label">Subject</label>
<input type="text" class="form-control" id="subject" required>
</div>
<div class="mb-3">
<label for="message" class="form-label">Message</label>
<textarea class="form-control" id="message" rows="5" required></textarea>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary btn-lg">
<i class="fas fa-paper-plane me-2"></i> Send Message
</button>
</div>
</form>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="bg-dark text-white py-5">
<div class="container">
<div class="row">
<div class="col-lg-4 mb-4">
<h3 class="mb-3">
<span class="text-primary">Port</span>folio
</h3>
<p>Creating beautiful digital experiences that make a difference.</p>
<div class="d-flex gap-3 mt-4">
<a href="#" class="text-white-50"><i class="fab fa-github fa-lg"></i></a>
<a href="#" class="text-white-50"><i class="fab fa-linkedin fa-lg"></i></a>
<a href="#" class="text-white-50"><i class="fab fa-twitter fa-lg"></i></a>
<a href="#" class="text-white-50"><i class="fab fa-dribbble fa-lg"></i></a>
</div>
</div>
<div class="col-lg-4 mb-4">
<h5 class="mb-3">Quick Links</h5>
<ul class="list-unstyled">
<li class="mb-2"><a href="#home" class="text-white-50 text-decoration-none">Home</a></li>
<li class="mb-2"><a href="#about" class="text-white-50 text-decoration-none">About</a></li>
<li class="mb-2"><a href="#skills" class="text-white-50 text-decoration-none">Skills</a></li>
<li class="mb-2"><a href="#portfolio" class="text-white-50 text-decoration-none">Portfolio</a></li>
<li class="mb-2"><a href="#contact" class="text-white-50 text-decoration-none">Contact</a></li>
</ul>
</div>
<div class="col-lg-4 mb-4">
<h5 class="mb-3">Contact Info</h5>
<ul class="list-unstyled">
<li class="mb-2">
<i class="fas fa-map-marker-alt text-primary me-2"></i>
San Francisco, CA
</li>
<li class="mb-2">
<i class="fas fa-phone text-primary me-2"></i>
+1 234 567 8900
</li>
<li class="mb-2">
<i class="fas fa-envelope text-primary me-2"></i>
alex@example.com
</li>
</ul>
</div>
</div>
<hr class="my-4 text-white-50">
<div class="text-center">
<p class="mb-0">© 2024 Alex Johnson. All rights reserved.</p>
</div>
</div>
</footer>Portfolio Styling CSS
Custom Portfolio Styles
/* Portfolio Custom Styles */
:root {
--primary-color: #0d6efd;
--secondary-color: #6c757d;
--dark-color: #212529;
--light-color: #f8f9fa;
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
/* Fixed navbar spacing */
body {
padding-top: 76px; /* Height of fixed navbar */
}
/* Hero Section */
.hero-section {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
min-height: calc(100vh - 76px);
display: flex;
align-items: center;
}
.profile-image-container {
position: relative;
display: inline-block;
}
.profile-image-container img {
width: 400px;
height: 400px;
object-fit: cover;
border: 5px solid white;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.experience-badge {
position: absolute;
bottom: 20px;
right: -20px;
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
/* Social Icons */
.social-icon {
color: var(--secondary-color);
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
border-radius: 50%;
background: white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.social-icon:hover {
color: white;
background: var(--primary-color);
transform: translateY(-3px);
text-decoration: none;
}
/* Progress Bars */
.progress {
border-radius: 10px;
overflow: hidden;
}
.progress-bar {
border-radius: 10px;
transition: width 1.5s ease-in-out;
}
.skill-item:hover .progress-bar {
transform: scale(1.02);
}
/* Portfolio Items */
.portfolio-item {
position: relative;
overflow: hidden;
cursor: pointer;
transition: transform 0.3s ease;
}
.portfolio-item:hover {
transform: translateY(-10px);
}
.portfolio-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(13, 110, 253, 0.9);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.portfolio-item:hover .portfolio-overlay {
opacity: 1;
}
.overlay-content {
text-align: center;
padding: 20px;
}
/* Section Spacing */
section {
padding: 100px 0;
}
@media (max-width: 768px) {
section {
padding: 60px 0;
}
}
/* Navigation Active State */
.nav-link {
position: relative;
padding: 0.5rem 1rem;
}
.nav-link::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background: var(--primary-color);
transition: all 0.3s ease;
transform: translateX(-50%);
}
.nav-link:hover::after,
.nav-link.active::after {
width: calc(100% - 2rem);
}
/* Dark Theme */
.dark-theme {
background-color: var(--dark-color);
color: var(--light-color);
}
.dark-theme .hero-section {
background: linear-gradient(135deg, #212529 0%, #343a40 100%);
}
.dark-theme .bg-light {
background-color: #343a40 !important;
color: var(--light-color);
}
.dark-theme .card {
background-color: #2d3748;
border-color: #4a5568;
}
.dark-theme .form-control {
background-color: #2d3748;
border-color: #4a5568;
color: var(--light-color);
}
.dark-theme .form-control:focus {
background-color: #2d3748;
border-color: var(--primary-color);
color: var(--light-color);
}
.dark-theme .social-icon {
background-color: #2d3748;
color: var(--light-color);
}
/* Animations */
.fade-in {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Portfolio Filter */
.btn-group-toggle .btn.active {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
/* Contact Form */
#contactForm .form-control {
padding: 0.75rem 1rem;
border-radius: 0.5rem;
border: 2px solid #dee2e6;
transition: all 0.3s ease;
}
#contactForm .form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
/* Responsive Adjustments */
@media (max-width: 992px) {
.profile-image-container img {
width: 300px;
height: 300px;
}
}
@media (max-width: 768px) {
.profile-image-container img {
width: 250px;
height: 250px;
}
.hero-section {
text-align: center;
}
.experience-badge {
right: 50%;
transform: translateX(50%);
}
}
/* Print Styles */
@media print {
.navbar,
.btn,
.social-icon,
footer {
display: none !important;
}
body {
padding-top: 0;
}
.hero-section {
min-height: auto;
page-break-after: avoid;
}
section {
padding: 30px 0;
page-break-inside: avoid;
}
}Portfolio JavaScript Functionality
Interactive Features
// Portfolio JavaScript Functionality
document.addEventListener('DOMContentLoaded', function() {
// Theme Toggle
const themeToggle = document.getElementById('themeToggle');
const themeIcon = themeToggle?.querySelector('i');
if (themeToggle) {
themeToggle.addEventListener('click', function() {
document.body.classList.toggle('dark-theme');
if (document.body.classList.contains('dark-theme')) {
themeIcon?.classList.remove('fa-moon');
themeIcon?.classList.add('fa-sun');
localStorage.setItem('portfolio-theme', 'dark');
} else {
themeIcon?.classList.remove('fa-sun');
themeIcon?.classList.add('fa-moon');
localStorage.setItem('portfolio-theme', 'light');
}
});
}
// Load saved theme
const savedTheme = localStorage.getItem('portfolio-theme');
if (savedTheme === 'dark') {
document.body.classList.add('dark-theme');
if (themeIcon) {
themeIcon.classList.remove('fa-moon');
themeIcon.classList.add('fa-sun');
}
}
// Portfolio Filtering
const filterButtons = document.querySelectorAll('[data-filter]');
const portfolioItems = document.querySelectorAll('.portfolio-item');
filterButtons.forEach(button => {
button.addEventListener('click', function() {
// Remove active class from all buttons
filterButtons.forEach(btn => {
btn.classList.remove('active');
btn.classList.remove('btn-primary');
btn.classList.add('btn-outline-primary');
});
// Add active class to clicked button
this.classList.add('active');
this.classList.add('btn-primary');
this.classList.remove('btn-outline-primary');
const filter = this.getAttribute('data-filter');
// Show/hide portfolio items
portfolioItems.forEach(item => {
const category = item.parentElement.getAttribute('data-category');
if (filter === 'all' || filter === category) {
item.parentElement.style.display = 'block';
setTimeout(() => {
item.classList.add('fade-in');
}, 50);
} else {
item.parentElement.style.display = 'none';
item.classList.remove('fade-in');
}
});
});
});
// Smooth Scrolling for Navigation Links
const navLinks = document.querySelectorAll('.navbar-nav a');
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
const targetId = this.getAttribute('href');
if (targetId.startsWith('#')) {
e.preventDefault();
const targetElement = document.querySelector(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 70,
behavior: 'smooth'
});
// Update active nav link
navLinks.forEach(navLink => {
navLink.classList.remove('active');
});
this.classList.add('active');
}
}
});
});
// Contact Form Submission
const contactForm = document.getElementById('contactForm');
if (contactForm) {
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
// Get form data
const formData = new FormData(this);
const formObject = {};
formData.forEach((value, key) => {
formObject[key] = value;
});
// Here you would typically send the data to a server
console.log('Form submitted:', formObject);
// Show success message
alert('Thank you for your message! I'll get back to you soon.');
// Reset form
this.reset();
});
}
// Skill Bar Animation on Scroll
const skillBars = document.querySelectorAll('.progress-bar');
function animateSkillBars() {
skillBars.forEach(bar => {
const value = bar.style.width;
bar.style.width = '0';
setTimeout(() => {
bar.style.width = value;
}, 500);
});
}
// Animate skill bars when they come into view
const observerOptions = {
threshold: 0.5
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateSkillBars();
observer.unobserve(entry.target);
}
});
}, observerOptions);
const skillsSection = document.getElementById('skills');
if (skillsSection) {
observer.observe(skillsSection);
}
// Portfolio Item Lightbox
const portfolioItems = document.querySelectorAll('.portfolio-item');
portfolioItems.forEach(item => {
item.addEventListener('click', function() {
const image = this.querySelector('img').src;
const title = this.querySelector('h5').textContent;
const description = this.querySelector('p').textContent;
// Create lightbox modal
const lightboxHTML = `
<div class="modal fade" id="portfolioLightbox" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">${title}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<img src="${image}" class="img-fluid mb-3" alt="${title}">
<p>${description}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<a href="#" class="btn btn-primary">View Live Project</a>
</div>
</div>
</div>
</div>
`;
// Add modal to body and show it
document.body.insertAdjacentHTML('beforeend', lightboxHTML);
const modal = new bootstrap.Modal(document.getElementById('portfolioLightbox'));
modal.show();
// Remove modal after it's hidden
document.getElementById('portfolioLightbox').addEventListener('hidden.bs.modal', function() {
this.remove();
});
});
});
// Download CV Button
const downloadBtn = document.querySelector('.btn-outline-primary[href="#"]');
if (downloadBtn) {
downloadBtn.addEventListener('click', function(e) {
e.preventDefault();
// Simulate download
alert('CV download would start here. In a real implementation, this would download your resume file.');
// For demo purposes, create a sample CV
const cvContent = `
Alex Johnson - Full Stack Developer
====================================
Contact:
- Email: alex@example.com
- Phone: +1 234 567 8900
- Location: San Francisco, CA
Experience:
- Senior Developer at TechCorp (2020-Present)
- Web Developer at DigitalAgency (2018-2020)
- Freelance Developer (2016-2018)
Skills:
- HTML/CSS, JavaScript, React, Node.js
- UI/UX Design, Bootstrap, Responsive Design
- Git, REST APIs, Database Design
Education:
- BS Computer Science, Stanford University
`;
const blob = new Blob([cvContent], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'Alex_Johnson_CV.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
}
// Update active nav link on scroll
function updateActiveNavLink() {
const sections = document.querySelectorAll('section');
const navLinks = document.querySelectorAll('.navbar-nav a');
let current = '';
sections.forEach(section => {
const sectionTop = section.offsetTop - 100;
const sectionHeight = section.clientHeight;
if (scrollY >= sectionTop && scrollY < sectionTop + sectionHeight) {
current = section.getAttribute('id');
}
});
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${current}`) {
link.classList.add('active');
}
});
}
window.addEventListener('scroll', updateActiveNavLink);
// Initialize Bootstrap tooltips and popovers
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function(tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
popoverTriggerList.map(function(popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl);
});
});
// Typing animation for hero section
function typeWriter(element, text, speed = 50) {
let i = 0;
element.innerHTML = '';
function type() {
if (i < text.length) {
element.innerHTML += text.charAt(i);
i++;
setTimeout(type, speed);
}
}
type();
}
// Usage example for hero title animation
const heroTitle = document.querySelector('.hero-section h1');
if (heroTitle && !document.body.classList.contains('dark-theme')) {
const originalText = heroTitle.textContent;
setTimeout(() => {
typeWriter(heroTitle, originalText);
}, 1000);
}Portfolio Sections Explained
| Section | Purpose | Key Components |
|---|---|---|
| Hero Section | First impression and introduction | Profile image, name, title, call-to-action buttons, social links |
| About Section | Personal and professional background | Bio, personal details, contact information, experience summary |
| Skills Section | Showcase technical abilities | Progress bars, skill categories, statistics cards |
| Portfolio Section | Display work samples | Filterable project grid, hover effects, lightbox gallery |
| Contact Section | Enable communication | Contact form, validation, submission handling |
| Navigation | Site navigation | Smooth scrolling, active state highlighting, responsive menu |
| Footer | Additional information and links | Quick links, contact info, copyright, social media |
Responsive Design Features
Mobile Optimization
- Responsive navbar with hamburger menu
- Stacked layout for content sections
- Appropriate font sizes for mobile
- Touch-friendly buttons and links
- Optimized images for mobile bandwidth
- Vertical progress bars
Desktop Enhancement
- Side-by-side layouts for hero and about sections
- Multi-column grid for portfolio
- Hover effects and animations
- Larger typography for better readability
- Wider content areas
- Enhanced visual effects
SEO Best Practices
Portfolio SEO Checklist
- Meta Tags: Title, description, keywords
- Structured Data: Person schema for portfolio
- Image Optimization: Alt text, proper file names, compression
- Mobile-Friendly: Responsive design, fast loading
- Social Media: Open Graph tags for sharing
- Performance: Fast loading times, optimized assets
- Accessibility: ARIA labels, keyboard navigation, contrast
- Content: Clear headings, descriptive text, portfolio descriptions
Performance Optimization
Performance Tips
- Use responsive images with appropriate sizes
- Minify CSS and JavaScript files
- Lazy load portfolio images
- Use CDN for Bootstrap and Font Awesome
- Implement proper caching headers
- Optimize SVG icons
- Reduce HTTP requests
- Use CSS animations instead of JavaScript when possible