博客现已支持手动切换深浅色模式
从现在开始,博客已经支持手动切换深浅色模式了。仅需点击页面左下角的最后一个按钮(移动端可拉到最下方点「🔁 切换主题」),或是试试点击这几个链接:深色模式,浅色模式,跟随系统
本来这里打了一长串字想解释来龙去脉的,直到后来我发现 hexo-theme-stellar 已有了一个比较粗暴的实现。所以这次仅提供修改时的流水账,供其他人魔改参考。
源码可参见 fa8e5d6 和 4277bc6 .
样式注入
定义 theme.style.darkmode == 'auto-switch'
为开关。
整体上通过写入 data-theme
选择器来实现,因为当前的逻辑是一启动就向 html
注入 data 属性,索性把 auto
也复制了一份情况。
source/css/_defines/theme.styl1 2 3 4 5 6
| if hexo-config('style.darkmode') == 'auto-switch' :root[data-theme="dark"] set_darkmode() :root[data-theme="auto"] @media (prefers-color-scheme: dark) set_darkmode()
|
评论区组件仅测试了 Waline,通过变量定义配色板非常方便。
source/css/_plugins/comments/waline.styl1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| if hexo-config('style.darkmode') == 'auto-switch' :root[data-theme="dark"] .wl-count padding: .375em; font-weight: bold; font-size: 1.25em; color: #fff;
:root[data-theme="dark"] .cmt-body.waline --waline-white: #000; --waline-light-grey: #666; --waline-dark-grey: #999; --waline-color: #fff; --waline-bg-color: #2b2f33b2; --waline-bg-color-light: #272727; --waline-border-color: #333; --waline-disable-bg-color: #444; --waline-disable-color: #272727; --waline-bq-color: #272727; --waline-info-bg-color: #272727; --waline-info-color: #666;
|
修改逻辑
修改了配置中 footer 的部分,仅需配置三个状态下的图标,方便不想把图标放在最后一位的用户。
_config.yml1 2 3 4 5 6
| footer: social: darkmode: auto: '<img no-lazy width="24" height="24" src="/images/sun-moon.svg"/>' light: '<img no-lazy width="24" height="24" src="/images/sun-fill.svg"/>' dark: '<img no-lazy width="24" height="24" src="/images/moon-fill.svg"/>'
|
预处理后的按钮直接绑定 switchTheme()
事件。
layout/components/sidebar/sidebar.jsx1 2 3 4 5 6 7
| <div className="darkmode-switch" id="darkmode-switch-light" title={__('message.theme_switched.light')} data-on-click="switchTheme()" > {parse(item.light)} </div>
|
为了防止页面加载闪屏,在加载 main.css
之前,就需要做好属性注入。
layout/components/head.jsx1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const ImportDarkMode = (props) => { const {theme} = props const DarkModePreset = ` const themeModeList = ['light', 'dark', 'auto']; var ThemeChange = (theme) => { if(theme == null){ theme = 'auto'; } document.documentElement.setAttribute('data-theme', theme) window.localStorage.setItem('Stellaris.theme', theme); } ThemeChange(window.localStorage.getItem('Stellaris.theme')); ` if (theme.style.darkmode == 'auto-switch') { return <script type="text/javascript" data-no-instant="true" dangerouslySetInnerHTML={{__html: DarkModePreset}}/> } else { return <></>; } }
<ImportDarkMode {...props}/> <ImportCSS {...props}/>
|
动态按钮
本来想着动态插拔 class 属性,晚上又突然想到可以使用 data-theme
来实现。
按照约定好的顺序,依次在当前状态下展示下一个按钮即可。
source/css/_components/darkmode.styl1 2 3 4 5 6 7 8 9
| .darkmode-switch display: none cursor: pointer :root[data-theme="dark"] #darkmode-switch-auto display: inline-block :root[data-theme="auto"] #darkmode-switch-light display: inline-block :root[data-theme="light"] #darkmode-switch-dark display: inline-block
|
在页尾再加个监听 prefers-color-scheme
的事件,以便在系统切换时自动切换。最后把前文绑定的函数实现一下即可。
layout/components/scripts.jsx1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const ImportDarkModeListener = (props) => { const {theme, __} = props const DarkModeListener = ` const applyTheme = (theme) => { document.documentElement.setAttribute('data-theme', theme); window.localStorage.setItem('Stellaris.theme', theme); const messages = { light: '${__('message.theme_switched.light')}', dark: '${__('message.theme_switched.dark')}', auto: '${__('message.theme_switched.auto')}', } hud?.toast?.(messages[theme]); } const switchTheme = () => { const currentTheme = document.documentElement.getAttribute('data-theme'); let nextTheme = themeModeList[(themeModeList.indexOf(currentTheme) + 1) % themeModeList.length]; applyTheme(nextTheme); } var OSTheme = window.matchMedia('(prefers-color-scheme: dark)'); OSTheme.addEventListener('change', e => { if (document.documentElement.getAttribute('data-theme') === 'auto') { ThemeChange('auto'); } }) ` if (theme.style.darkmode == 'auto-switch') { return <script type="text/javascript" data-no-instant="true" dangerouslySetInnerHTML={{__html: DarkModeListener}}/> } else { return <></>; } }
|