Skip to content

Add dark mode support with system preference detection#9

Merged
aidmax merged 2 commits intomainfrom
claude/add-dark-mode-support-z9Csc
Apr 1, 2026
Merged

Add dark mode support with system preference detection#9
aidmax merged 2 commits intomainfrom
claude/add-dark-mode-support-z9Csc

Conversation

@aidmax
Copy link
Copy Markdown
Owner

@aidmax aidmax commented Apr 1, 2026

  • Add useTheme hook with localStorage persistence and OS preference fallback
  • Add Sun/Moon toggle button to the app header
  • Add FOMT prevention inline script to index.html
  • Fix Calendar icon brightness in dark mode (blue-600 → blue-400)
  • Fix missing dark variant on date section wrapper

https://claude.ai/code/session_01MRpbiF23XV7QXSSH73zoZa

- Add useTheme hook with localStorage persistence and OS preference fallback
- Add Sun/Moon toggle button to the app header
- Add FOMT prevention inline script to index.html
- Fix Calendar icon brightness in dark mode (blue-600 → blue-400)
- Fix missing dark variant on date section wrapper

https://claude.ai/code/session_01MRpbiF23XV7QXSSH73zoZa
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 1, 2026

Greptile Summary

This PR adds dark mode support to PedalNotes with system preference detection, localStorage persistence, and FOUC prevention. The implementation is well-structured: a render-blocking inline script in <head> applies the dark class before the first paint, and a new useTheme hook keeps React state, the DOM, and localStorage in sync. The previously flagged missing try/catch around browser APIs has been fully addressed — both the lazy initialiser and the localStorage.setItem call inside useEffect are now properly guarded.

Key changes:

  • client/index.html — adds FOUC-prevention IIFE with try/catch before React mounts
  • client/src/hooks/use-theme.ts — new hook with error-safe localStorage read/write and OS-preference fallback
  • client/src/pages/home.tsx — Sun/Moon toggle button in header; fixes dark:bg-gray-700 on the date section and dark:text-blue-400 on the Calendar icon
  • One minor style note: the toggle button uses a raw <button> element instead of the existing shadcn/ui Button (variant="ghost" size="icon") as recommended by AGENTS.md §10

Confidence Score: 5/5

Safe to merge — all findings are non-blocking style suggestions.

The prior P1 concern (missing try/catch around browser APIs) has been fully addressed. The only remaining finding is a P2 style suggestion (raw button vs. shadcn/ui Button), which does not affect correctness or reliability.

No files require special attention.

Important Files Changed

Filename Overview
client/index.html Adds a render-blocking inline FOUC-prevention script that correctly reads localStorage and prefers-color-scheme, with a try/catch guard, before React mounts.
client/src/hooks/use-theme.ts New hook that initialises theme from localStorage/OS preference (with try/catch in both the lazy initializer and the persistence effect), exposes a toggle, and syncs the dark class on <html>.
client/src/pages/home.tsx Wires up useTheme, adds Sun/Moon toggle button in header (using raw button rather than the shadcn/ui Button), and fixes missing dark-mode classes on the date section and Calendar icon.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant InlineScript as "index.html script"
    participant useTheme as "useTheme hook"
    participant DOM as "document.documentElement"
    participant LS as "localStorage"

    Browser->>InlineScript: "Parse head (blocking)"
    InlineScript->>LS: "getItem(theme)"
    LS-->>InlineScript: "stored value / null"
    alt "stored === dark"
        InlineScript->>DOM: "classList.add(dark)"
    else "no stored + prefers-color-scheme: dark"
        InlineScript->>DOM: "classList.add(dark)"
    end

    Browser->>useTheme: "React mounts, lazy initialiser runs"
    useTheme->>LS: "getItem(theme)"
    LS-->>useTheme: "stored value"
    useTheme->>DOM: "classList.add/remove(dark)"
    useTheme->>LS: "setItem(theme, value)"

    note over useTheme,DOM: "On toggle click"
    useTheme->>useTheme: "setTheme toggle"
    useTheme->>DOM: "classList.add/remove(dark)"
    useTheme->>LS: "setItem(theme, newTheme)"
Loading

Reviews (2): Last reviewed commit: "Wrap localStorage calls in try/catch in ..." | Re-trigger Greptile

Comment thread client/src/hooks/use-theme.ts
Prevents unhandled render errors in environments where localStorage
is restricted (Safari private mode, sandboxed iframes, storage-blocked
extensions).

https://claude.ai/code/session_01MRpbiF23XV7QXSSH73zoZa
@aidmax
Copy link
Copy Markdown
Owner Author

aidmax commented Apr 1, 2026

Done. The review was spot-on — the index.html FOMT script already had the try/catch, but the hook didn't. Fixed both spots:
useState initializer — wrapped in try/catch, falls back to "light" if localStorage or matchMedia throws
useEffect — localStorage.setItem wrapped in try/catch so a full storage quota or access restriction doesn't crash the component

@greptile-apps

@aidmax aidmax merged commit 0e6c950 into main Apr 1, 2026
2 checks passed
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.

2 participants