Dark Mode

概览

自动更新原生界面

“Native interfaces” include the file picker, window border, dialogs, context menus, and more - anything where the UI comes from your operating system and not from your app. The default behavior is to opt into this automatic theming from the OS.

Automatically update your own interfaces

If your app has its own dark mode, you should toggle it on and off in sync with the system’s dark mode setting. You can do this by using the prefer-color-scheme CSS media query.

Manually update your own interfaces

If you want to manually switch between light/dark modes, you can do this by setting the desired mode in the themeSource property of the nativeTheme module. This property’s value will be propagated to your Renderer process. Any CSS rules related to prefers-color-scheme will be updated accordingly.

macOS settings

在 macOS 10.14 Mojave中, Apple 为所有 macOS 电脑引入了一个全新的 系统级黑暗模式。 如果您的 Electron 应用具有深色模式,您可以 使用”本机 api” 应用 应用

In macOS 10.15 Catalina, Apple introduced a new “automatic” dark mode option for all macOS computers. 为了让API isDarkModeTray 在这个模式中正常工作,你需要在 Info.plist 文件里把 NSRequiresAquaSystemAppearance 设置为 false ,或者使用 >=7.0.0 的Electron。 Both Electron Packager and Electron Forge have a darwinDarkModeSupport option to automate the Info.plist changes during app build time.

If you wish to opt-out while using Electron > 8.0.0, you must set the NSRequiresAquaSystemAppearance key in the Info.plist file to true. Please note that Electron 8.0.0 and above will not let you opt-out of this theming, due to the use of the macOS 10.14 SDK.

示例

We’ll start with a working application from the Quick Start Guide and add functionality gradually.

首先,让我们编辑我们的接口,以便用户可以在光线和暗色的 模式之间切换。 这个基本的界面包含更改 原生主题源 设置的按钮,并且包含一个文本元素,指明了哪些 主题源 值被选中。 By default, Electron follows the system’s dark mode preference, so we will hardcode the theme source as “System”.

Add the following lines to the index.html file:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Hello World!</title>
  6. <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
  7. <link rel="stylesheet" type="text/css" href="./styles.css">
  8. </head>
  9. <body>
  10. <h1>Hello World!</h1>
  11. <p>Current theme source: <strong id="theme-source">System</strong></p>
  12. <button id="toggle-dark-mode">Toggle Dark Mode</button>
  13. <button id="reset-to-system">Reset to System Theme</button>
  14. <script src="renderer.js"></script>
  15. </body>
  16. </body>
  17. </html>

Next, add event listeners that listen for click events on the toggle buttons. Because the nativeTheme module only exposed in the Main process, you need to set up each listener’s callback to use IPC to send messages to and handle responses from the Main process:

  • when the “Toggle Dark Mode” button is clicked, we send the dark-mode:toggle message (event) to tell the Main process to trigger a themek change, and update the “Current Theme Source” label in the UI based on the response from the Main process.
  • 单击”重置系统主题”按钮时,我们会发送 暗模式:系统 消息(事件),告诉主进程使用系统 配色方案,并更新”当前主题源”标签到 系统

To add listeners and handlers, add the following lines to the renderer.js file:

  1. const { ipcRenderer } = 需要 '电子'
  2. 文档. getelementbyid '切换- 暗模式'). 添加事件列表器 '点击' 异步 () => =
  3. const 是黑暗模式 = 等待 ipcrenderer. 调用 '黑暗模式:切换'
  4. 文档. getelementbyid '主题源' 'Dark' : 'Light'
  5. })
  6. document.getElementById('reset-to-system').addEventListener('click', async () => {
  7. await ipcRenderer.invoke('dark-mode:system')
  8. document.getElementById('theme-source').innerHTML = 'System'
  9. })

If you run your code at this point, you’ll see that your buttons don’t do anything just yet, and your Main process will output an error like this when you click on your buttons: Error occurred in handler for 'dark-mode:toggle': No handler registered for 'dark-mode:toggle' This is expected — we haven’t actually touched any nativeTheme code yet.

Now that we’re done wiring the IPC from the Renderer’s side, the next step is to update the main.js file to handle events from the Renderer process.

Depending on the received event, we update the nativeTheme.themeSource property to apply the desired theme on the system’s native UI elements (e.g. context menus) and propagate the preferred color scheme to the Renderer process:

  • Upon receiving dark-mode:toggle, we check if the dark theme is currently active using the nativeTheme.shouldUseDarkColors property, and set the themeSource to the opposite theme.
  • Upon receiving dark-mode:system, we reset the themeSource to system.
  1. const { app, BrowserWindow, ipcMain, nativeTheme } = require('electron')
  2. function createWindow () {
  3. const win = new BrowserWindow({
  4. width: 800,
  5. height: 600,
  6. webPreferences: {
  7. nodeIntegration: true
  8. }
  9. })
  10. win.loadFile('index.html')
  11. ipcMain.handle('dark-mode:toggle', () => {
  12. if (nativeTheme.shouldUseDarkColors) {
  13. nativeTheme.themeSource = 'light'
  14. } else {
  15. nativeTheme.themeSource = 'dark'
  16. }
  17. return nativeTheme.shouldUseDarkColors
  18. })
  19. ipcMain.handle('dark-mode:system', () => {
  20. nativeTheme.themeSouce = 'system'
  21. })
  22. }
  23. app.whenReady().then(createWindow)
  24. app.on('window-all-closed', () => {
  25. if (process.platform !== 'darwin') {
  26. app.quit()
  27. }
  28. })
  29. app.on('activate', () => {
  30. if (BrowserWindow.getAllWindows().length === 0) {
  31. createWindow()
  32. }
  33. })

The final step is to add a bit of styling to enable dark mode for the web parts of the UI by leveraging the prefers-color-scheme CSS attribute. The value of prefers-color-scheme will follow your nativeTheme.themeSource setting.

Create a styles.css file and add the following lines:

  1. @media (prefers-color-scheme: dark) {
  2. body { background: #333; color: white; }
  3. }
  4. @media (prefers-color-scheme: light) {
  5. body { background: #ddd; color: black; }
  6. }

After launching the Electron application, you can change modes or reset the theme to system default by clicking corresponding buttons:

Dark Mode