Files
Synculous-2/synculous-client/darkmode.md
2026-02-15 23:11:33 -06:00

3.9 KiB

Dark Mode Implementation Plan

Context

The Synculous client already has a bare @media (prefers-color-scheme: dark) in globals.css that only flips body background/foreground. Every component uses hardcoded Tailwind classes (bg-white, bg-gray-50, text-gray-900, etc.) with no dark: variants, so manually switching modes via a button has no effect. The goal is a user-controlled toggle that persists preference, respects the OS default, and applies comprehensive dark styles across all pages.


Steps

1. Configure Tailwind v4 for class-based dark mode

File: src/app/globals.css

Add the Tailwind v4 CSS directive that maps dark: utilities to an ancestor .dark class:

@variant dark (&:where(.dark, .dark *));

Replace the @media (prefers-color-scheme: dark) block with a .dark selector:

.dark {
  --background: #0a0a0a;
  --foreground: #ededed;
}

2. Create ThemeProvider

New file: src/components/theme/ThemeProvider.tsx

A minimal React context provider:

  • State: isDark: boolean
  • On mount: reads localStorage.getItem('theme'), falls back to window.matchMedia('(prefers-color-scheme: dark)').matches
  • Applies/removes dark class on document.documentElement
  • toggleDark() function that flips state and saves to localStorage
  • Exports useTheme() hook

3. Anti-flash script + wrap in layout

File: src/app/layout.tsx

  • Add ThemeProvider wrapping AuthProvider
  • Add inline <script> before <body> content to set .dark class before hydration (prevents white flash)

4. Dark mode toggle button in dashboard header

File: src/app/dashboard/layout.tsx

  • Import useTheme from the ThemeProvider
  • Add a sun/moon icon button in the existing right-side icon row (between Settings and Logout)
  • SunIcon and MoonIcon already exist in src/components/ui/Icons.tsx — no new icons needed

5. Add dark: variants to all components

Color mapping:

Light class Dark variant
bg-white dark:bg-gray-800
bg-gray-50 dark:bg-gray-900
bg-gray-100 dark:bg-gray-700
bg-gray-200 dark:bg-gray-600
text-gray-900 dark:text-gray-100
text-gray-700 dark:text-gray-300
text-gray-600 dark:text-gray-400
text-gray-500 dark:text-gray-400
border-gray-200 dark:border-gray-700
border-gray-100 dark:border-gray-800
border-gray-300 dark:border-gray-600
bg-indigo-50 dark:bg-indigo-900/30
bg-green-50 dark:bg-green-900/30
bg-amber-50 dark:bg-amber-900/30
bg-blue-50 dark:bg-blue-900/30
bg-red-50 dark:bg-red-900/30

Files to update (in order):

  1. src/app/dashboard/layout.tsx — shell, header, nav
  2. src/app/dashboard/page.tsx — Today view
  3. src/app/dashboard/routines/page.tsx
  4. src/app/dashboard/routines/new/page.tsx
  5. src/app/dashboard/routines/[id]/page.tsx
  6. src/app/dashboard/routines/[id]/launch/page.tsx
  7. src/app/dashboard/routines/[id]/run/page.tsx
  8. src/app/dashboard/templates/page.tsx
  9. src/app/dashboard/history/page.tsx
  10. src/app/dashboard/stats/page.tsx
  11. src/app/dashboard/medications/page.tsx
  12. src/app/dashboard/medications/new/page.tsx
  13. src/app/dashboard/settings/page.tsx
  14. src/app/login/page.tsx
  15. src/components/session/VisualTimeline.tsx
  16. src/components/notifications/PushNotificationToggle.tsx

Files to create

  • src/components/theme/ThemeProvider.tsx (new)

Files to modify

  • src/app/globals.css
  • src/app/layout.tsx
  • All pages listed in step 5

Verification

  1. Run npm run dev in synculous-client/
  2. Navigate to /dashboard — should default to OS preference
  3. Click the sun/moon toggle in the header — UI should switch immediately
  4. Refresh the page — theme should persist (no flash)
  5. Switch OS theme — manual override takes priority; no override follows OS