Skip to content
Open
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
28 changes: 28 additions & 0 deletions components/Layout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Example of updating a common layout component
export default function Layout({ children }) {
return (
<div className="min-h-screen bg-white dark:bg-gray-900">
<nav className="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
{/* Navigation content with light/dark mode support */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
{/* Logo */}
<div className="flex items-center">
<h1 className="text-xl font-bold text-gray-900 dark:text-white">st0x</h1>
</div>
{/* Nav items */}
<div className="flex items-center space-x-4">
<a href="/dashboard" className="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">
Dashboard
</a>
<a href="/dashboard/settings" className="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">
Settings
</a>
</div>
</div>
</div>
</nav>
<main className="flex-1">{children}</main>
</div>
Comment on lines +4 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

flex-1 on <main> has no effect without flex container.

The flex-1 class on Line 25 won't work because the parent <div> doesn't have display: flex. The main content won't expand to fill remaining height.

🛠️ Proposed fix
-    <div className="min-h-screen bg-white dark:bg-gray-900">
+    <div className="min-h-screen flex flex-col bg-white dark:bg-gray-900">
🤖 Prompt for AI Agents
In `@components/Layout.jsx` around lines 4 - 26, The top-level container isn't a
flex container so the <main> element's flex-1 has no effect; update the wrapping
div that contains <nav> and <main> (the div with classes "min-h-screen bg-white
dark:bg-gray-900") to be a column flex container (add "flex flex-col") so that
<main className="flex-1"> grows to fill remaining space; ensure the nav remains
above and children render inside the flex column.

);
}
46 changes: 46 additions & 0 deletions components/ThemeToggle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useState, useEffect } from 'react';

export default function ThemeToggle() {
const [isDark, setIsDark] = useState(true);

useEffect(() => {
// Check for saved theme preference or default to dark mode
const savedTheme = localStorage.getItem('theme');
const prefersDark = savedTheme ? savedTheme === 'dark' : true;
setIsDark(prefersDark);

if (prefersDark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, []);
Comment on lines +4 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Initial state mismatch causes toggle UI flash.

The initial useState(true) doesn't account for the actual stored preference. On first render, the toggle will show dark mode, then potentially flip to light mode after useEffect runs, causing a visual flash.

Consider initializing state lazily or using a loading state:

🛠️ Proposed fix with lazy initialization
 export default function ThemeToggle() {
-  const [isDark, setIsDark] = useState(true);
+  const [isDark, setIsDark] = useState(() => {
+    if (typeof window === 'undefined') return true;
+    const savedTheme = localStorage.getItem('theme');
+    return savedTheme ? savedTheme === 'dark' : true;
+  });

   useEffect(() => {
-    // Check for saved theme preference or default to dark mode
-    const savedTheme = localStorage.getItem('theme');
-    const prefersDark = savedTheme ? savedTheme === 'dark' : true;
-    setIsDark(prefersDark);
-    
-    if (prefersDark) {
+    // Apply theme class on mount
+    if (isDark) {
       document.documentElement.classList.add('dark');
     } else {
       document.documentElement.classList.remove('dark');
     }
-  }, []);
+  }, [isDark]);
🤖 Prompt for AI Agents
In `@components/ThemeToggle.jsx` around lines 4 - 17, The component initializes
theme state eagerly with useState(true) causing a flash; change to lazy
initialization by passing a function to useState that reads localStorage (e.g.,
useState(() => { const s = localStorage.getItem('theme'); return s ? s ===
'dark' : true; })) so isDark reflects the persisted preference on first render
and remove the initial forced set in the useEffect; ensure
document.documentElement.classList is updated once on mount (in the existing
useEffect) based on that initialized isDark value to avoid UI flicker.


const toggleTheme = () => {
const newIsDark = !isDark;
setIsDark(newIsDark);

if (newIsDark) {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
}
};

return (
<button
onClick={toggleTheme}
className="relative inline-flex items-center h-6 rounded-full w-11 transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-900 bg-gray-200 dark:bg-gray-700"
aria-label="Toggle theme"
>
<span
className={`${
isDark ? 'translate-x-6' : 'translate-x-1'
} inline-block w-4 h-4 transform bg-white rounded-full transition-transform`}
/>
<span className="sr-only">{isDark ? 'Dark' : 'Light'} mode</span>
</button>
);
}
18 changes: 18 additions & 0 deletions pages/_app.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useEffect } from 'react';
import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
useEffect(() => {
// Initialize theme on app load
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'light') {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
}, []);
Comment on lines +5 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Theme initialization in useEffect causes flash of incorrect theme (FOUC).

Since useEffect runs after hydration, users will briefly see the wrong theme before JavaScript applies the correct class. This is especially noticeable on slow connections or when the saved theme differs from the default.

The standard fix is to add a blocking inline script in _document.jsx that runs before React hydrates:

🛠️ Recommended fix using _document.jsx

Create or update pages/_document.jsx:

import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <script
          dangerouslySetInnerHTML={{
            __html: `
              (function() {
                const theme = localStorage.getItem('theme');
                if (theme === 'light') {
                  document.documentElement.classList.remove('dark');
                } else {
                  document.documentElement.classList.add('dark');
                }
              })();
            `,
          }}
        />
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

Then you can simplify _app.jsx:

-import { useEffect } from 'react';
 import '../styles/globals.css';

 function MyApp({ Component, pageProps }) {
-  useEffect(() => {
-    // Initialize theme on app load
-    const savedTheme = localStorage.getItem('theme');
-    if (savedTheme === 'light') {
-      document.documentElement.classList.remove('dark');
-    } else {
-      document.documentElement.classList.add('dark');
-    }
-  }, []);
-
   return <Component {...pageProps} />;
 }
🤖 Prompt for AI Agents
In `@pages/_app.jsx` around lines 5 - 13, Replace the client-side theme init in
the useEffect inside your App component with a blocking inline script injected
in the server document so theme is set before hydration: create/update the
Document component (function Document) to include a script element before Main
that reads localStorage.getItem('theme') and adds/removes the 'dark' class on
document.documentElement accordingly, then remove or simplify the useEffect in
pages/_app.jsx so it no longer toggles the class after hydration.


return <Component {...pageProps} />;
}

export default MyApp;
28 changes: 28 additions & 0 deletions pages/dashboard/settings.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ThemeToggle from '../../components/ThemeToggle';
// ... existing imports

export default function Settings() {
// ... existing code

return (
<div className="min-h-screen bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
<div className="max-w-4xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-8 text-gray-900 dark:text-white">Settings</h1>

{/* Theme Settings Section */}
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6">
<h2 className="text-xl font-semibold mb-4 text-gray-900 dark:text-white">Appearance</h2>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-900 dark:text-white">Theme</p>
<p className="text-sm text-gray-500 dark:text-gray-400">Switch between light and dark mode</p>
</div>
<ThemeToggle />
</div>
</div>

{/* ... rest of existing settings content */}
</div>
</div>
);
}
30 changes: 30 additions & 0 deletions styles/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

/* Base styles for smooth theme transitions */
* {
@apply transition-colors duration-200;
}

/* Scrollbar styling for both themes */
::-webkit-scrollbar {
@apply w-2;
}

::-webkit-scrollbar-track {
@apply bg-gray-100 dark:bg-gray-800;
}

::-webkit-scrollbar-thumb {
@apply bg-gray-400 dark:bg-gray-600 rounded-full;
}

::-webkit-scrollbar-thumb:hover {
@apply bg-gray-500 dark:bg-gray-500;
}

/* Custom focus styles */
*:focus {
@apply outline-none ring-2 ring-indigo-500 dark:ring-indigo-400 ring-offset-2 dark:ring-offset-gray-900;
}
24 changes: 24 additions & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
darkMode: 'class',
theme: {
extend: {
colors: {
// Custom colors for light mode if needed
light: {
primary: '#3B82F6',
secondary: '#10B981',
background: '#F9FAFB',
surface: '#FFFFFF',
text: '#111827',
}
},
},
},
plugins: [],
}
Loading