diff --git a/frontend/website/index.html b/frontend/website/index.html index 9be3fa0..9d16e36 100755 --- a/frontend/website/index.html +++ b/frontend/website/index.html @@ -6,9 +6,15 @@ AlgoLedger — DSA Progress Tracker + - +
diff --git a/frontend/website/package-lock.json b/frontend/website/package-lock.json index 335787a..44777ce 100644 --- a/frontend/website/package-lock.json +++ b/frontend/website/package-lock.json @@ -60,6 +60,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1778,6 +1779,7 @@ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1819,6 +1821,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1927,6 +1930,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2064,7 +2068,8 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/d3-array": { "version": "3.2.4", @@ -2330,6 +2335,7 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3405,6 +3411,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -3483,6 +3490,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -3492,6 +3500,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -3901,6 +3910,7 @@ "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -4022,6 +4032,7 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/frontend/website/src/App.jsx b/frontend/website/src/App.jsx index 2efdeef..7469265 100755 --- a/frontend/website/src/App.jsx +++ b/frontend/website/src/App.jsx @@ -1,34 +1,45 @@ -import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom' + +import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom' +import ChallengePage from './pages/ChallengePage' +import CommunityPage from './pages/CommunityPage' +import ContestPage from './pages/ContestPage' +import DashboardPage from './pages/DashboardPage' import LandingPage from './pages/LandingPage' import LoginPage from './pages/LoginPage' -import SignupPage from './pages/SignupPage' import OnboardingPage from './pages/OnboardingPage' -import DashboardPage from './pages/DashboardPage' import PracticePage from './pages/PracticePage' import ProfilePage from './pages/ProfilePage' -import ChallengePage from './pages/ChallengePage' -import ContestPage from './pages/ContestPage' -import CommunityPage from './pages/CommunityPage' +import SignupPage from './pages/SignupPage' +import { TourProvider } from './tour/TourContext' +import TourOverlay from './tour/TourOverlay' + import UsernameSetupPage from './pages/UsernameSetupPage' import { Toaster } from "react-hot-toast"; +function ThemedToaster() { + return ( + + ); +} + export default function App() { return ( - - + + + } /> } /> @@ -46,6 +57,7 @@ export default function App() { } /> + ) -} +} \ No newline at end of file diff --git a/frontend/website/src/components/Footer.jsx b/frontend/website/src/components/Footer.jsx index dedcb54..462dd4e 100644 --- a/frontend/website/src/components/Footer.jsx +++ b/frontend/website/src/components/Footer.jsx @@ -6,7 +6,7 @@ export default function Footer() {
-
+
-
+
14 ? '#F59E0B' - : streak > 6 ? '#FB923C' - : streak > 0 ? '#FCD34D' - : '#9CA3AF', + color: streak > 14 ? 'var(--streak-high)' + : streak > 6 ? 'var(--streak-medium)' + : streak > 0 ? 'var(--streak-low)' + : 'var(--streak-none)', }} > {streak > 0 ? `🔥 ${streak} day${streak === 1 ? '' : 's'}` : '⚡ No streak'} @@ -276,19 +279,19 @@ export default function Topbar({ title, subtitle }) { /* ── styles for the bell + dropdown (scoped to .bell-*) ───────────────── */ const BELL_CSS = ` .bell-wrap { position: relative; } -.notif-btn { +.bell-wrap .notif-btn { position: relative; - background: transparent; - border: 1px solid rgba(255,255,255,0.08); - color: #CBD5E1; + background: var(--bg-tertiary); + border: 1px solid var(--surface-border); + color: var(--text-secondary); width: 38px; height: 38px; border-radius: 10px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: background 0.15s, border-color 0.15s, transform 0.1s; } -.notif-btn:hover { background: rgba(229,166,83,0.08); border-color: rgba(229,166,83,0.25); } -.notif-btn:active { transform: translateY(1px); } +.bell-wrap .notif-btn:hover { background: var(--amber-light); border-color: var(--border-hover); color: var(--text-primary); } +.bell-wrap .notif-btn:active { transform: translateY(1px); } .bell-icon { font-size: 16px; line-height: 1; } .bell-has-unread { animation: bell-swing 2s ease-in-out 1; transform-origin: 50% 10%; } @keyframes bell-swing { @@ -312,7 +315,7 @@ const BELL_CSS = ` font-weight: 800; display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 2px 6px rgba(239,68,68,0.5); - border: 1.5px solid #0B0F1A; + border: 1.5px solid var(--badge-ring); letter-spacing: 0.02em; } @@ -323,10 +326,10 @@ const BELL_CSS = ` width: 360px; max-height: 480px; overflow: auto; - background: rgba(11, 15, 26, 0.96); - border: 1px solid rgba(229,166,83,0.22); + background: var(--popover-bg); + border: 1px solid var(--popover-border); border-radius: 14px; - box-shadow: 0 20px 60px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.02) inset; + box-shadow: var(--shadow-lg), 0 0 0 1px var(--surface-inset) inset; backdrop-filter: blur(14px); z-index: 50; animation: bell-pop-in 0.18s ease-out; @@ -338,29 +341,29 @@ const BELL_CSS = ` .bell-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 10px; - border-bottom: 1px solid rgba(255,255,255,0.05); + border-bottom: 1px solid var(--popover-divider); position: sticky; top: 0; - background: rgba(11,15,26,0.96); + background: var(--popover-head-bg); z-index: 2; } .bell-head-title { font-family: 'Space Grotesk', sans-serif; font-weight: 800; font-size: 14px; - color: #F1F5F9; + color: var(--text-primary); letter-spacing: -0.01em; } -.bell-head-sub { font-size: 11px; color: #64748B; letter-spacing: 0.02em; } +.bell-head-sub { font-size: 11px; color: var(--text-muted); letter-spacing: 0.02em; } .bell-empty { padding: 46px 22px; text-align: center; - color: #64748B; + color: var(--text-muted); } .bell-empty-emoji { font-size: 32px; margin-bottom: 10px; opacity: 0.8; } .bell-empty-title { - font-size: 14px; font-weight: 700; color: #CBD5E1; margin-bottom: 4px; + font-size: 14px; font-weight: 700; color: var(--text-secondary); margin-bottom: 4px; } -.bell-empty-sub { font-size: 12px; line-height: 1.55; max-width: 240px; margin: 0 auto; } +.bell-empty-sub { font-size: 12px; line-height: 1.55; max-width: 240px; margin: 0 auto; color: var(--text-muted); } .bell-list { list-style: none; margin: 0; padding: 4px 0 8px; } .bell-item { @@ -370,9 +373,9 @@ const BELL_CSS = ` cursor: pointer; transition: background 0.15s; } -.bell-item:hover { background: rgba(255,255,255,0.03); } -.bell-item-unread { background: rgba(229,166,83,0.06); } -.bell-item-unread:hover { background: rgba(229,166,83,0.09); } +.bell-item:hover { background: var(--popover-item-hover); } +.bell-item-unread { background: var(--amber-light); } +.bell-item-unread:hover { background: rgba(229,166,83,0.12); } .bell-item-icon { flex-shrink: 0; @@ -385,21 +388,21 @@ const BELL_CSS = ` .bell-item-body { flex: 1; min-width: 0; } .bell-item-title { font-size: 13.5px; font-weight: 700; - color: #F1F5F9; + color: var(--text-primary); line-height: 1.35; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .bell-item-msg { margin-top: 3px; font-size: 12px; - color: #94A3B8; + color: var(--text-secondary); line-height: 1.45; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .bell-item-foot { margin-top: 6px; font-size: 10.5px; - color: #64748B; + color: var(--text-muted); display: flex; align-items: center; gap: 6px; letter-spacing: 0.02em; } diff --git a/frontend/website/src/index.css b/frontend/website/src/index.css index 33fec5d..0aab001 100644 --- a/frontend/website/src/index.css +++ b/frontend/website/src/index.css @@ -93,6 +93,147 @@ /* === Motion === */ --transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1); + + /* === Semantic aliases (landing + shared) === */ + --ink: var(--text-primary); + --ink-mute: var(--text-secondary); + --ink-faint: var(--text-muted); + --edge: var(--border); + --edge-soft: var(--border-subtle); + + /* === Shell & surfaces === */ + --shell-bg: linear-gradient(140deg, #0B0F1A 0%, #121727 50%, #0B0F1A 100%); + --sidebar-bg: linear-gradient(180deg, #0A0F1E 0%, #080C18 100%); + --sidebar-border: rgba(255, 255, 255, 0.05); + --topbar-bg: rgba(8, 12, 20, 0.7); + --topbar-border: rgba(255, 255, 255, 0.04); + + --surface-glass: rgba(255, 255, 255, 0.026); + --surface-border: rgba(255, 255, 255, 0.068); + --surface-border-subtle: rgba(255, 255, 255, 0.05); + --surface-hover: rgba(255, 255, 255, 0.04); + --surface-inset: rgba(255, 255, 255, 0.04); + --surface-shadow: 0 4px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.04); + --card-bg: rgba(17, 24, 39, 0.7); + --card-border: rgba(255, 255, 255, 0.06); + + --nav-hover-bg: rgba(255, 255, 255, 0.04); + --nav-active-bg: rgba(139, 92, 246, 0.1); + --sidebar-user-bg: linear-gradient(135deg, rgba(229, 166, 83, 0.1), rgba(79, 70, 229, 0.06)); + --sidebar-user-border: rgba(139, 92, 246, 0.15); + --xp-track-bg: rgba(255, 255, 255, 0.06); + + --popover-bg: rgba(11, 15, 26, 0.96); + --popover-border: rgba(229, 166, 83, 0.22); + --popover-head-bg: rgba(11, 15, 26, 0.96); + --popover-divider: rgba(255, 255, 255, 0.05); + --popover-item-hover: rgba(255, 255, 255, 0.03); + --badge-ring: #0B0F1A; + + --chart-tooltip-bg: rgba(8, 12, 30, 0.98); + --chart-tooltip-border: rgba(229, 166, 83, 0.3); + + --heatmap-0: rgba(255, 255, 255, 0.04); + --heatmap-1: rgba(229, 166, 83, 0.22); + --heatmap-2: rgba(229, 166, 83, 0.48); + --heatmap-3: rgba(229, 166, 83, 0.76); + --heatmap-4: #E5A653; + + --code-bg: rgba(8, 12, 30, 0.75); + --code-border: rgba(255, 255, 255, 0.06); + --code-inline-bg: rgba(229, 166, 83, 0.1); + --code-inline-border: rgba(229, 166, 83, 0.18); + --code-inline-color: #F3C887; + + --on-accent: #ffffff; + --on-gradient: #1C1608; + + --toast-bg: #111827; + --toast-color: #fff; + --toast-border: rgba(255, 255, 255, 0.08); + + /* === Difficulty Colors === */ + --difficulty-easy: #22C55E; + --difficulty-easy-bg: rgba(34, 197, 94, 0.10); + --difficulty-easy-glow: rgba(34, 197, 94, 0.30); + + --difficulty-medium: #F59E0B; + --difficulty-medium-bg: rgba(245, 158, 11, 0.10); + --difficulty-medium-glow: rgba(245, 158, 11, 0.30); + + --difficulty-hard: #EF4444; + --difficulty-hard-bg: rgba(239, 68, 68, 0.10); + --difficulty-hard-glow: rgba(239, 68, 68, 0.30); + + /* === Platform Colors === */ + --platform-leetcode: #FFA116; + --platform-codeforces: #1890FF; + --platform-geeksforgeeks: #308D46; + + /* === Streak Colors === */ + --streak-high: #F59E0B; + --streak-medium: #FB923C; + --streak-low: #FCD34D; + --streak-none: #6B7280; + + /* === Category Colors === */ + --category-mission: #9F8FE3; + --category-mission-bg: rgba(159, 143, 227, 0.12); + --category-mission-glow: rgba(159, 143, 227, 0.35); + + --category-weakness: #EF4444; + --category-weakness-bg: rgba(239, 68, 68, 0.10); + --category-weakness-glow: rgba(239, 68, 68, 0.30); + + --category-levelup: #F59E0B; + --category-levelup-bg: rgba(245, 158, 11, 0.10); + --category-levelup-glow: rgba(245, 158, 11, 0.30); + + --category-explore: #38BDF8; + --category-explore-bg: rgba(56, 189, 248, 0.10); + --category-explore-glow: rgba(56, 189, 248, 0.30); + + --category-stretch: #E5A653; + --category-stretch-bg: rgba(229, 166, 83, 0.12); + --category-stretch-glow: rgba(229, 166, 83, 0.35); + + /* === Skill Stage Colors === */ + --stage-beginner: #22C55E; + --stage-intermediate: #F59E0B; + --stage-advanced: #EF4444; + --stage-expert: #9F8FE3; + + /* === Nav Item Colors === */ + --nav-dashboard: #6366F1; + --nav-training: #22D3EE; + --nav-challenges: #F59E0B; + --nav-community: #34D399; + --nav-profile: #F472B6; + + /* === Topic Colors === */ + --topic-arrays-dark: #E5A653; + --topic-arrays-light: #9F8FE3; + --topic-graphs-dark: #10B981; + --topic-graphs-light: #34D399; + --topic-dp-dark: #F59E0B; + --topic-dp-light: #FCD34D; + --topic-trees-dark: #9F8FE3; + --topic-trees-light: #9F8FE3; + --topic-binsearch-dark: #3B82F6; + --topic-binsearch-light: #60A5FA; + --topic-sysdesign-dark: #EC4899; + --topic-sysdesign-light: #F472B6; + --topic-interview-dark: #14B8A6; + --topic-interview-light: #2DD4BF; + --topic-strings-dark: #F97316; + --topic-strings-light: #FB923C; + --topic-backtrack-dark: #EF4444; + --topic-backtrack-light: #F87171; + --topic-general-dark: #94A3B8; + --topic-general-light: #CBD5E1; + + /* === Accent Colors for Emphasis === */ + --emphasis-color: #E5A653; } [data-theme="light"] { @@ -105,11 +246,196 @@ --text-primary: #0F172A; --text-secondary: #334155; --text-muted: #64748B; + --text-accent: #B45309; --border: rgba(15, 23, 42, 0.12); --border-subtle: rgba(15, 23, 42, 0.08); + --border-hover: rgba(15, 23, 42, 0.22); --bg-glass: rgba(255, 255, 255, 0.7); + + --shell-bg: linear-gradient(140deg, #F8FAFC 0%, #FFFFFF 50%, #F1F5F9 100%); + --sidebar-bg: linear-gradient(180deg, #FFFFFF 0%, #F8FAFC 100%); + --sidebar-border: rgba(15, 23, 42, 0.08); + --topbar-bg: rgba(255, 255, 255, 0.88); + --topbar-border: rgba(15, 23, 42, 0.08); + + --surface-glass: rgba(15, 23, 42, 0.03); + --surface-border: rgba(15, 23, 42, 0.10); + --surface-border-subtle: rgba(15, 23, 42, 0.06); + --surface-hover: rgba(15, 23, 42, 0.04); + --surface-inset: rgba(15, 23, 42, 0.03); + --surface-glow: rgba(255, 255, 255, 0.18); + --surface-shadow: 0 4px 24px rgba(15, 23, 42, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.9); + --card-bg: rgba(255, 255, 255, 0.95); + --card-border: rgba(15, 23, 42, 0.08); + + --nav-hover-bg: rgba(15, 23, 42, 0.04); + --nav-active-bg: rgba(159, 143, 227, 0.12); + --sidebar-user-bg: linear-gradient(135deg, rgba(229, 166, 83, 0.08), rgba(159, 143, 227, 0.06)); + --sidebar-user-border: rgba(159, 143, 227, 0.2); + --xp-track-bg: rgba(15, 23, 42, 0.08); + + --popover-bg: rgba(255, 255, 255, 0.98); + --popover-border: rgba(15, 23, 42, 0.12); + --popover-head-bg: rgba(255, 255, 255, 0.98); + --popover-divider: rgba(15, 23, 42, 0.08); + --popover-item-hover: rgba(15, 23, 42, 0.04); + --badge-ring: #FFFFFF; + + --chart-tooltip-bg: rgba(255, 255, 255, 0.98); + --chart-tooltip-border: rgba(15, 23, 42, 0.12); + + --heatmap-0: rgba(15, 23, 42, 0.06); + --heatmap-1: rgba(229, 166, 83, 0.18); + --heatmap-2: rgba(229, 166, 83, 0.38); + --heatmap-3: rgba(229, 166, 83, 0.58); + --heatmap-4: #B45309; + + --code-bg: rgba(241, 245, 249, 0.95); + --code-border: rgba(15, 23, 42, 0.1); + --code-inline-bg: rgba(229, 166, 83, 0.12); + --code-inline-border: rgba(180, 83, 9, 0.25); + --code-inline-color: #92400E; + + --shadow-sm: 0 1px 4px rgba(15, 23, 42, 0.08); + --shadow-md: 0 4px 20px rgba(15, 23, 42, 0.10); + --shadow-lg: 0 8px 40px rgba(15, 23, 42, 0.12); + --shadow-glow: 0 0 24px rgba(229, 166, 83, 0.15); + + --toast-bg: #FFFFFF; + --toast-color: #0F172A; + --toast-border: rgba(15, 23, 42, 0.12); + + /* === Light Theme Overrides for Color Systems === */ + /* Difficulty Colors - adjusted for light backgrounds */ + --difficulty-easy: #059669; + --difficulty-easy-bg: rgba(5, 150, 105, 0.12); + --difficulty-easy-glow: rgba(5, 150, 105, 0.25); + + --difficulty-medium: #92400E; + --difficulty-medium-bg: rgba(146, 64, 14, 0.12); + --difficulty-medium-glow: rgba(146, 64, 14, 0.25); + + --difficulty-hard: #991B1B; + --difficulty-hard-bg: rgba(153, 27, 27, 0.12); + --difficulty-hard-glow: rgba(153, 27, 27, 0.25); + + /* Platform Colors - adjusted for light backgrounds */ + --platform-leetcode: #D97706; + --platform-codeforces: #0369A1; + --platform-geeksforgeeks: #166534; + + /* Streak Colors - adjusted for light backgrounds */ + --streak-high: #D97706; + --streak-medium: #EA580C; + --streak-low: #B45309; + --streak-none: #6B7280; + + /* Category Colors - adjusted for light backgrounds */ + --category-mission: #6D28D9; + --category-mission-bg: rgba(109, 40, 217, 0.12); + --category-mission-glow: rgba(109, 40, 217, 0.25); + + --category-weakness: #991B1B; + --category-weakness-bg: rgba(153, 27, 27, 0.12); + --category-weakness-glow: rgba(153, 27, 27, 0.25); + + --category-levelup: #92400E; + --category-levelup-bg: rgba(146, 64, 14, 0.12); + --category-levelup-glow: rgba(146, 64, 14, 0.25); + + --category-explore: #0369A1; + --category-explore-bg: rgba(3, 105, 161, 0.12); + --category-explore-glow: rgba(3, 105, 161, 0.25); + + --category-stretch: #B45309; + --category-stretch-bg: rgba(180, 83, 9, 0.12); + --category-stretch-glow: rgba(180, 83, 9, 0.25); + + /* Skill Stage Colors */ + --stage-beginner: #059669; + --stage-intermediate: #92400E; + --stage-advanced: #991B1B; + --stage-expert: #6D28D9; + + /* Nav Item Colors - adjusted for light backgrounds */ + --nav-dashboard: #4F46E5; + --nav-training: #0891B2; + --nav-challenges: #D97706; + --nav-community: #059669; + --nav-profile: #BE185D; + + /* Topic Colors - adjusted for light backgrounds */ + --topic-arrays-dark: #B45309; + --topic-arrays-light: #6D28D9; + --topic-graphs-dark: #059669; + --topic-graphs-light: #10B981; + --topic-dp-dark: #92400E; + --topic-dp-light: #B45309; + --topic-trees-dark: #6D28D9; + --topic-trees-light: #6D28D9; + --topic-binsearch-dark: #1E40AF; + --topic-binsearch-light: #2563EB; + --topic-sysdesign-dark: #9D174D; + --topic-sysdesign-light: #BE185D; + --topic-interview-dark: #0D9488; + --topic-interview-light: #14B8A6; + --topic-strings-dark: #C2410C; + --topic-strings-light: #EA580C; + --topic-backtrack-dark: #991B1B; + --topic-backtrack-light: #DC2626; + --topic-general-dark: #475569; + --topic-general-light: #64748B; + + /* Accent Colors for Emphasis */ + --emphasis-color: #B45309; +} + +[data-theme="light"] .card:hover { + border-color: rgba(159, 143, 227, 0.28); + box-shadow: 0 12px 40px rgba(15, 23, 42, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.8); +} + +[data-theme="light"] .lp-pricing-divider, +[data-theme="light"] .lp-footer { + border-color: var(--border-subtle); +} + +[data-theme="light"] .lp-pricing-divider { + background: var(--border-subtle); +} + +[data-theme="light"] .lp-cta-banner-bg { + background: linear-gradient(135deg, #EDE9FE, #E0F2FE, #F8FAFC); +} + +[data-theme="light"] .lp-cta-banner-bg::before { + background: + radial-gradient(ellipse at top left, rgba(229, 166, 83, 0.2), transparent 50%), + radial-gradient(ellipse at bottom right, rgba(136, 192, 163, 0.15), transparent 50%); +} + +[data-theme="light"] .lp-section-head, +[data-theme="light"] .lp-pricing-card, +[data-theme="light"] .lp-feature-card, +[data-theme="light"] .lp-faq-item, +[data-theme="light"] .lp-team-card, +[data-theme="light"] .lp-placement-card { + border-color: var(--border-subtle); +} + +[data-theme="light"] .lp-ghost-num { + color: rgba(15, 23, 42, 0.06); + -webkit-text-stroke: 1px rgba(15, 23, 42, 0.1); +} + +.contest-page { + min-height: 100vh; + background: var(--shell-bg); + color: var(--text-primary); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + position: relative; } /* ── Reset ── */ @@ -462,11 +788,11 @@ body::before { CARDS ============================================================ */ .card { - background: rgba(17, 24, 39, 0.7); + background: var(--card-bg); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); - border: 1px solid rgba(255, 255, 255, 0.06); - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.04); + border: 1px solid var(--card-border); + box-shadow: var(--surface-shadow); border-radius: var(--radius-lg); padding: 24px; position: relative; @@ -485,6 +811,15 @@ body::before { border-radius: var(--radius-md); } +.glass-card { + background: var(--surface-glass); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border: 1px solid var(--surface-border); + box-shadow: var(--surface-shadow); + border-radius: var(--radius-lg); +} + /* ============================================================ INPUTS ============================================================ */ @@ -583,6 +918,8 @@ body::before { .app-shell { display: flex; min-height: 100vh; + background: var(--shell-bg); + color: var(--text-primary); } .main-content { @@ -605,8 +942,8 @@ body::before { ============================================================ */ .sidebar { width: var(--sidebar-width); - background: linear-gradient(180deg, #0A0F1E 0%, #080C18 100%); - border-right: 1px solid rgba(255, 255, 255, 0.05); + background: var(--sidebar-bg); + border-right: 1px solid var(--sidebar-border); height: 100vh; position: fixed; top: 0; @@ -663,8 +1000,8 @@ body::before { /* User card */ .sidebar-user { margin: 0 12px 8px; - background: linear-gradient(135deg, rgba(229, 166, 83, 0.1), rgba(79, 70, 229, 0.06)); - border: 1px solid rgba(139, 92, 246, 0.15); + background: var(--sidebar-user-bg); + border: 1px solid var(--sidebar-user-border); border-radius: var(--radius-lg); padding: 12px 14px; display: flex; @@ -736,12 +1073,12 @@ body::before { } .nav-item:hover { - background: rgba(255, 255, 255, 0.04); + background: var(--nav-hover-bg); color: var(--text-primary); } .nav-item.active { - background: rgba(139, 92, 246, 0.1); + background: var(--nav-active-bg); color: var(--text-primary); font-weight: 600; } @@ -851,7 +1188,7 @@ body::before { /* XP bar */ .sidebar-bottom { padding: 12px; - border-top: 1px solid rgba(255, 255, 255, 0.04); + border-top: 1px solid var(--sidebar-border); } .sidebar-xp-bar { @@ -880,7 +1217,7 @@ body::before { .sidebar-xp-track { height: 4px; - background: rgba(255, 255, 255, 0.06); + background: var(--xp-track-bg); border-radius: 2px; overflow: hidden; } @@ -914,10 +1251,10 @@ body::before { left: var(--sidebar-width); right: 0; height: var(--topbar-height); - background: rgba(8, 12, 20, 0.7); + background: var(--topbar-bg); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); - border-bottom: 1px solid rgba(255, 255, 255, 0.04); + border-bottom: 1px solid var(--topbar-border); display: flex; align-items: center; justify-content: space-between; @@ -2814,12 +3151,12 @@ body::before { -webkit-backdrop-filter: blur(22px); border: 1px solid var(--edge); border-radius: 18px; - box-shadow: 0 14px 40px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(237, 228, 206, 0.04); - transition: box-shadow 0.3s, border-color 0.3s; + box-shadow: none; + transition: border-color 0.3s; } .ml-nav-scrolled { - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(237, 228, 206, 0.05); + box-shadow: none; border-color: rgba(229, 166, 83, 0.35); } diff --git a/frontend/website/src/pages/ChallengePage.jsx b/frontend/website/src/pages/ChallengePage.jsx index fd1c97c..3901dc2 100644 --- a/frontend/website/src/pages/ChallengePage.jsx +++ b/frontend/website/src/pages/ChallengePage.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from 'react' +import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import Sidebar from '../components/Sidebar' import Topbar from '../components/TopBar' @@ -55,8 +55,8 @@ function PresetPicker({ value, onChange }) { return (
{n}
- {children} + {children}
) } @@ -110,7 +110,7 @@ function SectionLabel({ n, children }) { // ─── Back button ────────────────────────────────────────────────────────────── function BackBtn({ onBack }) { return ( - ) @@ -154,12 +154,12 @@ function DuelSetup({ onBack, onSuccess, myEmail }) {
⚔️
Challenge Sent!
-
- Duel #{success.id} sent to {success.opponentName || success.opponentId}.
Waiting for them to accept. +
+ Duel #{success.id} sent to {success.opponentName || success.opponentId}.
Waiting for them to accept.
- +
@@ -200,9 +200,9 @@ function DuelSetup({ onBack, onSuccess, myEmail }) { spellCheck={false} maxLength={30} placeholder="their_handle" - style={{ width: '100%', padding: '12px 14px 12px 36px', borderRadius: 12, background: 'rgba(255,255,255,.04)', border: '1.5px solid rgba(255,255,255,.1)', color: '#F1F5F9', fontSize: 14, outline: 'none', boxSizing: 'border-box', transition: 'border-color .2s' }} + style={{ width: '100%', padding: '12px 14px 12px 36px', borderRadius: 12, background: 'var(--surface-glass)', border: '1.5px solid var(--surface-border)', color: 'var(--text-primary)', fontSize: 14, outline: 'none', boxSizing: 'border-box', transition: 'border-color .2s' }} onFocus={e => e.target.style.borderColor = 'rgba(229,166,83,.5)'} - onBlur={e => e.target.style.borderColor = 'rgba(255,255,255,.1)'} + onBlur={e => e.target.style.borderColor = 'var(--surface-border)'} />
@@ -276,9 +276,9 @@ function ContestSetup({ onBack }) { setName(e.target.value)} placeholder="e.g. Friday Night Grind, Team Qualifier…" - style={{ width: '100%', padding: '11px 14px', borderRadius: 11, background: 'rgba(255,255,255,.04)', border: '1.5px solid rgba(255,255,255,.1)', color: '#F1F5F9', fontSize: 13.5, outline: 'none', boxSizing: 'border-box', transition: 'border-color .2s' }} - onFocus={e => e.target.style.borderColor = 'rgba(229,166,83,.5)'} - onBlur={e => e.target.style.borderColor = 'rgba(255,255,255,.1)'} + style={{ width: '100%', padding: '11px 14px', borderRadius: 11, background: 'var(--surface-glass)', border: '1.5px solid var(--surface-border)', color: 'var(--text-primary)', fontSize: 13.5, outline: 'none', boxSizing: 'border-box', transition: 'border-color .2s' }} + onFocus={e => e.target.style.borderColor = 'var(--border-hover)'} + onBlur={e => e.target.style.borderColor = 'var(--surface-border)'} />
@@ -292,9 +292,9 @@ function ContestSetup({ onBack }) { type="email" value={em} onChange={e => updateEmail(i, e.target.value)} placeholder={`Participant ${i + 1} email`} - style={{ flex: 1, padding: '10px 14px', borderRadius: 10, background: 'rgba(255,255,255,.04)', border: '1.5px solid rgba(255,255,255,.08)', color: '#F1F5F9', fontSize: 13, outline: 'none', transition: 'border-color .2s' }} - onFocus={e => e.target.style.borderColor = 'rgba(229,166,83,.4)'} - onBlur={e => e.target.style.borderColor = 'rgba(255,255,255,.08)'} + style={{ flex: 1, padding: '10px 14px', borderRadius: 10, background: 'var(--surface-glass)', border: '1.5px solid var(--surface-border)', color: 'var(--text-primary)', fontSize: 13, outline: 'none', transition: 'border-color .2s' }} + onFocus={e => e.target.style.borderColor = 'var(--border-hover)'} + onBlur={e => e.target.style.borderColor = 'var(--surface-border)'} /> {emails.length > 1 && ( @@ -307,8 +307,8 @@ function ContestSetup({ onBack }) {
{/* Max participants */} -
- Max participants +
+ Max participants setMaxPeople(Math.min(50, Math.max(2, Number(e.target.value) || 2)))} @@ -335,7 +335,7 @@ function ContestSetup({ onBack }) {
🔗 Invite Link
-
+
{inviteLink}
- + {fmt(count)} @@ -402,33 +475,33 @@ function WeeklyChallengeWidget() { ) } -// Trending Topics widget — static, backend integration in separate issue function TrendingTopicsWidget() { + const theme = 'dark' return (
- + - TRENDING TOPICS + TRENDING TOPICS
{TRENDING_TOPICS.map((t, i) => (
{ e.currentTarget.style.opacity = '0.75' }} onMouseLeave={e => { e.currentTarget.style.opacity = '1' }} > - - # {t.tag} + + # {t.tag} - {t.count} + {t.count}
))}
@@ -436,17 +509,17 @@ function TrendingTopicsWidget() { ) } -// Top Contributors widget — static, backend integration in separate issue function TopContributorsWidget() { - const rankColors = ['#E5A653', '#94A3B8', '#CD7F32'] + const theme = 'dark' + const rankColors = [getCSSVar('accent-amber', theme), getCSSVar('text-muted', theme), '#CD7F32'] return (
🏅 - TOP CONTRIBUTORS + TOP CONTRIBUTORS
{TOP_CONTRIBUTORS.map((c, i) => ( @@ -465,8 +538,8 @@ function TopContributorsWidget() { fontSize: 13, fontWeight: 800, color: '#fff', flexShrink: 0, }}>{c.name[0]}
-
{c.name}
-
{c.rep} · {c.solutions}
+
{c.name}
+
{c.rep} · {c.solutions}
))} @@ -475,42 +548,43 @@ function TopContributorsWidget() { ) } -// Community Stats widget — static function CommunityStatsWidget() { + const theme = 'dark' return (
-
-
1,204
-
+
+
1,204
+
ONLINE
-
342
-
POSTS TODAY
+
342
+
POSTS TODAY
) } -// ─── Search Bar ─────────────────────────────────────────────────────────────── +// Search Bar function SearchBar({ value, onChange }) { + const theme = 'dark' return (
{ e.currentTarget.style.borderColor = 'rgba(229,166,83,0.4)' }} onBlur={e => { e.currentTarget.style.borderColor = 'rgba(255,255,255,0.09)' }} > - +
) } -// ─── Markdown renderer (unchanged from original) ────────────────────────────── +// Markdown renderer function safeLinkUrl(raw) { if (raw == null) return null const url = String(raw).trim() @@ -562,7 +636,7 @@ function mdInline(s, keyBase = 'i') { return out } -function Markdown({ text }) { +function Markdown({ text, theme = 'dark' }) { if (!text) return null const lines = text.replace(/\r\n/g, '\n').split('\n') const blocks = [] @@ -611,7 +685,39 @@ function Markdown({ text }) { return
{blocks}
} -// ─── Write Editor (full-page, unchanged from original) ──────────────────────── +//Markdown CSS +function getMarkdownCSS(theme = 'dark') { + return ` +.md-toolbar { display:flex; flex-wrap:wrap; align-items:center; gap:4px; padding:6px 8px; margin-bottom:14px; background:rgba(15,23,42,0.55); border:1px solid rgba(255,255,255,0.06); border-radius:12px; backdrop-filter:blur(6px); } +.md-tb-btn { min-width:32px; height:32px; padding:0 10px; background:transparent; border:1px solid transparent; color:#CBD5E1; font-size:13px; font-weight:600; letter-spacing:0.02em; border-radius:8px; cursor:pointer; transition:background 0.15s,color 0.15s,border-color 0.15s,transform 0.1s; display:inline-flex; align-items:center; justify-content:center; } +.md-tb-btn:hover { background:rgba(229,166,83,0.12); color:#F8FAFC; border-color:rgba(229,166,83,0.25); } +.md-tb-btn:active { transform:translateY(1px); } +.md-tb-bold { font-weight:900; } .md-tb-italic { font-style:italic; } .md-tb-mono { font-family:'JetBrains Mono','Fira Code',ui-monospace,monospace; font-size:12px; } +.md-tb-sep { width:1px; height:18px; background:rgba(255,255,255,0.08); margin:0 4px; display:inline-block; } +.md-hint { display:flex; flex-wrap:wrap; gap:10px 16px; align-items:center; margin-top:18px; padding-top:14px; border-top:1px dashed rgba(255,255,255,0.06); font-size:12px; color:#64748B; } +.md-hint-k { font-family:'JetBrains Mono','Fira Code',ui-monospace,monospace; font-size:11.5px; color:#94A3B8; background:rgba(15,23,42,0.6); padding:2px 7px; border-radius:5px; border:1px solid rgba(255,255,255,0.05); } +.md-hint-muted { margin-left:auto; opacity:0.6; font-style:italic; } +.md-body { font-size:15px; line-height:1.8; color:#CBD5E1; letter-spacing:0.005em; } +.md-body > * + * { margin-top:16px; } +.md-h1,.md-h2,.md-h3 { color:#F1F5F9; font-weight:800; letter-spacing:-0.02em; line-height:1.25; margin-top:32px; } +.md-h1 { font-size:24px; margin-top:24px; } .md-h2 { font-size:20px; padding-bottom:6px; border-bottom:1px solid rgba(255,255,255,0.07); } .md-h3 { font-size:17px; color:#E2E8F0; } +.md-p { margin:0; } +.md-body strong { color:#F1F5F9; font-weight:800; } .md-body em { color:#E2E8F0; font-style:italic; } +.md-icode { font-family:'JetBrains Mono','Fira Code',ui-monospace,monospace; font-size:0.88em; padding:2px 7px; border-radius:5px; background:rgba(229,166,83,0.1); border:1px solid rgba(229,166,83,0.18); color:#F3C887; } +.md-block { font-family:'JetBrains Mono','Fira Code',ui-monospace,monospace; font-size:13px; line-height:1.65; padding:14px 16px; background:rgba(8,12,30,0.8); border:1px solid rgba(255,255,255,0.06); border-left:3px solid rgba(229,166,83,0.5); border-radius:10px; color:#E2E8F0; overflow-x:auto; } +.md-block code { background:none; border:none; padding:0; color:inherit; font-size:inherit; } +.md-ul,.md-ol { margin:0; padding-left:24px; color:#CBD5E1; } .md-ul li,.md-ol li { margin:6px 0; padding-left:4px; line-height:1.7; } +.md-ul li::marker { color:#E5A653; } .md-ol li::marker { color:#9F8FE3; font-weight:700; } +.md-quote { margin:0; padding:4px 18px; border-left:3px solid rgba(159,143,227,0.7); background:rgba(159,143,227,0.05); color:#E2E8F0; font-style:italic; border-radius:0 10px 10px 0; } +.md-quote p { margin:8px 0; } +.md-hr { border:none; height:1px; background:linear-gradient(90deg,transparent,rgba(229,166,83,0.3),transparent); margin:24px 0; } +.md-link { color:#F3C887; text-decoration:none; border-bottom:1px solid rgba(243,200,135,0.35); transition:color 0.15s,border-color 0.15s; } +.md-link:hover { color:#FFE4BC; border-bottom-color:rgba(255,228,188,0.7); } +.md-link-blocked { color:#94A3B8; text-decoration:line-through; text-decoration-color:rgba(148,163,184,0.5); cursor:not-allowed; } +` +} + +// Write Editor const TOOLS = [ { id: 'b', label: 'B', title: 'Bold (Ctrl+B)', bold: true, apply: ta => mdWrap(ta, '**', '**', 'bold text') }, { id: 'i', label: 'I', title: 'Italic (Ctrl+I)', italic: true, apply: ta => mdWrap(ta, '*', '*', 'italic') }, @@ -635,6 +741,7 @@ function mdWrap(ta, before, after = before, placeholder = '') { const next = v.slice(0, start) + before + sel + after + v.slice(end) return { value: next, selStart: start + before.length, selEnd: start + before.length + sel.length } } + function mdLinePrefix(ta, prefix) { const start = ta.selectionStart, end = ta.selectionEnd, v = ta.value const lineStart = v.lastIndexOf('\n', start - 1) + 1 @@ -644,11 +751,13 @@ function mdLinePrefix(ta, prefix) { const next = v.slice(0, lineStart) + replaced + v.slice(lineEnd) return { value: next, selStart: lineStart, selEnd: lineStart + replaced.length } } + function mdInsertAt(ta, snippet) { const start = ta.selectionStart, v = ta.value const next = v.slice(0, start) + snippet + v.slice(start) return { value: next, selStart: start + snippet.length, selEnd: start + snippet.length } } + function mdCodeBlock(ta, lang = '', placeholder = 'code') { const start = ta.selectionStart, end = ta.selectionEnd, v = ta.value const sel = v.slice(start, end) || placeholder @@ -667,6 +776,7 @@ function WriteEditor({ onCancel, onPublished }) { const [preview, setPreview] = useState(false) const textareaRef = useRef(null) const [c1] = topicColor(form.topic) + const theme = 'dark' useEffect(() => { const onKey = e => { if (e.key === 'Escape') onCancel() } @@ -715,34 +825,34 @@ function WriteEditor({ onCancel, onPublished }) { return (
-
- -
-
+
{formErr && ( -
+
{formErr}
)}
- setForm(f => ({ ...f, topic: e.target.value }))} style={{ background: `rgba(255,255,255,.06)`, border: `1px solid rgba(255,255,255,.1)`, color: getCSSVar('text-secondary', theme), padding: '8px 14px', borderRadius: 10, fontSize: 13, cursor: 'pointer', outline: 'none' }}> {TOPICS.filter(t => t !== 'all').map(t => )}
- setForm(f => ({ ...f, title: e.target.value }))} placeholder="Post title…" style={{ width: '100%', background: 'transparent', border: 'none', outline: 'none', fontSize: 26, fontWeight: 800, color: '#F8FAFC', letterSpacing: '-0.02em', marginBottom: 20, padding: 0, boxSizing: 'border-box' }} /> -
+ setForm(f => ({ ...f, title: e.target.value }))} placeholder="Post title…" style={{ width: '100%', background: 'transparent', border: 'none', outline: 'none', fontSize: 26, fontWeight: 800, color: getCSSVar('text-primary', theme), letterSpacing: '-0.02em', marginBottom: 20, padding: 0, boxSizing: 'border-box' }} /> +
{!preview ? ( <>
@@ -751,7 +861,7 @@ function WriteEditor({ onCancel, onPublished }) { : )}
-