Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 132 additions & 31 deletions web-app/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -1299,48 +1299,149 @@ main>.hero-section:has(.hero-code-snippets) {
}


/* ── Hero Meta ─────────────────────────────────────────────── */
.hero-meta {
/* ── Stats Dashboard ───────────────────────────────────────── */
.stats-dashboard {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 1rem;
width: 100%;
max-width: 1200px;
margin: 2rem auto 0;
padding: 0 1rem;
box-sizing: border-box;
}

.stats-card {
display: flex;
gap: 0.8rem;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: var(--font-mono);
font-size: 0.78rem;
color: var(--text-secondary);
margin: 1.25rem 0 0;
background: rgba(255, 255, 255, 0.45);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 16px;
padding: 1.25rem 1rem;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.05);
outline: none;
font-family: inherit;
color: inherit;
box-sizing: border-box;
}

.hero-meta span {
padding: 0.6rem 0.95rem;
border-radius: 12px;
background: rgba(255, 255, 255, 0.78);
border: 1px solid rgba(128, 104, 85, 0.08);
font-size: 0.82rem;
box-shadow: 0 2px 6px rgba(146, 111, 74, 0.04);
display: inline-flex;
align-items: center;
gap: 0.45rem;
[data-theme="dark"] .stats-card {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
}

.hero-meta strong {
color: var(--text);
font-weight: 600;
/* Hover and focus styles */
.stats-card:hover,
.stats-card:focus-visible {
transform: translateY(-5px) scale(1.03);
background: rgba(255, 255, 255, 0.6);
border-color: rgba(34, 197, 94, 0.5); /* Glowing green border on hover */
box-shadow: 0 0 15px rgba(34, 197, 94, 0.3), 0 12px 40px rgba(31, 38, 135, 0.08);
}

.hero-meta span::before {
content: "";
width: 4px;
height: 4px;
[data-theme="dark"] .stats-card:hover,
[data-theme="dark"] .stats-card:focus-visible {
background: rgba(255, 255, 255, 0.07);
border-color: rgba(74, 222, 128, 0.6); /* Green glowing border in dark mode */
box-shadow: 0 0 20px rgba(74, 222, 128, 0.3), 0 12px 40px rgba(0, 0, 0, 0.5);
}

/* Active/selected state filter highlight */
.stats-card.active {
background: rgba(255, 255, 255, 0.7);
border-color: rgba(34, 197, 94, 0.8);
box-shadow: 0 0 20px rgba(34, 197, 94, 0.4), 0 8px 24px rgba(31, 38, 135, 0.06);
}

[data-theme="dark"] .stats-card.active {
background: rgba(255, 255, 255, 0.09);
border-color: rgba(74, 222, 128, 0.9);
box-shadow: 0 0 25px rgba(74, 222, 128, 0.5), 0 8px 24px rgba(0, 0, 0, 0.4);
}

/* Accents and text elements */
.stats-icon {
margin-bottom: 0.75rem;
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--accent);
opacity: 0.6;
background: rgba(34, 197, 94, 0.1);
color: #22c55e;
transition: transform 0.3s ease;
}

[data-theme="dark"] .hero-meta span {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 239, 223, 0.1);
box-shadow: none;
[data-theme="dark"] .stats-icon {
background: rgba(74, 222, 128, 0.15);
color: #4ade80;
}

.stats-icon svg {
width: 20px;
height: 20px;
stroke-width: 2.25;
}

.stats-card:hover .stats-icon {
transform: rotate(8deg) scale(1.1);
}

.stats-info {
display: flex;
flex-direction: column;
align-items: center;
}

/* customized green accents for metrics counters */
.stats-info strong {
font-size: 1.75rem;
font-weight: 700;
line-height: 1.2;
color: #166534; /* customized dark-green accent */
transition: all 0.3s ease;
}

[data-theme="dark"] .stats-info strong {
color: #4ade80; /* customized light-green/neon accent for dark mode */
}

.stats-info span {
font-size: 0.8rem;
font-weight: 500;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-top: 0.25rem;
text-align: center;
}

@media (max-width: 1024px) {
.stats-dashboard {
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
}

@media (max-width: 640px) {
.stats-dashboard {
grid-template-columns: repeat(2, 1fr);
gap: 0.75rem;
}
}

@media (max-width: 400px) {
.stats-dashboard {
grid-template-columns: 1fr;
}
}


Expand Down
68 changes: 56 additions & 12 deletions web-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -237,17 +237,61 @@ <h1 class="hero-title">
</a>
</div>

<div class="hero-meta">
<div>
<span><strong id="heroProjectCount">0</strong> total projects</span>
<span><strong id="heroGameCount">0</strong> games</span>
<span><strong id="heroMathCount">0</strong> maths</span>
<span><strong id="heroUtilityCount">0</strong> utilities</span>
</div>
<div>
<span data-filter="favorites"><strong id="heroFavoriteCount">0</strong>Favorites</span>
<span><strong id="heroViewedCount">0</strong>Recently Viewed</span>
</div>
<div class="stats-dashboard">
<button class="stats-card active" data-filter="all" type="button" aria-label="Show all projects">
<div class="stats-icon">
<svg data-lucide="layers"></svg>
</div>
<div class="stats-info">
<strong id="heroProjectCount">0</strong>
<span>Total Projects</span>
</div>
</button>
<button class="stats-card" data-filter="games" type="button" aria-label="Filter games">
<div class="stats-icon">
<svg data-lucide="gamepad-2"></svg>
</div>
<div class="stats-info">
<strong id="heroGameCount">0</strong>
<span>Games</span>
</div>
</button>
<button class="stats-card" data-filter="math" type="button" aria-label="Filter mathematics projects">
<div class="stats-icon">
<svg data-lucide="calculator"></svg>
</div>
<div class="stats-info">
<strong id="heroMathCount">0</strong>
<span>Maths</span>
</div>
</button>
<button class="stats-card" data-filter="utilities" type="button" aria-label="Filter utilities">
<div class="stats-icon">
<svg data-lucide="wrench"></svg>
</div>
<div class="stats-info">
<strong id="heroUtilityCount">0</strong>
<span>Utilities</span>
</div>
</button>
<button class="stats-card" data-filter="favorites" type="button" aria-label="Filter favorite projects">
<div class="stats-icon">
<svg data-lucide="star"></svg>
</div>
<div class="stats-info">
<strong id="heroFavoriteCount">0</strong>
<span>Favorites</span>
</div>
</button>
<button class="stats-card" data-filter="recent" type="button" aria-label="Scroll to recently viewed projects">
<div class="stats-icon">
<svg data-lucide="history"></svg>
</div>
<div class="stats-info">
<strong id="heroViewedCount">0</strong>
<span>Recently Viewed</span>
</div>
</button>
</div>
</div>
</section>
Expand Down Expand Up @@ -1011,7 +1055,7 @@ <h3>${proj.title}</h3>
animateCounter("statsGames",games);
animateCounter("statsMath",math);
animateCounter("statsUtilities",utils);
const viewed= JSON.parse(localStorage.getItem("recentlyViewed")||"[]");
const viewed= JSON.parse(localStorage.getItem("recentProjects")||"[]");
animateCounter("heroViewedCount",viewed.length);

console.log(
Expand Down
52 changes: 47 additions & 5 deletions web-app/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,13 @@ document.addEventListener("DOMContentLoaded", function () {
currentCategory = category;
syncSidebarTabs(category);
syncStickyTabs(category);

// Sync stats cards highlight
var statsCards = document.querySelectorAll(".stats-card");
statsCards.forEach(function (card) {
card.classList.toggle("active", card.getAttribute("data-filter") === category);
});

var visibleCount = 0;
var favorites = JSON.parse(localStorage.getItem("favorites") || "[]");
projectCards.forEach(function (card) {
Expand Down Expand Up @@ -647,6 +654,10 @@ document.addEventListener("DOMContentLoaded", function () {
function showPlaygroundSection() {
playgroundActive = true;
syncStickyTabs("playground");
var statsCards = document.querySelectorAll(".stats-card");
statsCards.forEach(function (card) {
card.classList.remove("active");
});
if (projectsSection) projectsSection.style.display = "none";
if (playgroundSection) {
playgroundSection.style.display = "";
Expand Down Expand Up @@ -766,6 +777,35 @@ document.addEventListener("DOMContentLoaded", function () {
});
});

/* ── Stats Cards ──────────────────────────────────────────── */
var statsCards = document.querySelectorAll(".stats-card");
statsCards.forEach(function (card) {
card.addEventListener("click", function () {
var category = card.getAttribute("data-filter");

// Update active highlight class on stats cards
statsCards.forEach(function (c) {
c.classList.toggle("active", c === card);
});

if (category === "recent") {
var section = document.getElementById("recentlyViewedSection");
if (section) {
section.scrollIntoView({ behavior: "smooth", block: "start" });
}
} else {
showProjectsSection();
applyCategoryFilter(category);
if (projectsSection) {
projectsSection.scrollIntoView({
behavior: "smooth",
block: "start",
});
}
}
});
});

/* ── Sticky Filter Bar Visibility ─────────────────────────── */
if (stickyFilterBar && heroSection) {
var heroObserver = new IntersectionObserver(
Expand Down Expand Up @@ -1793,11 +1833,13 @@ card.appendChild(badge);
const historyBadge =
document.getElementById("historyCountBadge");

if(historyBadge){

historyBadge.textContent=`(${recent.length})`;

}
if(historyBadge){
historyBadge.textContent=`(${recent.length})`;
}
const heroViewedCount = document.getElementById("heroViewedCount");
if (heroViewedCount) {
heroViewedCount.textContent = String(recent.length);
}
console.log("[DEBUG] recentProjects array:", recent); if (recent.length === 0) {
section.style.display = "none";
return;
Expand Down
Loading