feat(theme): implement comprehensive theme management system

Add full theme support with light, dark, and system modes, including:
- Theme store using Pinia for state management
- useTheme composable for reactive theme handling
- Theme setting UI in settings page
- Enhanced CSS variable system with Tailwind integration
- IPC communication for theme persistence
- Internationalization support for theme texts
- System theme detection and auto-switching

The implementation follows ClawX's architecture while adapting to Vue 3 and zn-ai's existing infrastructure.
This commit is contained in:
duanshuwen
2026-04-08 23:46:41 +08:00
parent 3ef3392808
commit a8bfbff0e9
12 changed files with 1450 additions and 326 deletions

View File

@@ -6,4 +6,13 @@
</router-view>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { onMounted } from 'vue'
import { useThemeStore } from '@src/stores/theme'
// 初始化主题设置
const themeStore = useThemeStore()
onMounted(() => {
themeStore.init().catch(console.error)
})
</script>

139
src/composables/useTheme.ts Normal file
View File

@@ -0,0 +1,139 @@
/**
* 主题管理 composable
* 提供响应式的主题状态和操作方法
*/
import { computed, onMounted, onUnmounted } from 'vue';
import { useThemeStore, type Theme } from '@src/stores/theme';
/**
* 主题管理 composable
* 封装了主题状态管理、切换和系统主题检测
*/
export function useTheme() {
const themeStore = useThemeStore();
// 响应式状态
const theme = computed(() => themeStore.theme);
const isDark = computed(() => themeStore.isDark);
const systemTheme = computed(() => themeStore.systemTheme);
const initialized = computed(() => themeStore.initialized);
// 计算实际应用的主题(考虑 system 模式)
const appliedTheme = computed(() => {
if (theme.value === 'system') return systemTheme.value;
return theme.value;
});
// 主题选项
const themeOptions = computed(() => [
{ value: 'light' as Theme, label: '浅色' },
{ value: 'dark' as Theme, label: '深色' },
{ value: 'system' as Theme, label: '跟随系统' },
]);
// 获取主题对应的标签
const getThemeLabel = (themeValue: Theme): string => {
const option = themeOptions.value.find(opt => opt.value === themeValue);
return option?.label || themeValue;
};
// 当前主题标签
const currentThemeLabel = computed(() => getThemeLabel(theme.value));
/**
* 初始化主题设置
* 应该在应用启动时调用一次
*/
const initTheme = async () => {
await themeStore.init();
};
/**
* 切换主题
* @param newTheme 新的主题模式
*/
const setTheme = async (newTheme: Theme) => {
await themeStore.setTheme(newTheme);
};
/**
* 切换主题(轮换)
*/
const toggleTheme = async () => {
const themes: Theme[] = ['light', 'dark', 'system'];
const currentIndex = themes.indexOf(theme.value);
const nextIndex = (currentIndex + 1) % themes.length;
await setTheme(themes[nextIndex]);
};
/**
* 重置为系统主题
*/
const resetToSystemTheme = async () => {
await setTheme('system');
};
/**
* 检测当前系统主题
*/
const detectSystemTheme = () => {
return themeStore.detectSystemTheme();
};
// 监听系统主题变化store 内部已处理,这里提供额外的事件监听)
const onSystemThemeChange = (callback: (theme: 'light' | 'dark') => void) => {
if (typeof window === 'undefined') return () => {};
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleChange = (e: MediaQueryListEvent) => {
callback(e.matches ? 'dark' : 'light');
};
// 现代浏览器支持 addEventListener
if (mediaQuery.addEventListener) {
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
} else {
// 旧浏览器支持 addListener
mediaQuery.addListener(handleChange);
return () => mediaQuery.removeListener(handleChange);
}
};
// 自动初始化(可选)
onMounted(() => {
if (!themeStore.initialized) {
initTheme().catch(console.error);
}
});
return {
// 响应式状态
theme,
isDark,
systemTheme,
appliedTheme,
initialized,
// 主题选项
themeOptions,
currentThemeLabel,
// 操作方法
initTheme,
setTheme,
toggleTheme,
resetToSystemTheme,
detectSystemTheme,
getThemeLabel,
// 事件监听
onSystemThemeChange,
// store 引用(谨慎使用)
themeStore,
};
}
// 导出类型
export type { Theme };

View File

@@ -9,7 +9,24 @@
"dark": "Dark Theme",
"light": "Light Theme",
"system": "System Theme",
"primaryColor": "Primary Color"
"primaryColor": "Primary Color",
"description": "Customize the look and feel of the application",
"themeMode": "Theme Mode",
"themeModeDescription": "Choose your preferred theme mode, or follow system settings to switch automatically.",
"lightLabel": "Light",
"lightDescription": "Bright and clear",
"darkLabel": "Dark",
"darkDescription": "Eye comfort",
"systemLabel": "Follow system",
"systemDescription": "Auto switch",
"currentTheme": "Current theme",
"followingSystem": "Following system theme ({{theme}})",
"setToLight": "Set to light theme",
"setToDark": "Set to dark theme",
"preview": "Preview",
"previewDescription": "View the current theme effect",
"lightPreview": "Light preview",
"darkPreview": "Dark preview"
},
"appearance": {
"fontSize": "Font Size",

View File

@@ -9,7 +9,24 @@
"dark": "深色主题",
"light": "浅色主题",
"system": "跟随系统",
"primaryColor": "主题颜色"
"primaryColor": "主题颜色",
"description": "自定义应用程序的外观和风格",
"themeMode": "主题模式",
"themeModeDescription": "选择您偏好的主题模式,或跟随系统设置自动切换。",
"lightLabel": "浅色",
"lightDescription": "明亮清晰",
"darkLabel": "深色",
"darkDescription": "护眼舒适",
"systemLabel": "跟随系统",
"systemDescription": "自动切换",
"currentTheme": "当前主题",
"followingSystem": "跟随系统主题 ({{theme}})",
"setToLight": "已设置为浅色主题",
"setToDark": "已设置为深色主题",
"preview": "预览",
"previewDescription": "查看当前主题效果",
"lightPreview": "浅色预览",
"darkPreview": "深色预览"
},
"appearance": {
"fontSize": "字体大小",

View File

@@ -1,18 +1,58 @@
<template>
<div class="flex-1 h-full p-[20px] select-none">
<TitleSection title="账号设置" desc="请关联PMS和渠道房型名称可使用智能对标" />
<TitleSection title="通用设置" desc="配置应用程序的外观、语言和版本信息" />
<!-- 主题设置 -->
<div
class="w-full flex items-center mt-[20px] py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE]">
<div class="label w-[64px] text-[16px] font-medium text-[#171717] mr-[24px]">当前版本</div>
<div class="value text-[16px] font-medium text-[#171717]">1.0.0</div>
<el-button type="text" class="ml-auto">检查更新</el-button>
class="w-full flex items-center mt-[20px] py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE] dark:border-gray-700">
<div class="label w-[64px] text-[16px] font-medium text-[#171717] dark:text-gray-100 mr-[24px]">主题设置</div>
<div class="value flex-1">
<div class="space-y-4">
<div class="flex flex-wrap gap-3">
<!-- 浅色主题 -->
<button
class="theme-button px-5 py-2.5 rounded-full border text-[14px] font-medium transition-all duration-200 flex items-center gap-2"
:class="[
currentTheme === 'light'
? 'bg-primary-50 dark:bg-primary-900/20 border-primary-500 dark:border-primary-400 text-primary-700 dark:text-primary-300'
: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700'
]" @click="handleThemeChange('light')">
<RiSunLine class="w-4 h-4" />
浅色
</button>
<!-- 深色主题 -->
<button
class="theme-button px-5 py-2.5 rounded-full border text-[14px] font-medium transition-all duration-200 flex items-center gap-2"
:class="[
currentTheme === 'dark'
? 'bg-primary-50 dark:bg-primary-900/20 border-primary-500 dark:border-primary-400 text-primary-700 dark:text-primary-300'
: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700'
]" @click="handleThemeChange('dark')">
<RiMoonLine class="w-4 h-4" />
深色
</button>
<!-- 跟随系统 -->
<button
class="theme-button px-5 py-2.5 rounded-full border text-[14px] font-medium transition-all duration-200 flex items-center gap-2"
:class="[
currentTheme === 'system'
? 'bg-primary-50 dark:bg-primary-900/20 border-primary-500 dark:border-primary-400 text-primary-700 dark:text-primary-300'
: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700'
]" @click="handleThemeChange('system')">
<RiComputerLine class="w-4 h-4" />
跟随系统
</button>
</div>
</div>
</div>
</div>
<!-- 语言设置 -->
<div
class="w-full flex items-center mt-[20px] py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE]">
<div class="label w-[64px] text-[16px] font-medium text-[#171717] mr-[24px]">语言设置</div>
class="w-full flex items-center mt-[20px] py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE] dark:border-gray-700">
<div class="label w-[64px] text-[16px] font-medium text-[#171717] dark:text-gray-100 mr-[24px]">语言设置</div>
<div class="value flex gap-2">
<el-button v-for="lang in supportedLanguages" :key="lang.code"
:type="currentLanguage === lang.code ? 'primary' : 'text'" @click="handleLanguageChange(lang.code)"
@@ -21,14 +61,40 @@
</el-button>
</div>
</div>
<!-- 版本信息 -->
<div
class="w-full flex items-center mt-[20px] py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE] dark:border-gray-700">
<div class="label w-[64px] text-[16px] font-medium text-[#171717] dark:text-gray-100 mr-[24px]">当前版本</div>
<div class="value text-[16px] font-medium text-[#171717] dark:text-gray-100">1.0.0</div>
<el-button type="text" class="ml-auto">检查更新</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ref, onMounted, computed } from 'vue'
import TitleSection from '@src/components/TitleSection/index.vue'
import { setLanguage, getLanguage, type LanguageType } from '@src/i18n'
import { useThemeStore } from '@src/stores/theme'
import { RiSunLine, RiMoonLine, RiComputerLine } from '@remixicon/vue'
// 主题状态管理
const themeStore = useThemeStore()
const currentTheme = computed(() => themeStore.theme)
// 主题切换处理
const handleThemeChange = async (theme: 'light' | 'dark' | 'system') => {
try {
console.log('切换主题到:', theme)
await themeStore.setTheme(theme)
console.log('主题切换成功:', theme)
} catch (error) {
console.error('主题切换失败:', error)
}
}
// 语言设置
const supportedLanguages = [
{ code: 'zh', label: '中文' },
{ code: 'en', label: 'English' },
@@ -46,4 +112,19 @@ const handleLanguageChange = async (langCode: LanguageType) => {
await setLanguage(langCode)
currentLanguage.value = langCode
}
</script>
</script>
<style scoped>
.theme-button {
cursor: pointer;
outline: none;
transition-property: background-color, border-color, color, transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.theme-button:focus-visible {
outline: 2px solid var(--primary-color);
outline-offset: 2px;
}
</style>

182
src/stores/theme.ts Normal file
View File

@@ -0,0 +1,182 @@
/**
* 主题状态管理 store
* 采用 Vue 推荐的 Pinia 实现,功能和架构与 ClawX 的 Zustand 实现保持一致
* 集成 zn-ai 现有 IPC 通信
*/
import { defineStore } from 'pinia';
export type Theme = 'light' | 'dark' | 'system';
interface ThemeState {
// 主题状态
theme: Theme;
isDark: boolean;
systemTheme: 'light' | 'dark';
// 初始化状态
initialized: boolean;
}
// 检测系统主题
const detectSystemTheme = (): 'light' | 'dark' => {
if (typeof window === 'undefined') return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
};
// 计算实际应用的主题(考虑 system 模式)
const getAppliedTheme = (theme: Theme, systemTheme: 'light' | 'dark'): 'light' | 'dark' => {
if (theme === 'system') return systemTheme;
return theme;
};
// 应用主题到 DOM通过 CSS 类)
const applyThemeToDom = (theme: 'light' | 'dark') => {
if (typeof document === 'undefined') return;
const root = document.documentElement;
if (theme === 'dark') {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
};
export const useThemeStore = defineStore('zn-ai-theme', {
state: (): ThemeState => {
// 从 localStorage 恢复缓存的主题,确保在初始化前 UI 不会闪烁
const cachedTheme = typeof localStorage !== 'undefined' ? localStorage.getItem('zn-ai-theme-cache') as Theme : null;
const initialTheme = (cachedTheme === 'light' || cachedTheme === 'dark' || cachedTheme === 'system') ? cachedTheme : 'system';
return {
theme: initialTheme,
isDark: false,
systemTheme: 'light',
initialized: false,
};
},
actions: {
async init() {
if (this.initialized) return;
try {
// 1. 检测系统主题
const systemTheme = detectSystemTheme();
// 2. 从主进程获取持久化的主题设置
let savedTheme: Theme = 'system';
try {
savedTheme = await window.api.invoke<Theme>('get-theme-mode');
} catch (error) {
console.warn('Failed to get theme from main process, using default:', error);
}
// 3. 计算实际主题和 isDark 状态
const appliedTheme = getAppliedTheme(savedTheme, systemTheme);
const isDark = appliedTheme === 'dark';
// 4. 应用主题到 DOM
applyThemeToDom(appliedTheme);
// 5. 更新 store 状态
this.theme = savedTheme;
this.isDark = isDark;
this.systemTheme = systemTheme;
this.initialized = true;
// 缓存到 localStorage
if (typeof localStorage !== 'undefined') {
localStorage.setItem('zn-ai-theme-cache', savedTheme);
}
// 6. 监听系统主题变化
if (typeof window !== 'undefined') {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleChange = (e: MediaQueryListEvent) => {
const newSystemTheme = e.matches ? 'dark' : 'light';
if (this.theme === 'system') {
const appliedTheme = getAppliedTheme('system', newSystemTheme);
const isDark = appliedTheme === 'dark';
applyThemeToDom(appliedTheme);
this.systemTheme = newSystemTheme;
this.isDark = isDark;
} else {
this.systemTheme = newSystemTheme;
}
};
if (mediaQuery.addEventListener) {
mediaQuery.addEventListener('change', handleChange);
} else {
mediaQuery.addListener(handleChange);
}
// 7. 监听主进程主题更新事件
window.api.on('theme-mode-updated', (isDarkUpdate: boolean) => {
const newIsDark = Boolean(isDarkUpdate);
const newTheme = newIsDark ? 'dark' : 'light';
if (this.theme === 'system') {
this.isDark = newIsDark;
} else {
const actualTheme = newIsDark ? 'dark' : 'light';
if (this.theme !== actualTheme) {
this.theme = actualTheme;
this.isDark = newIsDark;
}
}
});
}
console.log('Theme store initialized:', { theme: savedTheme, isDark, systemTheme });
} catch (error) {
console.error('Failed to initialize theme store:', error);
const systemTheme = detectSystemTheme();
const appliedTheme = getAppliedTheme('system', systemTheme);
const isDark = appliedTheme === 'dark';
applyThemeToDom(appliedTheme);
this.theme = 'system';
this.isDark = isDark;
this.systemTheme = systemTheme;
this.initialized = true;
}
},
async setTheme(theme: Theme) {
if (theme === this.theme) return;
try {
// 1. 保存到主进程
await window.api.invoke('set-theme-mode', theme);
// 2. 计算实际应用的主题
const appliedTheme = getAppliedTheme(theme, this.systemTheme);
const isDark = appliedTheme === 'dark';
// 3. 应用主题到 DOM
applyThemeToDom(appliedTheme);
// 4. 更新 store 状态
this.theme = theme;
this.isDark = isDark;
// 5. 缓存到 localStorage
if (typeof localStorage !== 'undefined') {
localStorage.setItem('zn-ai-theme-cache', theme);
}
console.log('Theme changed:', { theme, appliedTheme, isDark });
} catch (error) {
console.error('Failed to set theme:', error);
throw error;
}
},
detectSystemTheme,
updateIsDark() {
const appliedTheme = getAppliedTheme(this.theme, this.systemTheme);
this.isDark = appliedTheme === 'dark';
}
}
});

View File

@@ -1,4 +1,8 @@
@import "tailwindcss";
/* 启用 dark 变体,基于 .dark 类 */
@custom-variant dark (&:where(.dark, .dark *));
@import "./theme/index.css";
@import "./tailwind.css";
@plugin "@tailwindcss/typography";

View File

@@ -1,126 +1,111 @@
@media (prefers-color-scheme: dark) {
:root {
--primary-color: #07C160;
--bg-color: #1E1E1E;
--bg-secondary: #2C2C2C;
/* 深色主题变量定义在 .dark 类下 */
.dark {
--primary-color: #07C160;
--bg-color: #1E1E1E;
--bg-secondary: #2C2C2C;
--text-primary: #E0E0E0;
--text-secondary: #A0A0A0;
--text-primary: #E0E0E0;
--text-secondary: #A0A0A0;
--bubble-self: var(--primary-color);
--bubble-others: #3A3A3A;
--input-bg: #333333;
--ripple-color: var(--text-secondary);
--ripple-opacity: 0.2;
}
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em
}
code.hljs {
padding: 3px 5px
}
/*!
Theme: GitHub Dark
Description: Dark theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-dark
Current colors taken from GitHub's CSS
*/
.hljs {
color: var(--text-primary);
background: var(--input-bg);
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #ff7b72
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #d2a8ff
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #79c0ff
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #a5d6ff
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #ffa657
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #8b949e
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #7ee787
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #c9d1d9
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #1f6feb;
font-weight: bold
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #f2cc60
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #c9d1d9;
font-style: italic
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #c9d1d9;
font-weight: bold
}
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #aff5b4;
background-color: #033a16
}
.hljs-deletion {
/* prettylights-syntax-markup-deleted */
color: #ffdcd7;
background-color: #67060c
}
--bubble-self: var(--primary-color);
--bubble-others: #3A3A3A;
--input-bg: #333333;
--ripple-color: var(--text-secondary);
--ripple-opacity: 0.2;
}
/* 代码高亮样式 */
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em
}
code.hljs {
padding: 3px 5px
}
/*!
Theme: GitHub Dark
Description: Dark theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-dark
Current colors taken from GitHub's CSS
*/
.dark .hljs {
color: var(--text-primary);
background: var(--input-bg);
}
.dark .hljs-doctag,
.dark .hljs-keyword,
.dark .hljs-meta .hljs-keyword,
.dark .hljs-template-tag,
.dark .hljs-template-variable,
.dark .hljs-type,
.dark .hljs-variable.language_ {
color: #ff7b72
}
.dark .hljs-title,
.dark .hljs-title.class_,
.dark .hljs-title.class_.inherited__,
.dark .hljs-title.function_ {
color: #d2a8ff
}
.dark .hljs-attr,
.dark .hljs-attribute,
.dark .hljs-literal,
.dark .hljs-meta,
.dark .hljs-number,
.dark .hljs-operator,
.dark .hljs-variable,
.dark .hljs-selector-attr,
.dark .hljs-selector-class,
.dark .hljs-selector-id {
color: #79c0ff
}
.dark .hljs-regexp,
.dark .hljs-string,
.dark .hljs-meta .hljs-string {
color: #a5d6ff
}
.dark .hljs-built_in,
.dark .hljs-symbol {
color: #ffa657
}
.dark .hljs-comment,
.dark .hljs-code,
.dark .hljs-formula {
color: #8b949e
}
.dark .hljs-name,
.dark .hljs-quote,
.dark .hljs-selector-tag,
.dark .hljs-selector-pseudo {
color: #7ee787
}
.dark .hljs-subst {
color: #c9d1d9
}
.dark .hljs-section {
color: #1f6feb;
font-weight: bold
}
.dark .hljs-bullet {
color: #f2cc60
}
.dark .hljs-emphasis {
color: #c9d1d9;
font-style: italic
}
.dark .hljs-strong {
color: #c9d1d9;
font-weight: bold
}
.dark .hljs-addition {
color: #aff5b4;
background-color: #033a16
}
.dark .hljs-deletion {
color: #ffdcd7;
background-color: #67060c
}

View File

@@ -1,129 +1,112 @@
@media (prefers-color-scheme: light) {
:root {
--primary-color: #07C160;
--bg-color: #FFFFFF;
--bg-secondary: #F5F5F5;
--text-primary: #000000;
--text-secondary: #7F7F7F;
/* 浅色主题变量定义在 :root 中 */
:root {
--primary-color: #07C160;
--bg-color: #FFFFFF;
--bg-secondary: #F5F5F5;
--text-primary: #000000;
--text-secondary: #7F7F7F;
--header-bg: var(--primary-color);
--bubble-self: var(--primary-color);
--bubble-others: #FFFFFF;
--input-bg: #F0F0F0;
--ripple-color: var(--text-secondary);
--ripple-opacity: 0.2;
}
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em
}
code.hljs {
padding: 3px 5px
}
/*!
Theme: GitHub
Description: Light theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
--header-bg: var(--primary-color);
--bubble-self: var(--primary-color);
--bubble-others: #FFFFFF;
--input-bg: #F0F0F0;
--ripple-color: var(--text-secondary);
--ripple-opacity: 0.2;
}
Outdated base version: https://github.com/primer/github-syntax-light
Current colors taken from GitHub's CSS
*/
.hljs {
/* color: #24292e;
background: #ffffff */
/* 代码高亮样式 */
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em
}
code.hljs {
padding: 3px 5px
}
/*!
Theme: GitHub
Description: Light theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
color: var(--text-primary);
background: var(--input-bg);
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #d73a49
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #6f42c1
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #005cc5
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #032f62
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #e36209
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #6a737d
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #22863a
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #24292e
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #005cc5;
font-weight: bold
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #735c0f
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #24292e;
font-style: italic
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #24292e;
font-weight: bold
}
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #22863a;
background-color: #f0fff4
}
.hljs-deletion {
/* prettylights-syntax-markup-deleted */
color: #b31d28;
background-color: #ffeef0
}
Outdated base version: https://github.com/primer/github-syntax-light
Current colors taken from GitHub's CSS
*/
.hljs {
color: var(--text-primary);
background: var(--input-bg);
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
color: #d73a49
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
color: #6f42c1
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
color: #005cc5
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
color: #032f62
}
.hljs-built_in,
.hljs-symbol {
color: #e36209
}
.hljs-comment,
.hljs-code,
.hljs-formula {
color: #6a737d
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
color: #22863a
}
.hljs-subst {
color: #24292e
}
.hljs-section {
color: #005cc5;
font-weight: bold
}
.hljs-bullet {
color: #735c0f
}
.hljs-emphasis {
color: #24292e;
font-style: italic
}
.hljs-strong {
color: #24292e;
font-weight: bold
}
.hljs-addition {
color: #22863a;
background-color: #f0fff4
}
.hljs-deletion {
color: #b31d28;
background-color: #ffeef0
}