Light/Dark Mode Switch
Relevant source files
The following files were used as context for generating this wiki page:The Light/Dark Mode Switch is a user-facing feature that allows for dynamic theme changes across the application. It provides three distinct modes: light, dark, and auto (which respects the user's operating system preference). The selected theme is persisted in the browser's localStorage to ensure a consistent experience across sessions.
This system is implemented using a Svelte component for the UI, utility functions for state management and DOM manipulation, and a global configuration file for setting the default theme. The styling is achieved by toggling a .dark class on the root <html> element, which is then used by CSS rules to apply the appropriate theme colors.
Core Component: LightDarkSwitch.svelte
The primary user interface for theme selection is the LightDarkSwitch.svelte component. It consists of a main button that cycles through the available themes and displays an icon corresponding to the current mode. On desktop, hovering over this button reveals a panel with explicit choices for Light, Dark, and Auto modes.
Sources: src/components/LightDarkSwitch.svelte
Component Logic
- State Management: The component maintains the current theme mode in a reactive
$statevariable namedmode. - Theme Sequence: An array
seqdefines the order of themes when toggling:['light', 'dark', 'auto']. - Initialization: On component mount (
onMount), it retrieves the stored theme usinggetStoredTheme()and sets up an event listener for changes in the system's color scheme (prefers-color-scheme: dark). This ensures the UI updates correctly when inautomode and the system theme changes. - User Interaction:
toggleScheme(): Cycles to the next theme in theseqarray.switchScheme(newMode): Sets the theme to a specific mode selected from the hover panel.showPanel()/hidePanel(): Control the visibility of the selection panel on mouse enter/leave.
The following diagram illustrates the component's initialization and event handling flow.
Diagram showing the initialization logic within the LightDarkSwitch.svelte component.
Sources: src/components/LightDarkSwitch.svelte:15-28
Theme Management Logic: setting-utils.ts
This utility file is the engine of the theme system, handling the persistence, retrieval, and application of the theme. It decouples the theme logic from the UI component.
Sources: src/utils/setting-utils.ts
Key Functions
| Function | Description |
|---|---|
getStoredTheme() | Retrieves the theme from localStorage. If not found, it falls back to the data-default-theme attribute on the #config-carrier element, and finally to the DEFAULT_THEME constant. |
setTheme(theme) | Persists the selected theme to localStorage by setting the "theme" key and then calls applyThemeToDocument() to update the UI. |
applyThemeToDocument(theme) | The core function that manipulates the DOM. It adds or removes the dark class from document.documentElement based on the selected theme and the result of the window.matchMedia query for auto mode. |
The sequence diagram below shows the process of a user changing the theme.
Sequence diagram illustrating the flow of changing the theme from user interaction to DOM update.
Sources: src/components/LightDarkSwitch.svelte:30-41, src/utils/setting-utils.ts:28-52
Theme Application Flow
The applyThemeToDocument function contains the logic for how each theme mode is translated into a DOM state.
Flowchart of the logic inside the applyThemeToDocument function.
Sources: src/utils/setting-utils.ts:28-44
Configuration and Constants
The theme system relies on a set of predefined constants and a global configuration object to ensure consistency and allow for easy customization.
Theme Constants (src/constants/constants.ts)
To avoid magic strings, theme modes are defined as constants.
| Constant | Value | Description |
|---|---|---|
LIGHT_MODE | "light" | Represents the light theme. |
DARK_MODE | "dark" | Represents the dark theme. |
AUTO_MODE | "auto" | Represents the system-preference theme. |
DEFAULT_THEME | AUTO_MODE | The ultimate fallback theme if no other is specified. |
Sources: src/constants/constants.ts:3-6
Type Definitions (src/types/config.ts)
A specific TypeScript type, LIGHT_DARK_MODE, is defined to enforce type safety when working with theme values.
export type LIGHT_DARK_MODE =
| typeof LIGHT_MODE
| typeof DARK_MODE
| typeof AUTO_MODE;
Sources: src/types/config.ts:80-83
Site-wide Configuration (src/config.ts)
The default theme for the entire site is set in the siteConfig object. This value is used for first-time visitors before they have made a selection.
// src/config.ts:22
export const siteConfig: SiteConfig = {
// ...
defaultTheme: "dark", // 默认主题模式:'light' | 'dark' | 'auto'
// ...
};
This configuration sets the default theme to dark.
Sources: src/config.ts:22, src/types/config.ts:15
Styling and CSS
The theme is visually applied using CSS. The presence of the .dark class on the <html> element triggers dark-mode-specific styles. The application heavily uses Tailwind CSS's dark mode variant (dark:).
For example, in the DisplaySettings.svelte component, the text color of a heading changes based on the theme:
<!-- src/components/widget/DisplaySettings.svelte:20 -->
<div class="flex flex-row gap-2 mb-3 items-center justify-between">
<div class="flex gap-2 font-bold text-lg text-neutral-900 dark:text-neutral-100 transition relative ml-3
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
before:absolute before:-left-3 before:top-[0.33rem]"
>
{i18n(I18nKey.themeColor)}
<!-- ... -->
</div>
<!-- ... -->
</div>
Here, text-neutral-900 is applied by default (light mode), and dark:text-neutral-100 is applied when the .dark class is present on a parent element (in this case, <html>).
Sources: src/components/widget/DisplaySettings.svelte:21