From 3e82def76cc21272b7beed5b5b8b7011c21a1322 Mon Sep 17 00:00:00 2001 From: Richard Roberson Date: Sun, 19 Jan 2025 03:44:04 -0700 Subject: [PATCH] Add theme switching + fixes --- public/theme.js | 30 ++++++ src/app/globals.css | 30 +++--- src/app/layout.tsx | 33 +++---- src/app/page.tsx | 35 ++++++- src/app/providers.tsx | 13 ++- src/components/DocumentList.tsx | 153 ++++++++++++++++++++++++------- src/components/PDFViewer.tsx | 2 +- src/components/SettingsModal.tsx | 147 +++++++++++++++++++++++++++++ src/components/TTSPlayer.tsx | 21 ++++- src/context/TTSContext.tsx | 52 +++++++++-- src/context/ThemeContext.tsx | 75 +++++++++++++++ src/lib/theme.ts | 16 ++++ tailwind.config.ts | 26 ++++++ 13 files changed, 553 insertions(+), 80 deletions(-) create mode 100644 public/theme.js create mode 100644 src/components/SettingsModal.tsx create mode 100644 src/context/ThemeContext.tsx create mode 100644 src/lib/theme.ts diff --git a/public/theme.js b/public/theme.js new file mode 100644 index 0000000..9d1c19e --- /dev/null +++ b/public/theme.js @@ -0,0 +1,30 @@ +// Immediately apply the theme before any content loads +(function() { + try { + let theme = localStorage.getItem('theme') || 'system'; + const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches; + + // If theme is system, use the system preference + if (theme === 'system') { + theme = systemTheme ? 'dark' : 'light'; + } + + // Remove both classes and add the current one + document.documentElement.classList.remove('light', 'dark'); + document.documentElement.classList.add(theme); + document.documentElement.style.colorScheme = theme; + + // Watch for system theme changes + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { + if (localStorage.getItem('theme') === 'system') { + document.documentElement.classList.remove('light', 'dark'); + document.documentElement.classList.add(e.matches ? 'dark' : 'light'); + document.documentElement.style.colorScheme = e.matches ? 'dark' : 'light'; + } + }); + } catch (e) { + // Fallback to light theme if localStorage is not available + document.documentElement.classList.add('light'); + document.documentElement.style.colorScheme = 'light'; + } +})(); diff --git a/src/app/globals.css b/src/app/globals.css index 697735e..08d34f2 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,7 +2,8 @@ @tailwind components; @tailwind utilities; -:root { +:root, +html.light { --background: #ffffff; --foreground: #171717; --base: #f5f5f5; @@ -11,19 +12,26 @@ --muted: #737373; } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - --base: #333333; - --offbase: #4a4a4a; - --accent: #f87171; - --muted: #a3a3a3; - } +html.dark { + --background: #111111; + --foreground: #ededed; + --base: #1f1f1f; + --offbase: #4a4a4a; + --accent: #f87171; + --muted: #a3a3a3; +} + +/* Ensure background color is set before content loads */ +html { + background-color: var(--background); } body { color: var(--foreground); background: var(--background); - font-family: Arial, Helvetica, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d200d6c..1d65e58 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,22 +1,11 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { Providers } from "./providers"; import TTSPlayer from "@/components/TTSPlayer"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); +import { Metadata } from "next"; export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "OpenReader WebUI", + description: "A modern web interface for reading and managing documents", }; export default function RootLayout({ @@ -25,10 +14,18 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - - + + + +