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/
βœ… Each file handles a specific purpose (functions, data, themes).

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();
βœ… Pulls data from GitHub and falls back to local data if offline.

πŸ”Ή 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);
});
βœ… The theme is remembered even after closing or refreshing.

πŸ”Ή 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!";
}
βœ… Adds personality and relevance to your page.

πŸ”Ή 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();
});
βœ… Keeps your UX smooth and professional.

πŸ”Ή 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;
}
βœ… Smooth entrance animations for your portfolio projects.

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.";
βœ… Shows the last time data was refreshed from GitHub.

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
  • localStorage persistence

βœ… 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.