feat: implement global dark/light mode toggle (#48)#50
Conversation
|
@atmihaa-06 is attempting to deploy a commit to the karan3431's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
🎉 Thank you for your Pull Request! We're thrilled to have your contribution to FreshScan AI. Before we review, please make sure you have:
A maintainer will review your code as soon as possible! |
There was a problem hiding this comment.
2 issues found across 11 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/pages/NotFound.tsx">
<violation number="1" location="src/pages/NotFound.tsx:7">
P1: NotFound page hardcodes `text-gray-*` Tailwind utility classes that do not respond to the global dark/light theme. In the default dark mode (background `#131313`), `text-gray-800`, `text-gray-600`, and `text-gray-500` produce extremely poor contrast, making the page nearly unreadable and breaking the theme contract established by this PR.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| export default function NotFound() { | ||
| return ( | ||
| <div className="flex flex-col items-center justify-center min-h-[60vh] text-center px-4"> | ||
| <h1 className="text-6xl font-bold text-gray-800 mb-4">404</h1> |
There was a problem hiding this comment.
P1: NotFound page hardcodes text-gray-* Tailwind utility classes that do not respond to the global dark/light theme. In the default dark mode (background #131313), text-gray-800, text-gray-600, and text-gray-500 produce extremely poor contrast, making the page nearly unreadable and breaking the theme contract established by this PR.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/NotFound.tsx, line 7:
<comment>NotFound page hardcodes `text-gray-*` Tailwind utility classes that do not respond to the global dark/light theme. In the default dark mode (background `#131313`), `text-gray-800`, `text-gray-600`, and `text-gray-500` produce extremely poor contrast, making the page nearly unreadable and breaking the theme contract established by this PR.</comment>
<file context>
@@ -0,0 +1,20 @@
+export default function NotFound() {
+ return (
+ <div className="flex flex-col items-center justify-center min-h-[60vh] text-center px-4">
+ <h1 className="text-6xl font-bold text-gray-800 mb-4">404</h1>
+ <h2 className="text-2xl font-semibold text-gray-600 mb-6">Page Not Found</h2>
+ <p className="text-gray-500 mb-8">
</file context>
There was a problem hiding this comment.
Pull request overview
Implements a global dark/light theme toggle (persisted via localStorage) and introduces app-wide error toasts for API/network failures, plus a new catch-all 404 route/page.
Changes:
- Added theme initialization + toggle utilities and wired early theme init to reduce load flicker.
- Refactored API fetching to use a shared wrapper with toast-based network/server error notifications.
- Added a
NotFoundpage and a*catch-all route.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pages/NotFound.tsx | New 404 page for unmatched routes. |
| src/pages/AuthPage.tsx | Simplifies redirect status handling and adds guarded OAuth initiation. |
| src/main.tsx | Calls initTheme() before rendering to prevent theme flash. |
| src/lib/theme.ts | Adds theme toggle/init helpers (persistence + root class toggle). |
| src/lib/api.ts | Adds safeFetch + shared response handling and toast error notifications. |
| src/index.css | Introduces CSS-variable-driven theme tokens and light-mode overrides. |
| src/components/Navbar.tsx | Adds a global THEME toggle button in the Navbar. |
| src/components/Layout.tsx | (Currently) adds an unused theme import. |
| src/App.tsx | Adds react-hot-toast Toaster and a * route to NotFound. |
| package.json | Adds react-hot-toast. |
| package-lock.json | Locks dependency changes for react-hot-toast and transitive deps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import Navbar from './Navbar'; | ||
| import BottomNav from './BottomNav'; | ||
| import Footer from './Footer'; | ||
| import { toggleTheme } from '../lib/theme'; |
| // src/lib/theme.ts | ||
| export const toggleTheme = () => { | ||
| const isLight = document.documentElement.classList.toggle('light'); | ||
| localStorage.setItem('theme', isLight ? 'light' : 'dark'); | ||
| }; | ||
|
|
||
| export const initTheme = () => { | ||
| const savedTheme = localStorage.getItem('theme'); | ||
| // Apply saved preference, or default to dark mode | ||
| if (savedTheme === 'light') { | ||
| document.documentElement.classList.add('light'); | ||
| } | ||
| }; No newline at end of file |
| <div className="flex flex-col items-center justify-center min-h-[60vh] text-center px-4"> | ||
| <h1 className="text-6xl font-bold text-gray-800 mb-4">404</h1> | ||
| <h2 className="text-2xl font-semibold text-gray-600 mb-6">Page Not Found</h2> | ||
| <p className="text-gray-500 mb-8"> | ||
| Oops! The page you are looking for doesn't exist or has been moved. | ||
| </p> | ||
| <Link | ||
| to="/" | ||
| className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors" | ||
| > | ||
| Return Home | ||
| </Link> |
| <button | ||
| onClick={toggleTheme} | ||
| className="font-[family-name:var(--font-mono)] text-[10px] tracking-widest text-on-surface-variant hover:text-neon transition-colors duration-200 border border-outline-variant/30 px-3 py-1" | ||
| > | ||
| THEME | ||
| </button> |
| async function handleResponse(res: Response): Promise<Response> { | ||
| if (res.ok) return res; | ||
|
|
||
| // Handle 5xx errors (Server Side) | ||
| if (res.status >= 500) { | ||
| toast.error("Server error. Please try again later."); | ||
| } else { | ||
| // Handle 4xx errors | ||
| const err = await res.json().catch(() => ({ detail: res.statusText })); | ||
| throw new Error((err as { detail?: string }).detail || `HTTP ${res.status}`); | ||
| } | ||
|
|
||
| throw new Error(`HTTP ${res.status}`); | ||
| } |
| :root.light { | ||
| --bg: #ffffff; | ||
| --on-surface: #121212; | ||
| --heading-color: #121212; | ||
| } |
|
Resolve the issues raised by copilot ! |
|
I've addressed all the feedback: Theme/UX: Implemented robust, system-aware theming with localStorage fallback and try/catch safety. Updated the 404 page and CSS variables to ensure full theme compatibility and contrast. Accessibility: Added type="button" to the theme toggle. API Stability: Updated api.ts to throw user-friendly error messages and consolidated the logic within safeFetch. Everything is now consistent, accessible, and theme-aware. Ready for final review! |
Description
This PR introduces a global theme-switching system to the FreshScan AI platform, allowing users to toggle between dark and light modes. This enhances accessibility and user experience by providing a high-contrast theme suited for different environments.
Key Changes
Theme Utility: Created src/lib/theme.ts to manage theme state persistence using localStorage and system preference detection.
Dynamic Styling: Updated index.css to leverage CSS custom properties, ensuring all UI components respond automatically to theme changes.
Initialization: Integrated initTheme() in main.tsx to prevent "theme flashing" (flicker) when the page loads.
UI Integration: Added a centralized THEME toggle button to the Navbar, ensuring easy access across all pages.
Refinement: Optimized typography colors and glass-panel transparency to maintain readability and contrast in both modes.
Implementation Details
State Management: The theme selection is persisted in localStorage.
Design System: Utilized Tailwind's utility-first approach paired with CSS variables defined in the :root and :root.light selectors.
Layout: Cleaned up redundant UI elements by removing the floating toggle button, centralizing controls in the navigation bar for a cleaner aesthetic.
Verification
[x] Toggle functionality confirmed in both Chrome and Firefox.
[x] Theme choice persists across browser reloads.
[x] Contrast ratios checked for all main headings (e.g., "Fish Freshness") in light mode.
Screenshots

Dark Mode:
Light Mode:

Closes #48
Summary by cubic
Adds a global dark/light mode toggle with CSS variables and a Navbar button. Adds global error toasts and centralizes API error handling for clearer network/server failures.
New Features
localStorage, initialized early withinitTheme()to prevent flash, and powered by CSS variables inindex.css.react-hot-toastand<Toaster />inApp.tsxfor network/server error notifications.Refactors
safeFetch()andhandleResponse()insrc/lib/api.ts; all uploads and Grad-CAM now share consistent error handling with friendly messages for 5xx and network drops.:root/:root.light), added heading/body color transitions, and improved light-mode contrast.AuthPage.Written for commit 82e72c6. Summary will update on new commits.