JavaScript Foundations β’ StreetGeek Academy
π§ MODULE 10: Final Project β Interactive Portfolio v2.0
Combine all the concepts from previous modules to create a dynamic, data-driven, interactive portfolio website that responds to user input,
loads data automatically, and saves preferences locally.
1) Project Overview
Youβll take your existing portfolio site and upgrade it into a fully interactive application using modern JavaScript.
π― Core Goals
- Fetch live data from GitHub
- Display project cards dynamically
- Add dark/light theme toggle (saved in
localStorage) - Create interactive UI components (menu, form, modals)
- Provide real-time validation and user feedback
- Store visitor preferences persistently
2) Folder Structure
Make sure your project is clean and modular:
portfolio-site/
βββ index.html
βββ about.html
βββ projects.html
βββ contact.html
β
βββ css/
β βββ style.css
β
βββ js/
β βββ app.js
β βββ data.js
β βββ utils.js
β βββ theme.js
β
βββ images/
3) Step-by-Step Project Tasks
πΉ Step 1: Setup Your Data
Create data.js:
const localProjects = [
{ title: "ShopBlocks WP", tech: "PHP / WooCommerce", link: "#" },
{ title: "Nfinite Dashboard", tech: "JavaScript / CSS Grid", link: "#" },
{ title: "BoldGrid Addon", tech: "WordPress / JavaScript", link: "#" }
];
Youβll display this data if GitHub data isnβt available.
πΉ Step 2: Dynamic Project Rendering
In app.js:
async function loadProjects() {
const projectGrid = document.getElementById("project-grid");
projectGrid.innerHTML = "<p>β³ Loading projects...</p>";
try {
const response = await fetch("https://api.github.com/users/SitesByYogi/repos");
const repos = await response.json();
const projects = repos.slice(0, 6);
projectGrid.innerHTML = "";
projects.forEach(repo => {
const card = document.createElement("div");
card.className = "card";
card.innerHTML = `
<h3>${repo.name}</h3>
<p>${repo.language || "JavaScript"}</p>
<a href="${repo.html_url}" target="_blank">View Project</a>
`;
projectGrid.appendChild(card);
});
localStorage.setItem("repos", JSON.stringify(projects));
localStorage.setItem("lastUpdated", new Date().toLocaleString());
} catch (error) {
projectGrid.innerHTML = "<p>β οΈ Unable to fetch GitHub projects. Showing local data.</p>";
localProjects.forEach(p => {
const card = document.createElement("div");
card.className = "card";
card.innerHTML = `
<h3>${p.title}</h3>
<p>${p.tech}</p>
<a href="${p.link}">View</a>
`;
projectGrid.appendChild(card);
});
}
}
loadProjects();
πΉ Step 3: Theme Toggle (Persistent)
In theme.js:
const toggleBtn = document.getElementById("theme-toggle");
const body = document.body;
// Apply saved preference
if (localStorage.getItem("theme") === "dark") {
body.classList.add("dark");
}
// Toggle mode
toggleBtn.addEventListener("click", () => {
body.classList.toggle("dark");
const mode = body.classList.contains("dark") ? "dark" : "light";
localStorage.setItem("theme", mode);
});
πΉ Step 4: Dynamic Greeting + Date
Add this near your header or hero area:
<p id="greeting"></p>
const greeting = document.getElementById("greeting");
const hour = new Date().getHours();
if (hour < 12) {
greeting.textContent = "βοΈ Good morning, StreetGeek!";
} else if (hour < 18) {
greeting.textContent = "π€οΈ Good afternoon, Developer!";
} else {
greeting.textContent = "π Good evening, Coder!";
}
πΉ Step 5: Form Validation + Feedback
Reuse the validated form logic from Module 8:
const form = document.getElementById("contactForm");
const status = document.getElementById("form-status");
form.addEventListener("submit", (e) => {
e.preventDefault();
const email = document.getElementById("email").value;
if (!email.includes("@") || !email.includes(".")) {
status.textContent = "β οΈ Invalid email!";
status.style.color = "red";
return;
}
status.textContent = "β
Message sent successfully!";
status.style.color = "green";
form.reset();
});
πΉ Step 6: Add Scroll Animation (Bonus Enhancement)
const cards = document.querySelectorAll(".card");
window.addEventListener("scroll", () => {
cards.forEach(card => {
const position = card.getBoundingClientRect().top;
if (position < window.innerHeight - 100) {
card.style.opacity = "1";
card.style.transform = "translateY(0)";
}
});
});
CSS
.card {
opacity: 0;
transform: translateY(50px);
transition: all 0.6s ease-out;
}
4) LocalStorage Data Recap
Add a βLast Updatedβ footer text:
<footer id="update-footer"></footer>
const footer = document.getElementById("update-footer");
const lastUpdated = localStorage.getItem("lastUpdated");
footer.textContent = lastUpdated
? `Last Updated: ${lastUpdated}`
: "No data fetched yet.";
5) Optional Features for Extra Credit
| Feature | Description |
|---|---|
| π Search | Add a search bar that filters projects in real-time. |
| π¬ Modal | Create a modal popup for project details. |
| π§ Save State | Use localStorage to remember selected filter or tab. |
| β° Update Button | Add a button to re-fetch new data manually. |
| β‘ Lazy Loading | Only load visible cards for faster performance. |
6) Quiz Time
Show/Hide Questions
1) What is the purpose of the async keyword?
2) How does localStorage differ from sessionStorage?
3) How can you show fallback data when a fetch request fails?
4) What does await do inside a function?
5) How can you persist user preferences like dark mode?
7) Final Challenge Task β Portfolio v2.0 Completion Checklist
β HTML
- Clean, semantic, structured pages
β CSS
- Mobile-responsive, consistent theme
β JavaScript
- DOM manipulation
- Functions and modular files
- Event handling
- Fetch API
- Async/Await
- Form validation
localStoragepersistence
β User Features
- Dark/Light theme toggle
- Dynamic project grid
- Realtime validation on contact form
- API data + caching
Bonus
Host it on GitHub Pages or your VPS β share your live interactive portfolio with the world π.
π Module 10 Summary
- A fully interactive portfolio powered by JavaScript
- Real-time data fetching and caching
- Smooth animations and theme toggles
- Persistent preferences and user feedback
- Clean modular code using ES6+ syntax
π Congratulations, Developer!
Youβve officially completed JavaScript Foundations: From Static to Interactive.
You now understand the full front-end stack:
HTML + CSS + JavaScript β Dynamic, Professional Websites.
Next up β PHP Foundations: From Interactive Frontend to Dynamic Backend
Weβll connect your JavaScript-powered portfolio to a live database and server, creating full-stack applications that can store and serve real data.