Choose Theme

Shortcuts: ⌘ ⇧ P
↑↓ Navigate • Enter Confirm • Esc Close
🖥️System
🌻Latte
🦭Nord
🌺Macchiato
🌿Mocha
🏙Tokyo Night
网站Dark Mode&多配色主题开发lqip

网站Dark Mode&多配色主题开发

Catppuccin配色已看腻,今日开发了博客主题&Shiki主题切换功能,支持多种主题;本文简单记录实现思路。

🖥️ System • 🐱 Catppuccin • 🌃 Tokyo Night • 🦭 Nord

效果预览

实现思路

实现多主题色彩管理的核心思路如下:

  • 为HTML根元素设置data-theme属性标识当前主题(如 nord、tokyo_night);切换主题时,动态修改该属性值
  • 针对不同 data-theme 值,在 CSS 中定义同名但值不同的颜色变量; 例如:
    • :where([data-theme=nord]) { --red: #d20f39; }
    • :where([data-theme=tokyo_night]) { --red: #f7768e; }
  • 页面中所有组件的颜色样式,均通过 var(--color-var) 引用 CSS 变量,而非直接使用具体颜色值

色彩变量定义

针对不同主题定义专属变量,可实现 where 选择器[1];同时建议设置 color-scheme[2] 属性,告知浏览器当前主题是亮色还是暗色

CSS
color.css
:where([data-theme="nord"]) {  color-scheme: light;  --red: #d20f39;}:where([data-theme="tokyo_night"]) {  color-scheme: dark;  --red: #f7768e;}

应用如上CSS规则后,页面元素即可根据当前主题动态应用对应颜色值。

例如对于如下页面,h1 元素会应用 nord 主题下的 --red 变量值,即 #d20f39。当用户切换至 tokyo_night 主题后,h1 元素则会应用新的 --red 变量值,即 #f7768e

HTML
index.html
<!-- apply nord theme --><html data-theme="nord">  <head>    <link rel="stylesheet" href="color.css" />  </head>  <body>    <h1 style="color: var(--red)">Hello, World!</h1>  </body></html>

跟随系统

对于“跟随系统主题”选项的实现,有两种实现路径:

CSS方案是思路是:先定义 data-theme="system" 情况下的颜色变量,这里采用浅色主题。在该规则后面,通过 @media (prefers-color-scheme: dark) 检测系统暗色偏好,如果偏好为暗色,则再次针对 :where([data-theme="system"]) 定义暗色主题的变量

CSS
color.css
/* ... light ... */:where([data-theme="system"]) {  color-scheme: light dark;  --rosewater: #dc8a78;}/* ... night ... */@media (prefers-color-scheme: dark) {  :where([data-theme="system"]) {    --rosewater: #f5e0dc;  }}

该方案局限是有冗余变量定义,带来一定维护成本

进一步完善JavaScript代码:

  1. 将用户选择的主题持久化到 localStorage
  2. 在浏览网页时,系统偏好可能改变。可以添加如下代码监听这一事件。
JS
theme-selector.js
const colorSchemeMediaQuery = window.matchMedia( "(prefers-color-scheme: dark)" );//  localStorage里获取用户选择的主题function getThemePreference() {    const stored = localStorage.getItem(STORAGE_KEY);    return stored && stored in THEME_MAP ? stored : DEFAULT_THEME;}// 监听系统主题改变colorSchemeMediaQuery.addEventListener("change", () => {    if (getThemePreference() === "system") {        applyTheme("system");    }});function applyTheme(theme, persist = false) {    const html = document.documentElement;    // 当用户选择跟随系统时,根据系统偏好设置,将data-theme设置为mocha/nord    const resolvedTheme =        theme === "system"            ? colorSchemeMediaQuery.matches                ? "mocha"                : "nord"            : theme;    html.setAttribute("data-theme", resolvedTheme);    if (persist) {        localStorage.setItem(STORAGE_KEY, theme);    }}

关于透明颜色

Example

一些组件可能需要使用半透明颜色。例如这段文字,使用了Callout组件。针对这种需求,一种不吃操作的方案是:

CSS
:where([data-theme="nord"]) {  --red: #d20f39;  --red-10: rgba(210, 15, 57, 0.1); /* 10% 透明度 */  --red-50: rgba(210, 15, 57, 0.5); /* 50% 透明度 */  --green: #40a02b;  --green-10: rgba(64, 160, 43, 0.1);  --green-50: rgba(64, 160, 43, 0.5);  /* 其他颜色 */}

如要广泛使用透明颜色,变量数量会成倍增加;该方法就颇费人力,维护成本也不小
更推荐使用CSS的relative color syntax[3] 来实现透明颜色的定义:

CSS
color-function(from origin-color channel1 channel2 channel3)color-function(from origin-color channel1 channel2 channel3 / alpha)/* color space included in the case of color() functions */color(from origin-color colorspace channel1 channel2 channel3)color(from origin-color colorspace channel1 channel2 channel3 / alpha)

对于上例中的红色,便可直接通过如下方式复用 --red 变量,定义透明版本:

CSS
/* 50% 透明度的红色背景 */background-color: hsl(from var(--red) h s l / 0.5);

浏览器兼容性[4]一般,不过对于个人博客来说,影响不大。

代码高亮 —— Shiki

在代码高亮方面,本博客使用 Shiki 进行静态代码高亮渲染。Shiki 支持传入多种主题。例如:

JS
const themes = {  light: "catppuccin-latte",  dark: "catppuccin-mocha",  tokyo: "tokyo-night"};let code_html = highlighter.codeToHtml(code, {  lang: options.lang || "",  themes: themes,  transformers: enableTransformers ? SUPPORTED_TRANSFORMERS : []});/* Generated Span Be Like  */<span style="color:#1E66F5;--shiki-dark:#89B4FA;--shiki-tokyo:#7AA2F7">name</span>

不难想到,我们可以通过 data-theme 属性,结合 CSS 变量,来实现多主题色彩支持:

CSS
shiki.css
:where([data-theme="tokyo_night"]) {  .shiki span {    color: var(--shiki-tokyo) !important;  }}:where([data-theme="mocha"], [data-theme="macchiato"]) {  .shiki span {    color: var(--shiki-dark) !important;  }}

如果要应用更多主题,那么前面生产的span标签就会非常臃肿,理想的方案是为每个颜色生成单独的class。Shiki提供了 transformerstyletoclass 转换器,将生成如下html

HTML
<pre class="shiki shiki-themes vitesse-dark vitesse-light __shiki_9knfln" tabindex="0"><code><span class="line">  <span class="__shiki_14cn0u">console</span>  <span class="__shiki_ps5uht">.</span>  <span class="__shiki_1zrdwt">log</span>  <span class="__shiki_ps5uht">(</span>  <span class="__shiki_236mh3">'</span>  <span class="__shiki_1g4r39">hello</span>  <span class="__shiki_236mh3">'</span>  <span class="__shiki_ps5uht">)</span></span></code></pre>

配合 transformerStyleToClass({ classPrefix: '__shiki_'}).getCSS() API获取相应的CSS文件:

CSS
auto_generated.css
.__shiki_14cn0u {  --shiki-dark: #bd976a;  --shiki-light: #b07d48;}.__shiki_ps5uht {  --shiki-dark: #666666;  --shiki-light: #999999;}.__shiki_1zrdwt {  --shiki-dark: #80a665;  --shiki-light: #59873a;}.__shiki_236mh3 {  --shiki-dark: #c98a7d77;  --shiki-light: #b5695977;}.__shiki_1g4r39 {  --shiki-dark: #c98a7d;  --shiki-light: #b56959;}.__shiki_9knfln {  --shiki-dark: #dbd7caee;  --shiki-light: #393a34;  --shiki-dark-bg: #121212;  --shiki-light-bg: #ffffff;}

  1. The CSS :where() pseudo-class is used to apply the same style to all the elements inside the parentheses, at the same time. :where() always has 0 specificity. CSS :where Pseudo-class ↩︎

  2. Common choices for operating system color schemes are “light” and “dark”, or “day mode” and “night mode”. When a user selects one of these color schemes, the operating system makes adjustments to the user interface. This includes form controlsscrollbars, and the used values of CSS system colors. color-scheme - CSS | MDN ↩︎

  3. The CSS colors module defines relative color syntax, which allows a CSS <color> value to be defined relative to another color. This is a powerful feature that enables easy creation of complements to existing colors — such as lighter, darker, saturated, semi-transparent, or inverted variants — enabling more effective color palette creation. Using relative colors - CSS | MDN ↩︎

  4. CSS Relative color syntax | Can I use… Support tables for HTML5, CSS3, etc ↩︎

网站Dark Mode&多配色主题开发

https://vluv.space/web_theme/

作者

GnixAij

发布于

2025-11-17

更新于

2025-11-17

许可协议

评论