-
Notifications
You must be signed in to change notification settings - Fork 178
added theme toggler button to switch in-between dark and light mode i… #173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds a dark mode feature across the client: introduces a persisted theme store, ThemeProvider, and ThemeToggle; integrates theme class application to document root; updates Tailwind config for class-based dark mode and daisyUI themes; applies dark variants to key pages/components and global styles. Updates example/env files. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Header as Header/ThemeToggle
participant Store as useThemeStore (persisted)
participant Provider as ThemeProvider (effect)
participant DOM as document.documentElement
participant UI as Tailwind/daisyUI styles
User->>Header: Click ThemeToggle
Header->>Store: toggleTheme()
Store-->>Header: theme updated (light↔dark)
Store-->>Provider: state change (subscribe)
Provider->>DOM: Add/remove "dark" class<br/>Set data-theme="light|dark"
DOM-->>UI: Class change detected
UI-->>User: Re-rendered UI with dark/light styles
note over Store: Theme persisted with "theme-storage"
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (2)
client/app/store/themeStore.ts (1)
1-24: LGTM!The theme store implementation is clean and follows Zustand best practices. The persist middleware with localStorage ensures theme preferences are maintained across sessions. The API is simple and intuitive with
theme,toggleTheme, andsetTheme.Optional enhancement: Add error handling for storage failures
The persist middleware may fail in environments where localStorage is unavailable (e.g., private browsing, SSR). While Zustand's persist middleware has built-in fallbacks, you could add explicit error handling:
export const useThemeStore = create<ThemeStore>()( persist( (set) => ({ theme: "light", toggleTheme: () => set((state) => ({ theme: state.theme === "light" ? "dark" : "light", })), setTheme: (theme) => set({ theme }), }), { name: "theme-storage", onRehydrateStorage: () => (state) => { // Optional: Log or handle rehydration errors if (!state) { console.warn('Failed to rehydrate theme from storage'); } }, } ) );Based on learnings: Zustand v4.5.2 includes built-in storage error handling, so explicit error handling is optional unless you need custom error recovery logic.
client/app/components/ThemeToggle/ThemeToggle.tsx (1)
54-56: Ensure consistent icon sizing.The invisible spacer uses
<SunIcon />without explicit size classes, while the visible icons on lines 40 and 51 useclassName="h-5 w-5". For consistency and to prevent unexpected sizing if defaults change, apply the same size classes.Apply this diff:
{/* Invisible spacer to maintain button size */} - <div className="h-5 w-5 opacity-0"> - <SunIcon /> + <div className="opacity-0"> + <SunIcon className="h-5 w-5" /> </div>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
client/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (14)
.env(1 hunks)blockchain/.env.example(0 hunks)client/.env.example(0 hunks)client/.env.lc(1 hunks)client/app/components/Header/Header.tsx(9 hunks)client/app/components/ThemeProvider/ThemeProvider.tsx(1 hunks)client/app/components/ThemeToggle/ThemeToggle.tsx(1 hunks)client/app/create/page.tsx(9 hunks)client/app/globals.css(2 hunks)client/app/layout.tsx(2 hunks)client/app/page.tsx(1 hunks)client/app/profile/page.tsx(3 hunks)client/app/store/themeStore.ts(1 hunks)client/tailwind.config.ts(2 hunks)
💤 Files with no reviewable changes (2)
- client/.env.example
- blockchain/.env.example
🧰 Additional context used
🧬 Code graph analysis (3)
client/app/components/ThemeToggle/ThemeToggle.tsx (1)
client/app/store/themeStore.ts (1)
useThemeStore(10-24)
client/app/components/ThemeProvider/ThemeProvider.tsx (1)
client/app/store/themeStore.ts (1)
useThemeStore(10-24)
client/app/layout.tsx (2)
client/app/components/ThemeProvider/ThemeProvider.tsx (1)
ThemeProvider(6-26)client/app/helpers/client.ts (2)
config(7-17)queryClient(19-19)
🪛 dotenv-linter (3.3.0)
.env
[warning] 1-1: [LeadingCharacter] Invalid leading character detected
(LeadingCharacter)
[warning] 2-2: [LeadingCharacter] Invalid leading character detected
(LeadingCharacter)
[warning] 3-3: [LeadingCharacter] Invalid leading character detected
(LeadingCharacter)
[warning] 3-3: [UnorderedKey] The RPC_URL_FUJI key should go before the RPC_URL_SEPOLIA key
(UnorderedKey)
[warning] 4-4: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
[warning] 4-4: [LeadingCharacter] Invalid leading character detected
(LeadingCharacter)
[warning] 4-4: [UnorderedKey] The ETHERSCAN_KEY key should go before the PRIVATE_KEY key
(UnorderedKey)
client/.env.lc
[warning] 1-1: [SpaceCharacter] The line has spaces around equal sign
(SpaceCharacter)
[warning] 2-2: [SpaceCharacter] The line has spaces around equal sign
(SpaceCharacter)
[warning] 2-2: [UnorderedKey] The NEXT_PUBLIC_AMOY_RPC_URL key should go before the NEXT_PUBLIC_SEPOLIA_RPC_URL key
(UnorderedKey)
[warning] 3-3: [SpaceCharacter] The line has spaces around equal sign
(SpaceCharacter)
[warning] 3-3: [UnorderedKey] The NEXT_PUBLIC_FUJI_RPC_URL key should go before the NEXT_PUBLIC_SEPOLIA_RPC_URL key
(UnorderedKey)
[warning] 4-4: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
[warning] 4-4: [SpaceCharacter] The line has spaces around equal sign
(SpaceCharacter)
[warning] 4-4: [UnorderedKey] The NEXT_PUBLIC_PINATA_JWT key should go before the NEXT_PUBLIC_SEPOLIA_RPC_URL key
(UnorderedKey)
🔇 Additional comments (14)
client/tailwind.config.ts (1)
9-9: LGTM!The Tailwind dark mode configuration is correct. Using
darkMode: "class"enables class-based dark mode toggling, which aligns perfectly with the ThemeProvider implementation that adds/removes the "dark" class on the document root. The daisyUI themes expansion to include both "light" and "dark" ensures proper theme support across the component library.Also applies to: 21-21
client/app/globals.css (2)
9-9: LGTM!The addition of
transition: background-color 0.3s easeon the body provides a smooth visual transition when switching themes, enhancing user experience. The dark mode body background color (#1a1a1a) is appropriately dark while remaining readable.Also applies to: 12-14
31-33: LGTM!The dark mode scrollbar styling is comprehensive and visually consistent with the overall dark theme. The colors chosen (#2a2a2a for track, #4a4a4a for thumb, #5a5a5a for hover) provide good contrast while maintaining the dark aesthetic.
Also applies to: 42-53
client/app/page.tsx (1)
6-6: LGTM!The dark mode background classes are correctly applied using Tailwind's
dark:variant. The color progression from white to gray-900 provides appropriate contrast for dark mode.client/app/layout.tsx (1)
15-15: LGTM!The ThemeProvider is correctly positioned as the outermost provider, ensuring the theme class is applied to the document root before any components render. This placement allows all nested providers and components to benefit from the theme context.
Also applies to: 32-44
client/app/create/page.tsx (4)
110-110: LGTM!The dark mode styling is comprehensive and correctly applied. The gradient backgrounds and card container properly support both light and dark themes with appropriate color choices (gray-800/gray-900 for dark backgrounds, gray-100 for dark text).
Also applies to: 116-120
135-150: LGTM!The candidate section styling is properly updated for dark mode with consistent use of dark: variants. The quote escaping on Line 150 (
") is correct for JSX strings. The border colors, backgrounds, and text colors provide good contrast in both light and dark modes.Also applies to: 158-193
196-213: LGTM!Form controls (select, input, textarea, labels, date picker) are correctly styled for dark mode with appropriate background (gray-700), border (gray-600), and text colors. The styling is consistent across all form elements.
Also applies to: 259-295, 310-310
341-345: LGTM!The ChainSwitchModal correctly applies dark mode styling to its background and text, ensuring the modal is readable in both themes.
client/app/components/ThemeProvider/ThemeProvider.tsx (1)
1-26: Consider mitigating the initial theme flash.The ThemeProvider correctly applies the theme class and data-theme attribute to the document root. However, since this runs in a useEffect (client-side only), there may be a brief flash of the wrong theme on initial page load before the persisted theme is applied.
To verify if the flash is noticeable, test the following scenario:
- Set the theme to dark and refresh the page
- Observe if there's a visible flash of light theme before dark theme is applied
If the flash is problematic, consider these solutions:
Solution 1: Add an inline script (recommended for persisted themes)
In
layout.tsx, before the body content, add an inline script to apply the theme immediately:<script dangerouslySetInnerHTML={{ __html: ` (function() { const theme = localStorage.getItem('theme-storage'); if (theme) { const { state } = JSON.parse(theme); if (state.theme === 'dark') { document.documentElement.classList.add('dark'); document.documentElement.setAttribute('data-theme', 'dark'); } } })(); `, }} />Solution 2: Detect system preference (optional enhancement)
Update the store initialization to detect system preference:
theme: typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light',client/app/profile/page.tsx (2)
11-17: LGTM!The skeleton loading state correctly implements dark mode variants with appropriate gray-scale progression (gray-800 for container, gray-700 for bars). This provides a consistent loading experience in both themes.
50-63: LGTM!The ProfilePage container and content properly support dark mode with well-chosen background colors (gray-800) and text colors (gray-100). The "No elections found" message uses gray-400 for dark mode, which provides good contrast while maintaining the subtle appearance of empty state messaging.
client/app/components/Header/Header.tsx (2)
16-16: LGTM! ThemeToggle integration is well-implemented.The ThemeToggle component is properly imported and placed in both desktop navigation (line 80) and mobile menu (line 88), ensuring consistent theme-switching functionality across all breakpoints.
Also applies to: 80-80, 88-88
33-33: LGTM! Dark mode styling is comprehensive and consistent.The dark mode variants are well-applied throughout the Header component, covering:
- Container backgrounds and borders (lines 33, 115)
- Text colors with appropriate contrast (lines 48, 60-61, 136-137)
- Interactive states (hover, active) with consistent color transitions (lines 61-62, 71, 94, 124, 136-137)
The styling choices follow Tailwind conventions and maintain visual hierarchy in both light and dark modes.
Also applies to: 48-48, 60-62, 71-71, 94-94, 115-115, 124-124, 136-137
| PRIVATE_KEY=011d4ba8e7b549ee9cd718df4c25abed | ||
| RPC_URL_SEPOLIA=https://eth-mainnet.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk | ||
| RPC_URL_FUJI=https://avax-fuji.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk | ||
| ETHERSCAN_KEY=WCU9BD1WA1835GAU9VBTGSPA2DGEPQHYSF No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strip secrets from tracked .env.
Lines [1-4] embed a blockchain private key, Alchemy RPC URLs with API keys, and an Etherscan key. These are sensitive credentials that must remain local only. Remove this committed .env, add placeholders/examples instead, rotate every exposed key, and scrub the repository history before merging.
🧰 Tools
🪛 dotenv-linter (3.3.0)
[warning] 1-1: [LeadingCharacter] Invalid leading character detected
(LeadingCharacter)
[warning] 2-2: [LeadingCharacter] Invalid leading character detected
(LeadingCharacter)
[warning] 3-3: [LeadingCharacter] Invalid leading character detected
(LeadingCharacter)
[warning] 3-3: [UnorderedKey] The RPC_URL_FUJI key should go before the RPC_URL_SEPOLIA key
(UnorderedKey)
[warning] 4-4: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
[warning] 4-4: [LeadingCharacter] Invalid leading character detected
(LeadingCharacter)
[warning] 4-4: [UnorderedKey] The ETHERSCAN_KEY key should go before the PRIVATE_KEY key
(UnorderedKey)
🤖 Prompt for AI Agents
In .env around lines 1 to 4 there are committed secrets (private key, Alchemy
RPC URLs with API keys, and Etherscan key); remove the tracked .env from the
repo, replace it with a .env.example containing placeholder values and usage
notes, add .env to .gitignore so local secrets aren’t committed, rotate/revoke
all exposed credentials immediately (generate new private key and API keys), and
scrub the repository history (use git filter-repo or BFG to remove the secrets
from past commits) before merging; notify the team about key rotation and
provide the new placeholders and instructions for securely injecting the real
values in CI/deploy.
| NEXT_PUBLIC_SEPOLIA_RPC_URL = https://eth-mainnet.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk | ||
| NEXT_PUBLIC_AMOY_RPC_URL = https://polygon-amoy.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk | ||
| NEXT_PUBLIC_FUJI_RPC_URL = https://avax-fuji.g.alchemy.com/v2/MBGDHRRT3_qjtqKzM2DZk | ||
| NEXT_PUBLIC_PINATA_JWT = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5mb3JtYXRpb24iOnsiaWQiOiI3Zjc0NGIzYi03NGZlLTQyOGMtYTY2NS1kMGFjODkwNTE0N2YiLCJlbWFpbCI6ImhlbWFudC5rQGFkeXB1LmVkdS5pbiIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaW5fcG9saWN5Ijp7InJlZ2lvbnMiOlt7ImRlc2lyZWRSZXBsaWNhdGlvbkNvdW50IjoxLCJpZCI6IkZSQTEifSx7ImRlc2lyZWRSZXBsaWNhdGlvbkNvdW50IjoxLCJpZCI6Ik5ZQzEifV0sInZlcnNpb24iOjF9LCJtZmFfZW5hYmxlZCI6ZmFsc2UsInN0YXR1cyI6IkFDVElWRSJ9LCJhdXRoZW50aWNhdGlvblR5cGUiOiJzY29wZWRLZXkiLCJzY29wZWRLZXlLZXkiOiIzNDRlMDU0ZTRiMDhkZWZjOTlmNiIsInNjb3BlZEtleVNlY3JldCI6IjE2MTcyZWM2YTMyYjVkMTlkZjUzNWE4ZjM2OTZlNzg5NGExMjY4MjViMTBmNTA2OWYzZDM4MWY4ZDBhMWZjNzQiLCJleHAiOjE3OTEzMDUwOTB9.c8lfSqJIUNh4F4DOvys5qI07pMwUc1H91dUBwt8emfI No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove hard-coded credentials immediately.
Lines [1-4] check in live Alchemy API keys and a Pinata JWT. These values grant direct access to third-party services and must never be stored in the repo. Purge the file from git history (or replace with placeholder/env.example) and provision fresh keys that haven’t been exposed.
🧰 Tools
🪛 dotenv-linter (3.3.0)
[warning] 1-1: [SpaceCharacter] The line has spaces around equal sign
(SpaceCharacter)
[warning] 2-2: [SpaceCharacter] The line has spaces around equal sign
(SpaceCharacter)
[warning] 2-2: [UnorderedKey] The NEXT_PUBLIC_AMOY_RPC_URL key should go before the NEXT_PUBLIC_SEPOLIA_RPC_URL key
(UnorderedKey)
[warning] 3-3: [SpaceCharacter] The line has spaces around equal sign
(SpaceCharacter)
[warning] 3-3: [UnorderedKey] The NEXT_PUBLIC_FUJI_RPC_URL key should go before the NEXT_PUBLIC_SEPOLIA_RPC_URL key
(UnorderedKey)
[warning] 4-4: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
[warning] 4-4: [SpaceCharacter] The line has spaces around equal sign
(SpaceCharacter)
[warning] 4-4: [UnorderedKey] The NEXT_PUBLIC_PINATA_JWT key should go before the NEXT_PUBLIC_SEPOLIA_RPC_URL key
(UnorderedKey)
🤖 Prompt for AI Agents
In client/.env.lc around lines 1-4 you have committed live Alchemy API keys and
a Pinata JWT; remove these secrets immediately, replace the file content with
placeholder variables (or add a client/.env.example) and add client/.env.lc to
.gitignore so no local secrets are committed; purge the sensitive values from
repository history using a tool like git filter-repo or BFG (or follow your
org’s secret-removal procedure), rotate/revoke the exposed Alchemy and Pinata
credentials and provision new keys, notify the team/security and update
deployment/config to use the new secrets (prefer environment-based secret
storage), and add a pre-commit secret scan hook (git-secrets or similar) to
prevent future accidental commits.
| useEffect(() => { | ||
| // Apply theme to document | ||
| if (theme === "dark") { | ||
| document.documentElement.classList.add("dark"); | ||
| document.documentElement.setAttribute("data-theme", "dark"); | ||
| } else { | ||
| document.documentElement.classList.remove("dark"); | ||
| document.documentElement.setAttribute("data-theme", "light"); | ||
| } | ||
| }, [theme]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Remove duplicate theme application logic.
The useEffect in ThemeToggle directly manipulates document.documentElement, but per the PR's AI summary, ThemeProvider (client/app/components/ThemeProvider/ThemeProvider.tsx) is responsible for applying the theme class and data-theme attribute to the document root. This duplicates the theme application logic, violating single responsibility and potentially causing synchronization issues.
The ThemeToggle should only call toggleTheme from the store; the provider should react to store changes and apply the theme.
Apply this diff to remove the duplicate logic:
- useEffect(() => {
- // Apply theme to document
- if (theme === "dark") {
- document.documentElement.classList.add("dark");
- document.documentElement.setAttribute("data-theme", "dark");
- } else {
- document.documentElement.classList.remove("dark");
- document.documentElement.setAttribute("data-theme", "light");
- }
- }, [theme]);
-
return (Verify that ThemeProvider handles theme application:
🏁 Script executed:
#!/bin/bash
# Verify ThemeProvider applies the theme to document root
rg -A 10 -B 2 'documentElement' client/app/components/ThemeProvider/ThemeProvider.tsxLength of output: 358
Remove duplicate theme application in ThemeToggle
ThemeToggle’s useEffect duplicates ThemeProvider’s DOM updates. Delete lines 11–20 so ThemeToggle only calls toggleTheme, and let ThemeProvider handle applying the dark class and data-theme attribute.
- useEffect(() => {
- // Apply theme to document
- if (theme === "dark") {
- document.documentElement.classList.add("dark");
- document.documentElement.setAttribute("data-theme", "dark");
- } else {
- document.documentElement.classList.remove("dark");
- document.documentElement.setAttribute("data-theme", "light");
- }
- }, [theme]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| // Apply theme to document | |
| if (theme === "dark") { | |
| document.documentElement.classList.add("dark"); | |
| document.documentElement.setAttribute("data-theme", "dark"); | |
| } else { | |
| document.documentElement.classList.remove("dark"); | |
| document.documentElement.setAttribute("data-theme", "light"); | |
| } | |
| }, [theme]); |
🤖 Prompt for AI Agents
In client/app/components/ThemeToggle/ThemeToggle.tsx around lines 11 to 20,
remove the useEffect block that applies the theme to document (the DOM updates
adding/removing the "dark" class and setting data-theme) so ThemeToggle no
longer manipulates the DOM; leave the component to only call toggleTheme and
rely on ThemeProvider to apply the "dark" class and data-theme attribute.
|
Can i get the review on this PR? |
Issue #170
Description
Include a summary of the change and which issue is fixed. List any dependencies that are required for this change.
Fixes #170 (issue)
Type of change
Please mark the options that are relevant.
Checklist:
Summary by CodeRabbit
New Features
Style
Chores