Skip to content

feat: enhance header navigation and improve responsive user experienceFeature/navbar improvements - #611

Open
hariharan-km wants to merge 12 commits into
FOSSUChennai:mainfrom
hariharan-km:feature/navbar-improvements
Open

feat: enhance header navigation and improve responsive user experienceFeature/navbar improvements#611
hariharan-km wants to merge 12 commits into
FOSSUChennai:mainfrom
hariharan-km:feature/navbar-improvements

Conversation

@hariharan-km

@hariharan-km hariharan-km commented Jun 5, 2026

Copy link
Copy Markdown

Summary

This PR enhances the header/navigation experience by improving component structure, responsiveness, and overall usability.

Changes Made

  • Refined header navigation layout and styling
  • Improved responsiveness across different screen sizes
  • Updated GitHub action button integration within the header
  • Enhanced notification subscription component presentation and interaction
  • Improved consistency of navigation-related UI elements

Benefits

  • Better user experience on desktop and mobile devices
  • More consistent and accessible navigation interface
  • Cleaner header component behavior and visual hierarchy
  • Improved maintainability of header-related components

Testing

  • Verified header rendering across multiple viewport sizes
  • Tested navigation interactions locally
  • Confirmed GitHub and notification actions remain functional after the UI updates

Screenshots

Before :
image

After :
image

Summary by CodeRabbit

  • New Features
    • Added scroll-aware, sticky header styling; notification, GitHub, and theme controls adapt as you scroll
    • Introduced client-side theme support with a theme toggle and early theme initialization
  • Bug Fixes
    • Prevents crashes when push notification configuration is missing and surfaces clearer subscribe errors
    • Restored notification button label visibility on small screens
    • Fixed header logo sizing
    • Improved dark-mode styling for error and 404 pages
  • Chores
    • Cleared existing event entries (events list emptied)

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Implements a complete dark mode system using Tailwind's class strategy with theme context, localStorage persistence, and pre-hydration initialization. Header becomes a client component that tracks scroll position and passes isScrolled to child components for adaptive styling. PushSubscribe and GitHubButton gain scroll-responsive variants. Dark mode styling is applied consistently across all pages and components. Events dataset is cleared.

Changes

Dark mode infrastructure and system integration

Layer / File(s) Summary
Tailwind dark mode config and CSS transitions
tailwind.config.ts, src/app/globals.css
Tailwind configured to use darkMode: 'class' strategy; global CSS adds smooth 0.3s ease transitions for background-color and color.
Theme initialization script for hydration-safe setup
public/theme-init.js
Inline script reads localStorage.theme and prefers-color-scheme, applies dark class to document.documentElement before hydration; errors silently caught.
React theme context provider and hook
src/components/ThemeProvider.tsx
Defines Theme type and ThemeContext; ThemeProvider manages theme state with localStorage and system preference fallback, applies/removes dark class on updates, and provides { theme, toggleTheme } context; useTheme hook exposes context with error guard.
Theme toggle button component
src/components/ThemeToggle.tsx
Client component reads theme from context, toggles on click, accepts optional isScrolled prop for styling variants, conditionally renders sun/moon icons with CSS transitions, and includes accessible title/aria-label.
Root layout wraps content with ThemeProvider and theme-init script
src/app/layout.tsx
Imports next/script and ThemeProvider; injects /theme-init.js with beforeInteractive; adds suppressHydrationWarning to <html>; applies dark-text classes to <body>; wraps Header, children, and Footer within ThemeProvider.

Scroll-responsive header with component wiring

Layer / File(s) Summary
Header client directive and scroll state management
src/components/shared/header.tsx
Adds 'use client'; establishes isScrolled state with throttled requestAnimationFrame scroll listener and proper cleanup.
Header sticky layout and scroll-aware child component wiring
src/components/shared/header.tsx
Restructures header from <nav> to sticky <header>; applies conditional classNames based on isScrolled; fixes logo height class to h-8; updates RSS link styling; passes isScrolled to PushSubscribe, GitHubButton, and ThemeToggle; removes responsive duplication wrappers.

PushSubscribe and GitHubButton scroll-responsive updates

Layer / File(s) Summary
PushSubscribe portal rendering with mount gating and VAPID validation
src/components/PushSubscribe.tsx
Extends props with isScrolled, adds isMounted state and createPortal rendering into document.body post-mount; VAPID key defaults to empty string with runtime validation inside subscribeToNotifications that throws if empty; notification button styling branches on isScrolled and label is always visible.
GitHubButton scroll-responsive styling
src/components/github-button.tsx
Component accepts optional isScrolled prop; applies conditional anchor classes for background/text/shadow/hover variants based on scroll state; GitHub icon adds dark:invert class.

Dark mode styling across pages and components

Layer / File(s) Summary
Error and 404 page dark mode styling
src/app/error.tsx, src/app/not-found.tsx
Error page adds dark:* classes to heading, button, and message container; 404 back-home link updated with bg-gradient-to-r blue gradient, font size, and dark-mode gradient/hover variants.
Hero section and CTA dark mode styling
src/components/pages/home/hero.tsx, src/components/pages/home/calltoaction.tsx
Hero container adds dark background, heading switches to white in dark mode, paragraph uses dark-mode gray; CTA adds dark-mode shadow.
Home events component dark mode styling
src/components/pages/home/events.tsx
Event cards gain dark background/border/hover classes; community badges and metadata badges (location/date/time) receive dark-mode variants; headers and tooltips updated with dark styling.
Archive page dark mode styling
src/components/pages/Archive/archive.tsx
Event cards, community badges (both tooltip and regular paths), titles, and metadata all receive dark-mode variants; filter controls, sort buttons, results count, and tooltip bubble/arrow include dark-mode border/background/text classes.
Communities page dark mode styling
src/components/pages/Communities/Community.tsx, src/components/pages/Communities/HoverIcon.tsx
Community cards and page wrapper receive dark background/text/border variants; card title, location, and description updated with dark classes; divider refactored with dark border; search input and heading styled for dark mode; social link icon adds dark color.
Footer and empty events card dark mode styling
src/components/shared/footer.tsx, src/components/no-events-card.tsx
Footer container and links (including new wiki.tamilnadu.tech link) receive dark-mode classes; empty card border/background and text updated with dark variants.

Event data cleanup

Layer / File(s) Summary
Clear event data
src/data/events.json
Events array replaced with empty JSON array.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • FOSSUChennai/Communities#477: Adjusts VAPID/push-notification setup and environment gating, complementing this PR's runtime VAPID validation changes in PushSubscribe.tsx.

Suggested labels

enhancement

Suggested reviewers

  • JustinBenito
  • Sarveshmrao

Poem

🐰 Through dark and light the rabbit hops with glee,
Theme context blooms, as scrolls decree,
Portal pages render, VAPID checks take flight,
Dark shadows dance where once was bright,
A whisker twitch—the site feels right! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately reflects the main changes: header navigation enhancements and responsive experience improvements across multiple components, which aligns with updates to header, navigation buttons, scroll state handling, and dark mode support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/shared/header.tsx (2)

14-25: ⚡ Quick win

Optimize scroll event listener performance.

The scroll listener fires on every scroll event, which can cause performance issues during rapid scrolling. Throttling or debouncing the handler would reduce unnecessary state updates and re-renders.

⚡ Proposed fix using throttle or simple flag check
  useEffect(() => {
+   let ticking = false;
    const handleScroll = () => {
+     if (!ticking) {
+       window.requestAnimationFrame(() => {
          if (window.scrollY > 20) {
            setIsScrolled(true);
          } else {
            setIsScrolled(false);
          }
+         ticking = false;
+       });
+       ticking = true;
+     }
    };

    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/shared/header.tsx` around lines 14 - 25, The scroll handler in
useEffect (handleScroll -> setIsScrolled) runs on every scroll event causing
frequent state updates; wrap the handler with a throttling strategy (e.g.,
lodash.throttle or a requestAnimationFrame-based throttle) or add a simple
"ticking" flag so updates only occur at most once per animation frame, attach
the throttled handler to window and ensure you cancel/cleanup the throttled
function in the return cleanup to removeEventListener and cancel any pending RAF
or throttled timers; update references to handleScroll in the effect to use the
throttled wrapper so setIsScrolled is called less frequently.

63-68: ⚡ Quick win

Simplify duplicate PushSubscribe rendering.

The same <PushSubscribe isScrolled={isScrolled} /> is rendered twice with opposite responsive visibility classes. This creates unnecessary duplication and increases bundle size slightly.

♻️ Proposed consolidation
-         {/* Always show PushSubscribe on mobile, only on desktop if sm+ */}
-         <span className='flex sm:hidden'>
-           <PushSubscribe isScrolled={isScrolled} />
-         </span>
-         <span className='hidden sm:flex'>
-           <PushSubscribe isScrolled={isScrolled} />
-         </span>
+         <PushSubscribe isScrolled={isScrolled} />

The PushSubscribe component itself can handle responsive visibility if needed, or it can simply be rendered once.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/shared/header.tsx` around lines 63 - 68, Duplicate rendering
of PushSubscribe (with classes 'flex sm:hidden' and 'hidden sm:flex') should be
consolidated into a single render; remove the two spans and render
<PushSubscribe isScrolled={isScrolled} /> once, and if responsive visibility is
required move the conditional CSS into the PushSubscribe component (or wrap it
in one container) so only the PushSubscribe component (referenced by
PushSubscribe and prop isScrolled) is included once to avoid duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/shared/header.tsx`:
- Around line 70-80: The Link to '/rss' in header.tsx uses target='_blank'
without a rel attribute; update the Link element (the Link with href '/rss' and
RssSimple icon) to include rel="noopener noreferrer" to prevent window.opener
access and mitigate security/performance risks when opening the RSS page in a
new tab. Ensure the rel attribute is added alongside the existing target prop on
that Link element.

---

Nitpick comments:
In `@src/components/shared/header.tsx`:
- Around line 14-25: The scroll handler in useEffect (handleScroll ->
setIsScrolled) runs on every scroll event causing frequent state updates; wrap
the handler with a throttling strategy (e.g., lodash.throttle or a
requestAnimationFrame-based throttle) or add a simple "ticking" flag so updates
only occur at most once per animation frame, attach the throttled handler to
window and ensure you cancel/cleanup the throttled function in the return
cleanup to removeEventListener and cancel any pending RAF or throttled timers;
update references to handleScroll in the effect to use the throttled wrapper so
setIsScrolled is called less frequently.
- Around line 63-68: Duplicate rendering of PushSubscribe (with classes 'flex
sm:hidden' and 'hidden sm:flex') should be consolidated into a single render;
remove the two spans and render <PushSubscribe isScrolled={isScrolled} /> once,
and if responsive visibility is required move the conditional CSS into the
PushSubscribe component (or wrap it in one container) so only the PushSubscribe
component (referenced by PushSubscribe and prop isScrolled) is included once to
avoid duplication.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b51d481a-d1bc-4a2a-a9cc-38ee4989c925

📥 Commits

Reviewing files that changed from the base of the PR and between 70e40a7 and 7db8aac.

📒 Files selected for processing (4)
  • src/components/PushSubscribe.tsx
  • src/components/github-button.tsx
  • src/components/shared/header.tsx
  • src/data/events.json

Comment on lines +70 to +80
<Link
href='/rss'
className={`inline-flex items-center rounded-lg px-4 py-2 text-sm transition duration-200 ${
isScrolled
? 'bg-black/5 text-black hover:bg-black/10'
: 'bg-white text-black shadow hover:text-gray-700'
}`}
target='_blank'
>
<RssSimple size={20} />
</Link>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add rel attribute to external link.

The RSS link uses target="_blank" without rel="noopener noreferrer", which creates a security and performance risk. The opened page can access window.opener and potentially redirect the original page.

🔒 Proposed fix
          <Link
            href='/rss'
            className={`inline-flex items-center rounded-lg px-4 py-2 text-sm transition duration-200 ${
              isScrolled
                ? 'bg-black/5 text-black hover:bg-black/10'
                : 'bg-white text-black shadow hover:text-gray-700'
            }`}
            target='_blank'
+           rel='noopener noreferrer'
          >
📝 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.

Suggested change
<Link
href='/rss'
className={`inline-flex items-center rounded-lg px-4 py-2 text-sm transition duration-200 ${
isScrolled
? 'bg-black/5 text-black hover:bg-black/10'
: 'bg-white text-black shadow hover:text-gray-700'
}`}
target='_blank'
>
<RssSimple size={20} />
</Link>
<Link
href='/rss'
className={`inline-flex items-center rounded-lg px-4 py-2 text-sm transition duration-200 ${
isScrolled
? 'bg-black/5 text-black hover:bg-black/10'
: 'bg-white text-black shadow hover:text-gray-700'
}`}
target='_blank'
rel='noopener noreferrer'
>
<RssSimple size={20} />
</Link>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/shared/header.tsx` around lines 70 - 80, The Link to '/rss' in
header.tsx uses target='_blank' without a rel attribute; update the Link element
(the Link with href '/rss' and RssSimple icon) to include rel="noopener
noreferrer" to prevent window.opener access and mitigate security/performance
risks when opening the RSS page in a new tab. Ensure the rel attribute is added
alongside the existing target prop on that Link element.

@iamimmanuelraj

Copy link
Copy Markdown
Member

Can you Please fix the conflicts and we will get this merged

@JustinBenito

Copy link
Copy Markdown
Contributor

Hey @hariharan-km any updates on this buddy ?

@hariharan-km

hariharan-km commented Jun 19, 2026

Copy link
Copy Markdown
Author

Hey @JustinBenito 👋

Thanks for checking in! I've kept this PR focused on the navbar/header improvements and addressed the related feedback.

In the meantime, I also worked on a new feature: Dark Theme support (theme toggle, persistence using localStorage, and support for prefers-color-scheme). Since it's a separate enhancement, I've opened a dedicated PR for it to keep the review process cleaner and easier.

Looking forward to your feedback on this PR. Thanks!

Dark Theme PR: #622

Thanks!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/components/pages/Archive/archive.tsx (1)

369-385: ⚡ Quick win

Extract duplicate Tooltip component to shared module.

This Tooltip component is duplicated identically in src/components/pages/home/events.tsx (lines 323-339). Extracting it to a shared location (e.g., src/components/Tooltip.tsx or src/components/ui/Tooltip.tsx) would eliminate duplication and improve maintainability.

♻️ Suggested refactoring approach
  1. Create src/components/Tooltip.tsx:
import { useState } from 'react';

interface TooltipProps {
  content: string;
  children: React.ReactNode;
}

export function Tooltip({ content, children }: TooltipProps) {
  const [showTooltip, setShowTooltip] = useState(false);

  return (
    <div className='relative inline-block'>
      <div onMouseEnter={() => setShowTooltip(true)} onMouseLeave={() => setShowTooltip(false)}>
        {children}
      </div>
      {showTooltip && (
        <div className='absolute -top-12 left-1/2 z-50 -translate-x-1/2 transform whitespace-nowrap rounded-md border-2 border-gray-800 bg-gray-100 px-2 py-1 text-xs text-gray-800 shadow-lg dark:border-gray-600 dark:bg-[`#2a2a2a`] dark:text-gray-200'>
          {content}
          <div className='absolute -bottom-1 left-1/2 h-2 w-2 -translate-x-1/2 rotate-45 transform bg-gray-100 dark:bg-[`#2a2a2a`]' />
        </div>
      )}
    </div>
  );
}
  1. Replace the local implementations in both files with:
import { Tooltip } from '`@/components/Tooltip`';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/pages/Archive/archive.tsx` around lines 369 - 385, The Tooltip
component is duplicated identically in both archive.tsx and events.tsx files,
creating maintenance issues. Create a new shared component file at
src/components/Tooltip.tsx and move the Tooltip function there with its
TooltipProps interface. Then remove the duplicate Tooltip function definition
from archive.tsx and import it from the new shared location instead. Repeat the
same process for the events.tsx file to eliminate the duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/ThemeProvider.tsx`:
- Around line 20-21: The localStorage.getItem call on line 20 and
localStorage.setItem operations on line 37 are not protected against exceptions
that can be thrown in restricted browser modes or when storage is disabled. Wrap
both the localStorage.getItem call in the effect where the theme is being
retrieved and the localStorage.setItem call where the theme is being persisted
with try-catch blocks to handle potential exceptions gracefully. For the getItem
call, fall back to a default theme value if an exception occurs, and for
setItem, silently fail without breaking the application flow if storage is
unavailable.
- Around line 15-27: The ThemeProvider component initializes the theme state to
'light' in useState, but the pre-hydration script in public/theme-init.js may
have already set the html element's dark class before hydration, causing a
visual mismatch on first paint. Instead of hardcoding 'light' as the initial
state value in the theme useState declaration, initialize it by checking if the
html element currently has the 'dark' class, falling back to 'light' if not.
This ensures the initial React state matches the DOM state that was set during
pre-hydration, eliminating the flash of incorrect theme before useEffect runs.

---

Nitpick comments:
In `@src/components/pages/Archive/archive.tsx`:
- Around line 369-385: The Tooltip component is duplicated identically in both
archive.tsx and events.tsx files, creating maintenance issues. Create a new
shared component file at src/components/Tooltip.tsx and move the Tooltip
function there with its TooltipProps interface. Then remove the duplicate
Tooltip function definition from archive.tsx and import it from the new shared
location instead. Repeat the same process for the events.tsx file to eliminate
the duplication.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f42c34f8-8f99-4213-8ae1-9ed59b715273

📥 Commits

Reviewing files that changed from the base of the PR and between 88a387c and 0dffb23.

📒 Files selected for processing (19)
  • public/theme-init.js
  • src/app/error.tsx
  • src/app/globals.css
  • src/app/layout.tsx
  • src/app/not-found.tsx
  • src/components/PushSubscribe.tsx
  • src/components/ThemeProvider.tsx
  • src/components/ThemeToggle.tsx
  • src/components/github-button.tsx
  • src/components/no-events-card.tsx
  • src/components/pages/Archive/archive.tsx
  • src/components/pages/Communities/Community.tsx
  • src/components/pages/Communities/HoverIcon.tsx
  • src/components/pages/home/calltoaction.tsx
  • src/components/pages/home/events.tsx
  • src/components/pages/home/hero.tsx
  • src/components/shared/footer.tsx
  • src/components/shared/header.tsx
  • tailwind.config.ts
✅ Files skipped from review due to trivial changes (8)
  • public/theme-init.js
  • src/components/pages/home/calltoaction.tsx
  • src/app/not-found.tsx
  • src/app/error.tsx
  • src/components/pages/Communities/HoverIcon.tsx
  • src/app/globals.css
  • tailwind.config.ts
  • src/components/pages/home/hero.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/github-button.tsx
  • src/components/PushSubscribe.tsx
  • src/components/shared/header.tsx

Comment on lines +15 to +27
const [theme, setTheme] = useState<Theme>('light');
const [mounted, setMounted] = useState(false);

useEffect(() => {
// Read theme from localStorage, fallback to system preference
const stored = localStorage.getItem('theme') as Theme | null;
if (stored === 'dark' || stored === 'light') {
setTheme(stored);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setTheme('dark');
}
setMounted(true);
}, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Initialize theme from the pre-hydration DOM class to avoid first-paint mismatch.

public/theme-init.js can set html.dark before hydration, but this provider always starts with 'light' until useEffect runs. That briefly desyncs UI state (icon/labels) from actual page theme.

💡 Suggested fix
-export function ThemeProvider({ children }: { children: React.ReactNode }) {
-  const [theme, setTheme] = useState<Theme>('light');
+export function ThemeProvider({ children }: { children: React.ReactNode }) {
+  const [theme, setTheme] = useState<Theme>(() => {
+    if (typeof document !== 'undefined' && document.documentElement.classList.contains('dark')) {
+      return 'dark';
+    }
+    return 'light';
+  });
   const [mounted, setMounted] = useState(false);
📝 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.

Suggested change
const [theme, setTheme] = useState<Theme>('light');
const [mounted, setMounted] = useState(false);
useEffect(() => {
// Read theme from localStorage, fallback to system preference
const stored = localStorage.getItem('theme') as Theme | null;
if (stored === 'dark' || stored === 'light') {
setTheme(stored);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setTheme('dark');
}
setMounted(true);
}, []);
const [theme, setTheme] = useState<Theme>(() => {
if (typeof document !== 'undefined' && document.documentElement.classList.contains('dark')) {
return 'dark';
}
return 'light';
});
const [mounted, setMounted] = useState(false);
useEffect(() => {
// Read theme from localStorage, fallback to system preference
const stored = localStorage.getItem('theme') as Theme | null;
if (stored === 'dark' || stored === 'light') {
setTheme(stored);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setTheme('dark');
}
setMounted(true);
}, []);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ThemeProvider.tsx` around lines 15 - 27, The ThemeProvider
component initializes the theme state to 'light' in useState, but the
pre-hydration script in public/theme-init.js may have already set the html
element's dark class before hydration, causing a visual mismatch on first paint.
Instead of hardcoding 'light' as the initial state value in the theme useState
declaration, initialize it by checking if the html element currently has the
'dark' class, falling back to 'light' if not. This ensures the initial React
state matches the DOM state that was set during pre-hydration, eliminating the
flash of incorrect theme before useEffect runs.

Comment thread src/components/ThemeProvider.tsx
@JustinBenito

Copy link
Copy Markdown
Contributor

Hey @JustinBenito 👋

Thanks for checking in! I've kept this PR focused on the navbar/header improvements and addressed the related feedback.

In the meantime, I also worked on a new feature: Dark Theme support (theme toggle, persistence using localStorage, and support for prefers-color-scheme). Since it's a separate enhancement, I've opened a dedicated PR for it to keep the review process cleaner and easier.

Looking forward to your feedback on this PR. Thanks!

Dark Theme PR: #622

Thanks!

Hey @hariharan-km 2 things, we would need a screen recording of the dark theme and 2 there are a lot of merge conflicts ( which can be resolved after we are done with the UI confirmation )

But thanks a lot for the update, and the work.
Cheers :)

@hariharan-km

Copy link
Copy Markdown
Author

Hey @JustinBenito 👋

Thanks for the feedback!

I've now resolved the merge conflicts and pushed the latest updates to this PR.

I've also attached a short screen recording demonstrating:

PR.622.mp4

Dark/Light theme switching
Theme persistence after refresh
Responsive/mobile behavior

Please let me know if you'd like any UI refinements or additional improvements. Looking forward to your feedback :)

Thanks!

@JustinBenito

Copy link
Copy Markdown
Contributor

Hey @hariharan-km I definitely like the work you have done in Dark mode so far, there are a lot of tiny minor things we need to work on. I think we are closer than before, so I suggest we get on a meet and get this going.
We are planning for weekly meetings from next week where we discuss our public projects.
Let's get this going there, will update you on the meeting once we finalise things @hariharan-km

Thanks a lot for the contribution.
Cheers mate :)

@hariharan-km

Copy link
Copy Markdown
Author

Thanks, @JustinBenito. Appreciate the review. I'm available for the meeting and can work through the remaining UI refinements. Please let me know the schedule once it's confirmed.

@hariharan-km

Copy link
Copy Markdown
Author

Hey any updates buddy :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants