跳到主要内容

深色模式怎么跟系统走?nativeTheme 和 prefers-color-scheme 是什么关系?

考察点​

  • 主题源在主进程
  • 页面只消费媒体查询,不自己猜系统主题

参考答案​

1. 关系​

nativeTheme.themeSource 有三个值:system、light、dark。

它一变,所有渲染进程里的 CSS prefers-color-scheme 跟着变。页面不要用 Node 去读系统设置,只写媒体查询:

:root { color-scheme: light; background: #f6f6f6; color: #1a1a1a; }
@media (prefers-color-scheme: dark) {
:root { color-scheme: dark; background: #1e1e1e; color: #f3f3f3; }
}

窗口的 backgroundColor 也要和主题一致,否则 ready-to-show 之前会闪一块白。

2. 让用户切换​

// 主进程
ipcMain.handle('theme:set', (_event, source) => {
// source 只允许这三个值
if (!['system', 'light', 'dark'].includes(source)) return;
nativeTheme.themeSource = source;
store.set('themeSource', source);
});

nativeTheme.on('updated', () => {
const dark = nativeTheme.shouldUseDarkColors;
for (const win of BrowserWindow.getAllWindows()) {
win.setBackgroundColor(dark ? '#1e1e1e' : '#f6f6f6');
win.webContents.send('theme:changed', dark);
}
});

启动时在 ready 之后、创建窗口之前,把用户上次选的 themeSource 设回去。选 system 时,用户改系统外观会触发 updated,不用轮询。

3. 托盘和标题栏​

macOS 托盘图标用 template image,系统会按菜单栏深浅上色。Windows 标题栏可以 win.setTitleBarOverlay 配深色背景。这些不跟随 CSS,要在 updated 里单独设。

4. 不要只在一个窗口里改 class​

如果只给某个窗口的 documentElement 加 class="dark",新开的窗口和系统切换都会漏。以 nativeTheme 为唯一来源,CSS 媒体查询才能所有窗口一致。

面试话术​

主题开关放在主进程的 nativeTheme.themeSource。页面只用 prefers-color-scheme,这样所有窗口一起变。用户选择持久化,启动时在建窗口前设回去。系统外观变化听 updated,同时改 backgroundColor 和托盘,避免白屏闪一下和图标颜色不对。

常见追问​

  • 渲染进程能读 shouldUseDarkColors 吗? 不要直接读。需要初始值就 invoke 问主进程,之后等 theme:changed。
  • 第三方页面不跟主题? 远程页面有自己的配色,不要强行注入。只保证自己的壳和背景色不闪。