深色模式
概觀
自動更新原生介面
「原生介面」包括檔案選擇器、視窗邊框、對話方塊、上下文選單等等 - 任何 UI 來自您的作業系統而不是您的應用程式。預設行為是選擇加入來自作業系統的自動主題設定。
自動更新您自己的介面
如果您的應用程式有自己的深色模式,您應該與系統的深色模式設定同步開啟和關閉它。您可以使用 prefers-color-scheme CSS 媒體查詢來做到這一點。
手動更新您自己的介面
如果您想手動在淺色/深色模式之間切換,您可以透過在 nativeTheme
模組的 themeSource 屬性中設定所需的模式來做到這一點。此屬性的值將會傳播到您的 Renderer 處理程序。任何與 prefers-color-scheme
相關的 CSS 規則都將相應地更新。
macOS 設定
在 macOS 10.14 Mojave 中,Apple 為所有 macOS 電腦引入了新的全系統深色模式。如果您的 Electron 應用程式具有深色模式,您可以使用 nativeTheme
API 使其遵循全系統深色模式設定。
在 macOS 10.15 Catalina 中,Apple 為所有 macOS 電腦引入了新的「自動」深色模式選項。為了使 nativeTheme.shouldUseDarkColors
和 Tray
API 在 Catalina 上的此模式下正常運作,您需要使用 Electron >=7.0.0
,或在舊版本的 Info.plist
檔案中將 NSRequiresAquaSystemAppearance
設定為 false
。Electron Packager 和 Electron Forge 都有 darwinDarkModeSupport
選項,可在應用程式建置期間自動執行 Info.plist
變更。
如果您希望在使用 Electron > 8.0.0 時選擇退出,您必須在 Info.plist
檔案中將 NSRequiresAquaSystemAppearance
鍵設定為 true
。請注意,由於使用了 macOS 10.14 SDK,Electron 8.0.0 及更高版本將不允許您選擇退出此主題設定。
範例
此範例示範了一個 Electron 應用程式,該應用程式從 nativeTheme
衍生其主題顏色。此外,它還使用 IPC 通道提供主題切換和重置控制項。
- main.js
- preload.js
- index.html
- renderer.js
- styles.css
const { app, BrowserWindow, ipcMain, nativeTheme } = require('electron/main')
const path = require('node:path')
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('index.html')
}
ipcMain.handle('dark-mode:toggle', () => {
if (nativeTheme.shouldUseDarkColors) {
nativeTheme.themeSource = 'light'
} else {
nativeTheme.themeSource = 'dark'
}
return nativeTheme.shouldUseDarkColors
})
ipcMain.handle('dark-mode:system', () => {
nativeTheme.themeSource = 'system'
})
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
const { contextBridge, ipcRenderer } = require('electron/renderer')
contextBridge.exposeInMainWorld('darkMode', {
toggle: () => ipcRenderer.invoke('dark-mode:toggle'),
system: () => ipcRenderer.invoke('dark-mode:system')
})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<link rel="stylesheet" type="text/css" href="./styles.css">
</head>
<body>
<h1>Hello World!</h1>
<p>Current theme source: <strong id="theme-source">System</strong></p>
<button id="toggle-dark-mode">Toggle Dark Mode</button>
<button id="reset-to-system">Reset to System Theme</button>
<script src="renderer.js"></script>
</body>
</html>
document.getElementById('toggle-dark-mode').addEventListener('click', async () => {
const isDarkMode = await window.darkMode.toggle()
document.getElementById('theme-source').innerHTML = isDarkMode ? 'Dark' : 'Light'
})
document.getElementById('reset-to-system').addEventListener('click', async () => {
await window.darkMode.system()
document.getElementById('theme-source').innerHTML = 'System'
})
:root {
color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
body { background: #333; color: white; }
}
@media (prefers-color-scheme: light) {
body { background: #ddd; color: black; }
}
這是如何運作的?
從 index.html
檔案開始
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<link rel="stylesheet" type="text/css" href="./styles.css">
</head>
<body>
<h1>Hello World!</h1>
<p>Current theme source: <strong id="theme-source">System</strong></p>
<button id="toggle-dark-mode">Toggle Dark Mode</button>
<button id="reset-to-system">Reset to System Theme</button>
<script src="renderer.js"></script>
</body>
</html>
以及 styles.css
檔案
@media (prefers-color-scheme: dark) {
body { background: #333; color: white; }
}
@media (prefers-color-scheme: light) {
body { background: #ddd; color: black; }
}
此範例呈現一個包含幾個元素的 HTML 頁面。<strong id="theme-source">
元素顯示目前選取的主題,而兩個 <button>
元素是控制項。CSS 檔案使用 prefers-color-scheme
媒體查詢來設定 <body>
元素的背景和文字顏色。
preload.js
腳本將一個新的 API 新增到名為 darkMode
的 window
物件。此 API 向渲染器處理程序公開兩個 IPC 通道,'dark-mode:toggle'
和 'dark-mode:system'
。它還分配了兩種方法,toggle
和 system
,它們將訊息從渲染器傳遞到主處理程序。
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('darkMode', {
toggle: () => ipcRenderer.invoke('dark-mode:toggle'),
system: () => ipcRenderer.invoke('dark-mode:system')
})
現在,渲染器處理程序可以安全地與主處理程序通訊,並對 nativeTheme
物件執行必要的變更。
renderer.js
檔案負責控制 <button>
功能。
document.getElementById('toggle-dark-mode').addEventListener('click', async () => {
const isDarkMode = await window.darkMode.toggle()
document.getElementById('theme-source').innerHTML = isDarkMode ? 'Dark' : 'Light'
})
document.getElementById('reset-to-system').addEventListener('click', async () => {
await window.darkMode.system()
document.getElementById('theme-source').innerHTML = 'System'
})
使用 addEventListener
,renderer.js
檔案將 'click'
事件監聽器新增到每個按鈕元素。每個事件監聽器處理程序都會呼叫各自的 window.darkMode
API 方法。
最後,main.js
檔案代表主處理程序,並包含實際的 nativeTheme
API。
const { app, BrowserWindow, ipcMain, nativeTheme } = require('electron')
const path = require('node:path')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('index.html')
ipcMain.handle('dark-mode:toggle', () => {
if (nativeTheme.shouldUseDarkColors) {
nativeTheme.themeSource = 'light'
} else {
nativeTheme.themeSource = 'dark'
}
return nativeTheme.shouldUseDarkColors
})
ipcMain.handle('dark-mode:system', () => {
nativeTheme.themeSource = 'system'
})
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
ipcMain.handle
方法是主處理程序如何回應 HTML 頁面上按鈕的點擊事件。
'dark-mode:toggle'
IPC 通道處理程序方法檢查 shouldUseDarkColors
布林屬性,設定對應的 themeSource
,然後傳回目前的 shouldUseDarkColors
屬性。回顧此 IPC 通道的渲染器處理程序事件監聽器,此處理程序的傳回值用於將正確的文字指派給 <strong id='theme-source'>
元素。
'dark-mode:system'
IPC 通道處理程序方法將字串 'system'
指派給 themeSource
,並且不傳回任何內容。這也與相對應的渲染器處理程序事件監聽器一致,因為該方法在等待時沒有預期的傳回值。
使用 Electron Fiddle 執行此範例,然後點擊「切換深色模式」按鈕;應用程式應該開始在淺色和深色背景顏色之間交替。