Tome offers a flexible theme system with three options:
- Light Mode: Always use light theme regardless of system preference
- Dark Mode: Always use dark theme regardless of system preference
- Auto Mode (Default): Automatically match your system's theme preference and respond to changes in real-time
- Background: Warm beige (#F0EAE3)
- Foreground: Dark brown (#3a3a3a)
- Card Background: Soft cream (#f5f1e8)
- Accent: Earthy brown (#8b6f47)
- Background: Deep charcoal (#1f1c19)
- Foreground: Warm beige (#e8dcc4)
- Card Background: Dark brown (#3d3935)
- Accent: Golden brown (#a89968)
Auto mode continuously monitors your operating system's theme preference and updates the app's theme in real-time:
- If your OS is set to dark mode, Tome will use dark theme
- If your OS is set to light mode, Tome will use light theme
- When your OS switches themes (e.g., at sunset), Tome automatically updates
This provides a seamless experience that matches your system-wide theme preferences.
- Navigate to Settings from the sidebar or bottom navigation
- In the Theme section, select your preferred option:
- Light: Always light mode
- Dark: Always dark mode
- Auto: Match system preferences (recommended)
- When Auto is selected, you'll see which theme is currently active
Your preference is saved and will persist across sessions.
The theme is controlled exclusively through the Settings page (app/settings/page.tsx) using the ThemeSettings component. This provides a clear, explicit interface with radio buttons for the three theme options.
The theme system is managed by the useTheme hook which:
- Three-state preference: Stores user preference as "light" | "dark" | "auto"
- Computed effective theme: Determines actual theme to display based on preference and system
- Real-time listener: Uses
matchMediato detect system theme changes when in auto mode - localStorage persistence: Saves preference under key "themePreference"
- Migration: Automatically migrates from old boolean "darkMode" format
export type ThemePreference = "light" | "dark" | "auto";
export type Theme = "light" | "dark";
const {
preference, // User's selected preference
effectiveTheme, // Actual theme being displayed
setThemePreference
} = useTheme();Radio button UI that allows users to:
- Select between Light, Dark, and Auto modes
- See descriptions of each option
- View current effective theme when in Auto mode
The component uses custom-styled radio buttons with the app's accent color for a cohesive design.
An inline script runs before page load to:
- Check for saved theme preference in localStorage
- Migrate old boolean format if present
- Determine effective theme (handles "auto" by checking system preference)
- Apply theme attributes to prevent flash of incorrect theme
- Default to "auto" for new users
// Prevents flash of wrong theme on initial load
const savedPreference = localStorage.getItem("themePreference");
let preference = "auto"; // Default for new users
if (preference === "auto") {
theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}Themes are applied using CSS custom properties (variables) that change based on the data-theme attribute:
html[data-theme="dark"] {
--background: #1f1c19;
--foreground: #e8dcc4;
/* ... other variables */
}
html[data-theme="light"] {
--background: #F0EAE3;
--foreground: #3a3a3a;
/* ... other variables */
}All components use these CSS variables, so theme changes are instant and consistent.
Users upgrading from the old toggle-based theme system:
- Old
darkMode: "true"→ migrates tothemePreference: "dark" - Old
darkMode: "false"→ migrates tothemePreference: "light" - Old localStorage key is automatically removed after migration
- New users default to
themePreference: "auto"
Migration happens automatically in both the initial load script and the React hook.
Works on all modern browsers that support:
- CSS custom properties (variables)
- localStorage API
- matchMedia API for system preference detection
The implementation gracefully handles:
- Browsers without matchMedia (falls back to user's explicit preference or default)
- Disabled localStorage (theme works but doesn't persist)
- Server-side rendering (inline script ensures correct theme before hydration)
New users: Default to Auto mode, matching system preference Existing users: Preference is preserved through migration No preference + no system preference: Falls back to light mode