diff --git a/static/script.js b/static/script.js index 7366707..d0b72f8 100644 --- a/static/script.js +++ b/static/script.js @@ -683,62 +683,69 @@ if (isIndexPage) { // ---------------------------------------------------------- form.addEventListener("submit", function (evt) { - evt.preventDefault(); //stop the browser from reloading the page on form submit - clearAllErrors() + evt.preventDefault(); - if (skillsTextInput.value.trim()) { - addSkill(skillsTextInput.value); - skillsTextInput.value = ""; - hideSuggestions(); - } + clearAllErrors(); - if (!validateForm()) return; //stop - anything missing/invalid + if (skillsTextInput.value.trim()) { + addSkill(skillsTextInput.value); + skillsTextInput.value = ""; + hideSuggestions(); + } - setLoadingState(true); + if (!validateForm()) return; - // Allow browser to paint spinner before request starts - requestAnimationFrame(function () { + setLoadingState(true); - var payload = { - skills: skillsHidden.value.trim() || skillsTextInput.value.trim(), - level: document.getElementById("level").value, - interest: document.getElementById("interest").value, - time: document.getElementById("time").value - }; - - fetch("/api/recommend", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload) - }) - .then(function (res) { - return res.json(); - }) - .then(function (data) { + requestAnimationFrame(function () { - setLoadingState(false); - - if (data.error) { - var generalErr = document.getElementById("form-error-general"); + var payload = { + skills: skillsHidden.value.trim() || skillsTextInput.value.trim(), + level: document.getElementById("level").value, + interest: document.getElementById("interest").value, + time: document.getElementById("time").value + }; - if (generalErr) { - generalErr.textContent = data.error; - } + fetch("/api/recommend", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(payload) + }) + .then(function (res) { + return res.json(); + }) + .then(function (data) { - return; - } + setLoadingState(false); - renderResults(data.projects || [], data.message); - }) - .catch(function () { - setLoadingState(false); + if (data.error) { var generalErr = document.getElementById("form-error-general"); if (generalErr) { - generalErr.textContent = "An error occurred. Please try again."; + generalErr.textContent = data.error; } - }); - }); + + return; + } + + renderResults(data.projects || [], data.message); + }) + .catch(function (err) { + + setLoadingState(false); + + var generalErr = document.getElementById("form-error-general"); + + if (generalErr) { + generalErr.textContent = + "Something went wrong. Please try again."; + } + + console.error("API request failed:", err); + }); }); +}); // Manages the loading state of the form and results section(whats visible or not) function setLoadingState(isLoading) { @@ -775,22 +782,18 @@ if (isIndexPage) { // Clear out any cards from a previous search before showing new ones resultsGrid.innerHTML = ""; - if (!projects || projects.length === 0) { //if no projects returned from api, show the "no results" message and hide the grid - resultsGrid.style.display = "none"; - resultsEmptyEl.style.display = "block"; - - // Show a friendly custom message when the user selected an interest - var selectedInterest = document.getElementById("interest")?.value; - if (selectedInterest) { - emptyMessageEl.textContent = "No projects are currently available for this interest. Please check back later or try a different area."; - } else if (message) { - emptyMessageEl.textContent = message; - } else { - emptyMessageEl.textContent = "Try adjusting your skills or choosing a different interest area."; - } - - resultsSection.scrollIntoView({ behavior: "smooth" }); - return; + if (!projects || projects.length === 0) { + resultsGrid.style.display = "none"; + resultsEmptyEl.style.display = "block"; + + // Show a friendly custom message when the user selected an interest + var selectedInterest = document.getElementById("interest")?.value; + if (selectedInterest) { + emptyMessageEl.textContent = "No projects are currently available for this interest. Please check back later or try a different area."; + } else if (message) { + emptyMessageEl.textContent = message; + } else { + emptyMessageEl.textContent = "Try adjusting your skills or choosing a different interest area."; } resultsEmptyEl.style.display = "none"; @@ -1190,4 +1193,4 @@ function scrollToTop() { if (scrollTopBtn) { window.addEventListener('scroll', handleScroll); scrollTopBtn.addEventListener('click', scrollToTop); - } +} diff --git a/static/style.css b/static/style.css index 3050d2e..8677dc3 100644 --- a/static/style.css +++ b/static/style.css @@ -3394,728 +3394,42 @@ html[data-theme="dark"] .btn-view-code-sm { color: #a5b4fc; } -[data-theme="dark"] .navbar { - background: rgba(5, 8, 30, 0.92); -} - -[data-theme="dark"] .hero { - background: linear-gradient(140deg, #080b2e 0%, #0f1460 40%, #1a0a3e 100%); -} - -[data-theme="dark"] .hero-visual-card { - background: rgba(30, 32, 48, 0.95); -} - -[data-theme="dark"] .hvc-title { - color: var(--gray-900); -} - -[data-theme="dark"] .hvc-sub { - color: var(--gray-500); -} - -[data-theme="dark"] .skill-strip { - background: - radial-gradient(circle at top left, rgba(79, 110, 247, 0.08), transparent 34%), - radial-gradient(circle at bottom right, rgba(91, 33, 182, 0.06), transparent 30%), - linear-gradient(180deg, var(--gray-50), var(--indigo-50)); - border-top: 1px solid rgba(79, 110, 247, 0.06); - border-bottom: 1px solid rgba(79, 110, 247, 0.06); -} - -[data-theme="dark"] .skill-strip-inner { - background: rgba(20, 24, 48, 0.85); - border-color: rgba(79, 110, 247, 0.08); -} - -[data-theme="dark"] .skill-strip-label { - color: #818cf8; -} - -[data-theme="dark"] .ss-item { - background: rgba(30, 32, 48, 0.92); - color: var(--gray-600); - border-color: rgba(79, 110, 247, 0.08); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); -} - -[data-theme="dark"] .ss-item:hover { - background: #1e2030; - color: #a5b4fc; - border-color: rgba(79, 110, 247, 0.25); - box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4); -} - -[data-theme="dark"] .ss-sep { - background: rgba(79, 110, 247, 0.2); -} - -[data-theme="dark"] .detail-page { - background: var(--gray-50); -} - -[data-theme="dark"] .detail-section { - background: var(--white); -} - -[data-theme="dark"] .sidebar-card { - background: var(--white); -} - -[data-theme="dark"] .sidebar-card--code { - background: linear-gradient(135deg, rgba(26, 31, 58, 0.6) 0%, rgba(30, 19, 53, 0.6) 100%); -} - -[data-theme="dark"] .code-panel-overlay { - background: rgba(0, 0, 0, 0.75); -} - -[data-theme="dark"] .nav-mobile-menu { - background: #080b2e; -} - -[data-theme="dark"] .tooltip { - background: #4f6ef7; - color: #fff; -} - -[data-theme="dark"] .hero-heading { - color: #ffffff; -} - -[data-theme="dark"] .hero-heading-accent { - color: var(--yellow-300); -} - -[data-theme="dark"] .nav-logo { - color: #ffffff; -} - -[data-theme="dark"] .nav-logo-accent { - color: var(--yellow-400); -} - -[data-theme="dark"] .detail-title { - color: #ffffff; -} - -[data-theme="dark"] .btn-view-code { - color: #ffffff; -} - -[data-theme="dark"] .footer-logo { - color: #ffffff; -} - -[data-theme="dark"] .footer-logo-accent { - color: var(--yellow-400); -} - -[data-theme="dark"] .section-eyebrow { - color: #818cf8; - background: rgba(26, 31, 58, 0.8); -} - -[data-theme="dark"] .badge { - color: var(--gray-500); - background: var(--gray-100); -} - -[data-theme="dark"] .badge--beginner { - color: #34d399; - background: rgba(16, 185, 129, 0.12); -} - -[data-theme="dark"] .badge--web { - color: #818cf8; - background: rgba(79, 110, 247, 0.12); -} - -[data-theme="dark"] .badge--python { - color: #fbbf24; - background: rgba(251, 191, 36, 0.12); -} - -[data-theme="dark"] .badge--data { - color: #a78bfa; - background: rgba(124, 58, 237, 0.12); -} - -[data-theme="dark"] .stat-card { - background: var(--white); - border-color: var(--border); -} - -[data-theme="dark"] .stat-icon--indigo { - background: rgba(79, 110, 247, 0.12); - color: #818cf8; -} - -[data-theme="dark"] .stat-icon--green { - background: rgba(16, 185, 129, 0.12); - color: #34d399; -} - -[data-theme="dark"] .stat-icon--yellow { - background: rgba(251, 191, 36, 0.12); - color: #fbbf24; -} - -[data-theme="dark"] .stat-value { - color: var(--text-heading); -} - -[data-theme="dark"] .stat-label { - color: var(--gray-500); -} - -[data-theme="dark"] .how-section { - background: var(--white); -} - -[data-theme="dark"] .step-card { - background: var(--white); - border-color: var(--border); -} - -[data-theme="dark"] .step-num { - color: #818cf8; - background: rgba(79, 110, 247, 0.12); -} - -[data-theme="dark"] .step-card h3 { - color: var(--text-heading); -} - -[data-theme="dark"] .step-card p { - color: var(--text-body); -} - -[data-theme="dark"] .step-connector { - color: var(--gray-500); -} - -[data-theme="dark"] .features-section { - background: var(--gray-50); -} - -[data-theme="dark"] .feature-card { - background: transparent; - border: 1px solid var(--border); -} - -[data-theme="dark"] .feature-card--pink { - background: rgba(236, 72, 153, 0.06); - border-color: rgba(236, 72, 153, 0.15); -} - -[data-theme="dark"] .feature-card--yellow { - background: rgba(251, 191, 36, 0.06); - border-color: rgba(251, 191, 36, 0.15); -} - -[data-theme="dark"] .feature-card--purple { - background: rgba(124, 58, 237, 0.06); - border-color: rgba(124, 58, 237, 0.15); -} - -[data-theme="dark"] .feature-card-icon { - background: rgba(15, 17, 23, 0.5); -} - -[data-theme="dark"] .feature-card h3 { - color: var(--text-heading); -} - -[data-theme="dark"] .feature-card p { - color: var(--text-body); -} - -[data-theme="dark"] .feature-card-link { - color: #818cf8; - border-color: rgba(79, 110, 247, 0.25); - background: rgba(15, 17, 23, 0.5); -} - -[data-theme="dark"] .feature-card-link:hover { - background: rgba(30, 32, 48, 0.8); - border-color: #818cf8; -} - -[data-theme="dark"] .form-section { - background: var(--white); -} - -[data-theme="dark"] .form-card { - background: var(--gray-50); - border-color: var(--border); -} - -[data-theme="dark"] label { - color: var(--gray-700); -} - -[data-theme="dark"] .skill-input-wrap { - background: var(--white); - border-color: var(--border); -} - -[data-theme="dark"] .skill-input-wrap:focus-within { - background: var(--white); -} - -[data-theme="dark"] .skill-chip-selected { - color: #818cf8; - background: rgba(79, 110, 247, 0.12); - border-color: rgba(79, 110, 247, 0.2); -} - -[data-theme="dark"] .skill-chip-remove { - color: #818cf8; -} - -[data-theme="dark"] .skill-input-wrap input[type="text"] { - color: var(--gray-900); -} - -[data-theme="dark"] .skill-input-wrap input[type="text"]::placeholder { - color: var(--gray-500); -} - -[data-theme="dark"] .skills-suggestions { - background: var(--white); - border-color: rgba(79, 110, 247, 0.2); -} - -[data-theme="dark"] .suggestion-item { - color: var(--gray-700); - border-color: var(--gray-100); -} - -[data-theme="dark"] .suggestion-item:hover, -[data-theme="dark"] .suggestion-item--active { - background: rgba(79, 110, 247, 0.08); - color: #818cf8; -} - -[data-theme="dark"] .skill-chip { - color: var(--gray-500); - background: var(--gray-50); - border-color: var(--gray-300); -} - -[data-theme="dark"] .skill-chip:hover, -[data-theme="dark"] .skill-chip.active { - background: #1e2030; - border-color: #818cf8; - color: #818cf8; -} - -[data-theme="dark"] select, -[data-theme="dark"] input[type="text"]:not(.skill-input-wrap input) { - color: var(--gray-900); - background: var(--white); - border-color: var(--border); -} - -[data-theme="dark"] select:focus { - background: var(--white); -} - -[data-theme="dark"] .select-wrap::after { - border-top-color: var(--gray-500); -} - -[data-theme="dark"] .form-hint { - color: var(--gray-500); -} - -[data-theme="dark"] .results-section { - background: var(--gray-50); -} - -[data-theme="dark"] .empty-icon { - color: var(--gray-400); -} - -[data-theme="dark"] .btn-try-again { - color: #818cf8; - background: rgba(79, 110, 247, 0.08); - border-color: rgba(79, 110, 247, 0.2); -} - -[data-theme="dark"] .btn-try-again:hover { - background: rgba(79, 110, 247, 0.15); -} - -[data-theme="dark"] .project-card { - background: var(--white); - border-color: var(--border); -} - -[data-theme="dark"] .project-card:hover { - border-color: rgba(79, 110, 247, 0.25); -} - -[data-theme="dark"] .project-card-title { - color: var(--text-heading); -} - -[data-theme="dark"] .project-card-desc { - color: var(--text-body); -} - -[data-theme="dark"] .project-tag--skill { - background: rgba(79, 110, 247, 0.1); - color: #818cf8; -} - -[data-theme="dark"] .project-tag--time { - background: rgba(107, 114, 128, 0.12); - color: var(--gray-500); -} - -[data-theme="dark"] .project-tag--beginner { - background: rgba(16, 185, 129, 0.12); - color: #34d399; -} - -[data-theme="dark"] .project-tag--intermediate { - background: rgba(251, 191, 36, 0.12); - color: #fbbf24; -} - -[data-theme="dark"] .project-tag--advanced { - background: rgba(239, 68, 68, 0.12); - color: #f87171; -} - -[data-theme="dark"] .project-card-footer { - border-top-color: var(--gray-100); -} - -[data-theme="dark"] .btn-details { - color: #818cf8; - border-color: #818cf8; -} - -[data-theme="dark"] .btn-details:hover { - background: #818cf8; - color: #0f1117; -} - -[data-theme="dark"] .detail-page { - background: var(--gray-50); -} - -[data-theme="dark"] .detail-hero { - background: linear-gradient(135deg, #080b2e 0%, #0f1460 50%, #1a0a3e 100%); -} - -[data-theme="dark"] .detail-section { - background: var(--white); -} - -[data-theme="dark"] .detail-section-icon { - background: rgba(79, 110, 247, 0.12); - color: #818cf8; -} - -[data-theme="dark"] .detail-section-header h2 { - color: var(--text-heading); -} - -[data-theme="dark"] .detail-section-sub { - color: var(--text-body); -} - -[data-theme="dark"] .feature-list-item { - color: var(--text-body); -} - -[data-theme="dark"] .feature-check { - background: rgba(16, 185, 129, 0.12); - color: #34d399; -} - -[data-theme="dark"] .roadmap-dot { - background: var(--white); - border-color: #818cf8; -} - -[data-theme="dark"] .roadmap-step:first-child .roadmap-dot { - background: #818cf8; - border-color: rgba(129, 140, 248, 0.2); - box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.15); -} - -[data-theme="dark"] .roadmap-line { - border-left-color: var(--border); -} - -[data-theme="dark"] .roadmap-step-num { - color: #818cf8; - background: rgba(79, 110, 247, 0.1); -} - -[data-theme="dark"] .roadmap-step-text { - color: var(--text-body); -} - -[data-theme="dark"] .resource-link { - color: #818cf8; - background: var(--gray-50); - border-color: var(--border); -} - -[data-theme="dark"] .resource-link:hover { - background: rgba(79, 110, 247, 0.08); - border-color: rgba(79, 110, 247, 0.25); -} - -[data-theme="dark"] .resource-plain { - color: var(--text-body); -} - -[data-theme="dark"] .sidebar-card { - background: var(--white); -} - -[data-theme="dark"] .sidebar-card-title { - color: var(--text-heading); -} - -[data-theme="dark"] .sidebar-card-desc { - color: var(--text-body); -} - -[data-theme="dark"] .tech-tag { - color: var(--gray-600); - background: var(--gray-100); - border-color: var(--border); -} - -[data-theme="dark"] .stats-list li { - color: var(--text-body); -} - -[data-theme="dark"] .stats-label { - color: var(--text-body); -} - -[data-theme="dark"] .stats-value { - color: var(--text-heading); -} - -[data-theme="dark"] .stats-value--beginner { - color: #34d399; -} - -[data-theme="dark"] .stats-value--intermediate { - color: #fbbf24; -} - -[data-theme="dark"] .stats-value--advanced { - color: #f87171; -} - -[data-theme="dark"] .btn-view-code-sm { - color: #818cf8; - background: transparent; - border-color: #818cf8; -} - -[data-theme="dark"] .btn-view-code-sm:hover { - background: #818cf8; - color: #0f1117; -} - -[data-theme="dark"] .code-panel-overlay { - background: rgba(0, 0, 0, 0.75); -} - -[data-theme="dark"] .error-page { - background: var(--white); -} - -[data-theme="dark"] .error-code { - color: var(--gray-300); -} - -[data-theme="dark"] .error-page-inner h1 { - color: var(--text-heading); -} - -[data-theme="dark"] .error-page-inner p { - color: var(--text-body); -} - -[data-theme="dark"] #btn-show-github { - color: #818cf8; - background: rgba(79, 110, 247, 0.08); -} - -[data-theme="dark"] #btn-show-github:hover { - background: rgba(79, 110, 247, 0.15); - border-color: rgba(79, 110, 247, 0.2); -} - -[data-theme="dark"] .sidebar-card--code .sidebar-card-desc { - color: var(--text-body); -} - -[data-theme="dark"] #github-modal-overlay .sidebar-card { - background: var(--white); -} - -[data-theme="dark"] #github-modal-overlay .sidebar-card-desc { - color: var(--text-body); -} - -[data-theme="dark"] #scroll-top-btn { - background: #4f6ef7; - color: #fff; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); -} - -[data-theme="dark"] .loading-dots span { - background: #818cf8; -} - -[data-theme="dark"] .loading-box p { - color: var(--text-body); -} - -[data-theme="dark"] .cta-section { - background: linear-gradient(135deg, #080b2e 0%, #0f1460 50%, #1a0a3e 100%); -} - -[data-theme="dark"] .cta-inner h2 { - color: #ffffff; -} - -[data-theme="dark"] .cta-accent { - color: #ffffff; -} - -[data-theme="dark"] .footer { - background: #080b2e; -} - -[data-theme="dark"] .footer-tagline { - color: rgba(255, 255, 255, 0.5); -} - -[data-theme="dark"] .footer-col-title { - color: rgba(255, 255, 255, 0.5); -} - -[data-theme="dark"] .footer-links-list a { - color: rgba(255, 255, 255, 0.55); -} - -[data-theme="dark"] .footer-links-list a:hover { - color: #fff; -} - -[data-theme="dark"] .footer-bottom p { - color: rgba(255, 255, 255, 0.35); -} - -[data-theme="dark"] .footer-bottom-links a { - color: rgba(255, 255, 255, 0.35); -} - -[data-theme="dark"] .footer-bottom-links a:hover { - color: rgba(255, 255, 255, 0.7); -} - -[data-theme="dark"] .btn-hero-primary { - background: var(--yellow-400); - color: var(--indigo-900); - box-shadow: 0 4px 20px rgba(251, 191, 36, 0.35); -} -[data-theme="dark"] .btn-hero-primary:hover { - background: var(--yellow-300); - color: var(--indigo-900); - box-shadow: 0 6px 28px rgba(251, 191, 36, 0.4); -} - -[data-theme="dark"] .btn-hero-ghost { - background: rgba(255,255,255,0.06); - border-color: rgba(255,255,255,0.35); - color: rgba(255,255,255,0.9); -} -[data-theme="dark"] .btn-hero-ghost:hover { - background: rgba(255,255,255,0.14); - border-color: rgba(255,255,255,0.55); -} - -[data-theme="dark"] .btn-submit { - background: var(--yellow-400); - color: var(--indigo-900); - box-shadow: 0 4px 20px rgba(251, 191, 36, 0.3); -} -[data-theme="dark"] .btn-submit:hover { - background: var(--yellow-300); - box-shadow: 0 6px 28px rgba(251, 191, 36, 0.4); - opacity: 1; -} - -[data-theme="dark"] .btn-primary { - background: linear-gradient(90deg, #4f6ef7 0%, #6366f1 100%); - box-shadow: 0 4px 18px rgba(79, 110, 247, 0.3); -} - -[data-theme="dark"] .btn-cta { - background: var(--yellow-400); - color: var(--indigo-900); - box-shadow: 0 6px 24px rgba(251, 191, 36, 0.35); -} -[data-theme="dark"] .btn-cta:hover { - background: var(--yellow-300); - color: var(--indigo-900); - box-shadow: 0 6px 28px rgba(251, 191, 36, 0.4); +.line-content { + flex: 1; + white-space: pre; + color: #e6edf3; } -[data-theme="dark"] .btn-download { - background: #4f6ef7; - color: #fff; - box-shadow: 0 4px 16px rgba(79, 110, 247, 0.35); -} -[data-theme="dark"] .btn-download:hover { - background: #6366f1; -} +@media (prefers-reduced-motion: reduce) { + html:focus-within { + scroll-behavior: auto; + } -[data-theme="dark"] .btn-download-sm { - background: #4f6ef7; - color: #fff; -} -[data-theme="dark"] .btn-download-sm:hover { - background: #6366f1; + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } } -/* ---- Dark Mode Toggle Button ---- */ .theme-toggle { - display: inline-flex; - align-items: center; - justify-content: center; - background: rgba(255,255,255,0.1); - border: 1.5px solid rgba(255,255,255,0.2); - border-radius: var(--r-full); - width: 36px; - height: 36px; + width: 42px; + height: 42px; + border-radius: 12px; + border: 1px solid rgba(255,255,255,0.15); + background: rgba(255,255,255,0.08); + color: white; cursor: pointer; - color: rgba(255,255,255,0.75); - transition: background var(--t), color var(--t), transform var(--t); - flex-shrink: 0; + font-size: 1rem; + transition: all 0.25s ease; } + .theme-toggle:hover { background: rgba(255,255,255,0.18); - color: var(--white); - transform: scale(1.05); -} -.theme-toggle svg { - width: 18px; - height: 18px; + transform: translateY(-2px); } + +/* ============================================================ + WORKING DARK MODE diff --git a/templates/index.html b/templates/index.html index ba1be2e..ad47a7d 100644 --- a/templates/index.html +++ b/templates/index.html @@ -29,34 +29,14 @@ Features Find Project GitHub - - + {% include 'partials/theme_toggle.html' %} @@ -768,6 +748,38 @@ + \ No newline at end of file