adopt CYBER neon-dark theme tokens

Replace the Pure Ink light/dark/comic theme system with the CYBER
single-theme dark palette from the reviewed prototype: OKLCH surfaces,
one neon-cyan accent, and monospace-first typography.

Arco components were reading colors from arco.css because no
ConfigProvider was mounted, so the theme values in arco-theme.ts never
took effect. Bridge CYBER into Arco through its --color-* variables in
global.scss instead, and align arco-theme.ts to match.

Brightness and style switches are gone: useTheme now pins dark, the
comic font faces and locale keys are removed, and ThemeSwitch.vue is
deleted along with its sidebar slot and display setting.
This commit is contained in:
2026-10-19 13:04:49 +08:00
parent 145d240dc2
commit 282624f801
12 changed files with 197 additions and 384 deletions
@@ -3,7 +3,6 @@ import { computed } from 'vue'
import { useMessage } from '@/composables/useMessage'
import { useI18n } from 'vue-i18n'
import { useSettingsStore } from '@/stores/hermes/settings'
import { useTheme, type BrightnessMode } from '@/composables/useTheme'
import { requestCompletionNotificationPermission, showCompletionNotification, type CompletionNotificationPermissionResult } from '@/utils/completion-notification'
import { clampChatInputHeight, MAX_CHAT_INPUT_HEIGHT, MIN_CHAT_INPUT_HEIGHT } from '@/utils/chat-input-height'
import SettingRow from './SettingRow.vue'
@@ -11,13 +10,7 @@ import SettingRow from './SettingRow.vue'
const settingsStore = useSettingsStore()
const message = useMessage()
const { t } = useI18n()
const { brightness, setBrightness } = useTheme()
const themeOptions = [
{ label: t('settings.display.themeLight'), value: 'light' },
{ label: t('settings.display.themeDark'), value: 'dark' },
{ label: t('settings.display.themeSystem'), value: 'system' },
]
const chatInputHeight = computed(() => clampChatInputHeight(settingsStore.display.chat_input_height))
async function save(values: Record<string, any>) {
@@ -29,12 +22,6 @@ async function save(values: Record<string, any>) {
}
}
function handleThemeChange(val: string) {
const m = val as BrightnessMode
setBrightness(m)
save({ skin: m })
}
function handleChatInputHeightChange(value: number | null) {
return save({ chat_input_height: clampChatInputHeight(value) })
}
@@ -90,9 +77,6 @@ async function testCompletionNotification() {
<template>
<section class="settings-section">
<SettingRow :label="t('settings.display.theme')" :hint="t('settings.display.themeHint')">
<a-select :model-value="brightness" :options="themeOptions" size="small" class="input-sm" @update:model-value="v => handleThemeChange(String(v))" />
</SettingRow>
<SettingRow :label="t('settings.display.streaming')" :hint="t('settings.display.streamingHint')">
<a-switch :model-value="settingsStore.display.streaming" @update:model-value="v => save({ streaming: v })" />
</SettingRow>
@@ -9,7 +9,6 @@ import RouteLinkItem from '@/components/common/RouteLinkItem.vue'
import ModelSelector from "@/components/layout/ModelSelector.vue";
import ProfileSelector from "@/components/layout/ProfileSelector.vue";
import LanguageSwitch from "@/components/layout/LanguageSwitch.vue";
import ThemeSwitch from "@/components/layout/ThemeSwitch.vue";
import { changelog } from "@/data/changelog";
import { getStoredUsername, isStoredSuperAdmin } from "@/api/client";
@@ -350,7 +349,6 @@ function handleUpdateClick() {
>
Studio v{{ appStore.serverVersion || "0.1.0" }}
</span>
<ThemeSwitch />
</div>
<a-button v-if="appStore.clientOutdated" type="primary" status="warning" size="mini" long class="update-btn" @click="handleReloadClient">
{{ t('sidebar.reloadClientVersion', { version: appStore.serverVersion }) }}
@@ -662,10 +660,6 @@ function handleUpdateClick() {
}
}
.version-info :deep(.theme-switch-container) {
flex-shrink: 0;
}
.update-btn {
margin: 4px 0 0;
border-radius: $radius-sm;
@@ -1,59 +0,0 @@
<script setup lang="ts">
import { useTheme } from '@/composables/useTheme'
const { isDark, isComic, toggleBrightness, toggleStyle } = useTheme()
</script>
<template>
<div class="theme-switch-container" style="display: flex; gap: 4px; align-items: center;">
<button class="theme-switch" :title="isComic ? 'Ink style' : 'Comic style'" @click="toggleStyle">
<!-- Palette icon for comic toggle -->
<svg v-if="isComic" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" />
</svg>
<!-- Sparkle icon for ink mode -->
<svg v-else width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 3l1.5 5.5L19 10l-5.5 1.5L12 17l-1.5-5.5L5 10l5.5-1.5L12 3z" />
</svg>
</button>
<button class="theme-switch" :title="isDark ? 'Light mode' : 'Dark mode'" @click="toggleBrightness">
<!-- Sun icon (shown in dark mode) -->
<svg v-if="isDark" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5" />
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</svg>
<!-- Moon icon (shown in light mode) -->
<svg v-else width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
</svg>
</button>
</div>
</template>
<style scoped lang="scss">
.theme-switch {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border: none;
background: transparent;
border-radius: 4px;
cursor: pointer;
color: var(--text-muted);
transition: color 0.15s ease, background-color 0.15s ease;
&:hover {
color: var(--text-primary);
background: rgba(var(--accent-primary-rgb), 0.06);
}
}
</style>
+12 -75
View File
@@ -1,91 +1,28 @@
import { ref, watch, computed } from 'vue'
import { ref, computed } from 'vue'
export type BrightnessMode = 'light' | 'dark' | 'system'
export type ThemeStyle = 'ink' | 'comic'
// CYBER 是单色暗色主题:亮度与风格切换器已移除(comic 与 light 主题不再提供)。
// 这里保留 isDark 以兼容 App.vue / JourneyView 里的 :class 绑定,值恒为 true。
const isDark = ref(true)
const BRIGHTNESS_KEY = 'hermes_brightness'
const STYLE_KEY = 'hermes_style'
export type BrightnessMode = 'dark'
const brightness = ref<BrightnessMode>(
(localStorage.getItem(BRIGHTNESS_KEY) as BrightnessMode) || 'system',
)
const brightness = ref<BrightnessMode>('dark')
const style = ref<ThemeStyle>(
(localStorage.getItem(STYLE_KEY) as ThemeStyle) || 'ink',
)
const isDark = ref(false)
const isComic = ref(false)
function resolveDark(b: BrightnessMode): boolean {
if (b === 'system') {
return window.matchMedia('(prefers-color-scheme: dark)').matches
}
return b === 'dark'
}
function applyClasses() {
const dark = resolveDark(brightness.value)
isDark.value = dark
isComic.value = style.value === 'comic'
document.documentElement.classList.toggle('dark', dark)
document.documentElement.classList.toggle('comic', isComic.value)
// Arco Design dark theme mechanism (Phase 005)
document.documentElement.setAttribute('arco-theme', dark ? 'dark' : 'light')
}
// Initial
applyClasses()
// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
if (brightness.value === 'system') {
applyClasses()
}
})
// Persist & apply on change
watch(brightness, (b) => {
localStorage.setItem(BRIGHTNESS_KEY, b)
applyClasses()
})
watch(style, (s) => {
localStorage.setItem(STYLE_KEY, s)
applyClasses()
})
// 固定应用暗色:Arco 的 token 通过 arco-theme 属性切换,项目的令牌写在 :root。
document.documentElement.setAttribute('arco-theme', 'dark')
export function useTheme() {
const themeName = computed(() => {
const b = isDark.value ? 'dark' : 'light'
return isComic.value ? `comic-${b}` : b
})
const themeName = computed(() => 'dark')
function setBrightness(b: BrightnessMode) {
brightness.value = b
}
function setStyle(s: ThemeStyle) {
style.value = s
}
function toggleBrightness() {
brightness.value = isDark.value ? 'light' : 'dark'
}
function toggleStyle() {
style.value = isComic.value ? 'ink' : 'comic'
function setBrightness(_b: BrightnessMode) {
// 单主题:忽略任何切换请求。
brightness.value = 'dark'
}
return {
brightness,
style,
isDark,
isComic,
themeName,
setBrightness,
setStyle,
toggleBrightness,
toggleStyle,
}
}
-5
View File
@@ -1575,11 +1575,6 @@ export default {
notifyOnCompleteTestFailed: 'Failed to send test notification',
busyInputMode: 'Busy Input Mode',
busyInputModeHint: 'Allow input while AI is processing',
theme: 'Theme',
themeHint: 'Choose light, dark, or follow system preference',
themeLight: 'Light',
themeDark: 'Dark',
themeSystem: 'System',
chatInputHeight: 'Chat input height',
chatInputHeightHint: 'Default chat input height in pixels on wide screens. Mobile keeps auto height.'
},
-5
View File
@@ -1567,11 +1567,6 @@ export default {
notifyOnCompleteTestFailed: '测试通知发送失败',
busyInputMode: '忙碌输入模式',
busyInputModeHint: 'AI 处理中仍可输入',
theme: '主题',
themeHint: '选择浅色、暗色或跟随系统',
themeLight: '浅色',
themeDark: '暗色',
themeSystem: '跟随系统',
chatInputHeight: '聊天输入框高度',
chatInputHeightHint: '桌面端聊天输入框默认高度(像素)。移动端保持自动高度。'
},
+2 -18
View File
@@ -8,24 +8,8 @@ import App from './App.vue'
import './styles/global.scss'
import 'katex/dist/katex.min.css'
// Apply theme classes before mount to prevent FOUC (Flash of Unstyled Content)
const savedBrightness = localStorage.getItem('hermes_brightness') || 'system'
const savedStyle = localStorage.getItem('hermes_style') || 'ink'
// Resolve dark mode
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
const isDark = savedBrightness === 'dark' || (savedBrightness === 'system' && prefersDark)
// Resolve style
const isComic = savedStyle === 'comic'
// Apply classes to prevent FOUC
if (isDark) {
document.documentElement.classList.add('dark')
}
if (isComic) {
document.documentElement.classList.add('comic')
}
// CYBER 单主题:固定暗色,无需读取 localStorage 或匹配系统偏好。
document.documentElement.setAttribute('arco-theme', 'dark')
// Read token from URL BEFORE router initializes (hash router strips params)
const urlParams = new URLSearchParams(window.location.search)
+15 -9
View File
@@ -1,14 +1,20 @@
// Arco Design theme tokens for hermes-ui (Phase 005 baseline).
// Dark mode is carried by `body[arco-theme='dark']` CSS overrides, not a second theme object.
// See docs/ui-baseline.md for the frozen visual language.
// Arco Design theme tokens for hermes-ui — CYBER.
//
// 这些值是「参考值」,不是运行时来源:项目没有挂 ConfigProvider,Arco 的实际外观
// 由 styles/global.scss 里的 --color-* 变量覆盖决定(见那里的 Arco token bridge)。
// 保留本文件是为了让读取 token 的人能在一处看到意图,以及给测试一个可断言的锚点。
// 改色时请同时改 global.scss 的 Arco token bridge,两处保持一致。
export const arcoTheme = {
// 延续旧版克制灰阶
colorPrimary: '#2f2f2f',
colorBgLayout: '#fafafa',
colorBgContainer: '#ffffff',
// 霓虹青 oklch(0.805 0.148 188) ≈ rgb(45, 212, 191)
colorPrimary: '#2dd4bf',
// 页面底 / 卡片底,对应 oklch(0.155 0.012 250) 与 oklch(0.130 0.012 250)
colorBgLayout: '#1a1d23',
colorBgContainer: '#14171c',
borderRadius: 6,
fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
fontFamilyMono: '"JetBrains Mono", "Fira Code", monospace',
fontFamily:
'-apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Segoe UI", Roboto, sans-serif',
fontFamilyMono:
'ui-monospace, "JetBrains Mono", "SF Mono", Menlo, "Cascadia Code", monospace',
}
export type ArcoTheme = typeof arcoTheme
+96 -59
View File
@@ -1,54 +1,6 @@
@use 'variables' as *;
@use 'code-block';
@font-face {
font-family: 'Comic Neue';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/ComicNeue-Bold.ttf') format('truetype');
}
@font-face {
font-family: 'ZCOOL KuaiLe';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/ZCOOLKuaiLe-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Zen Maru Gothic';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/ZenMaruGothic-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Zen Maru Gothic';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/fonts/ZenMaruGothic-Bold.ttf') format('truetype');
}
@font-face {
font-family: 'Gaegu';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/Gaegu-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Gaegu';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/fonts/Gaegu-Bold.ttf') format('truetype');
}
*,
*::before,
*::after {
@@ -75,13 +27,98 @@ textarea {
}
}
// Theme transition (applied programmatically on toggle, not on load)
html.theme-transitioning,
html.theme-transitioning *,
html.theme-transitioning *::before,
html.theme-transitioning *::after {
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease,
box-shadow 0.3s ease, fill 0.3s ease, stroke 0.3s ease !important;
// ─── Arco Design token bridge ──────────────────────────────────
// arco-theme.ts 不是运行时来源(没有 ConfigProvider),Arco 的实际外观由 arco.css
// 里的 --color-* 变量决定。这里把 CYBER 令牌翻译成 Arco 变量,让 a-select /
// a-input / a-tag / a-table / a-modal 等自有控件与项目配色对齐。
// 变量表见 node_modules/@arco-design/web-vue/dist/arco.css 的 :root 块。
html[arco-theme='dark'],
:root {
--color-primary-light-1: oklch(0.30 0.060 188);
--color-primary-light-2: oklch(0.39 0.075 188);
--color-primary-light-3: oklch(0.50 0.095 188);
--color-primary-light-4: oklch(0.60 0.115 188);
--color-primary-light-5: oklch(0.68 0.130 188);
--color-primary-light-6: oklch(0.805 0.148 188);
--color-primary: oklch(0.805 0.148 188);
--color-primary-dark-1: oklch(0.760 0.144 188);
--color-primary-dark-2: oklch(0.700 0.138 188);
--color-primary-dark-3: oklch(0.640 0.130 188);
--color-primary-dark-4: oklch(0.580 0.120 188);
--color-primary-dark-5: oklch(0.520 0.108 188);
--color-primary-dark-6: oklch(0.460 0.094 188);
--color-success-light-1: oklch(0.28 0.050 165);
--color-success-light-2: oklch(0.42 0.078 165);
--color-success-light-3: oklch(0.58 0.108 165);
--color-success: oklch(0.760 0.140 165);
--color-warning-light-1: oklch(0.30 0.052 85);
--color-warning-light-2: oklch(0.46 0.082 85);
--color-warning-light-3: oklch(0.62 0.112 85);
--color-warning: oklch(0.810 0.145 85);
--color-danger-light-1: oklch(0.28 0.062 22);
--color-danger-light-2: oklch(0.42 0.096 22);
--color-danger-light-3: oklch(0.56 0.138 22);
--color-danger: oklch(0.665 0.185 22);
--color-link-light-1: oklch(0.30 0.060 188);
--color-link-light-2: oklch(0.42 0.084 188);
--color-link-light-3: oklch(0.54 0.110 188);
--color-link-light-4: oklch(0.66 0.136 188);
--color-link-light-5: oklch(0.74 0.148 188);
--color-link-light-6: oklch(0.805 0.148 188);
--color-link: oklch(0.805 0.148 188);
// 容器与文字
--color-text-1: oklch(0.945 0.006 200);
--color-text-2: oklch(0.860 0.008 210);
--color-text-3: oklch(0.705 0.014 230);
--color-text-4: oklch(0.520 0.014 236);
--color-text-disabled: oklch(0.420 0.012 238);
--color-bg-1: oklch(0.155 0.012 250);
--color-bg-2: oklch(0.130 0.012 250);
--color-bg-3: oklch(0.185 0.014 248);
--color-bg-4: oklch(0.225 0.016 246);
--color-bg-5: oklch(0.255 0.018 247);
--color-fill-1: oklch(0.185 0.014 248);
--color-fill-2: oklch(0.225 0.016 246);
--color-fill-3: oklch(0.255 0.018 247);
--color-fill-4: oklch(0.285 0.018 246);
--color-border-1: oklch(0.305 0.018 242);
--color-border-2: oklch(0.255 0.016 244);
--color-border-3: oklch(0.225 0.014 246);
--color-border-4: oklch(0.195 0.014 248);
--color-fill-2-hover: oklch(0.255 0.018 247);
--color-fill-3-hover: oklch(0.285 0.018 246);
--radius-small: 4px;
--radius-medium: 6px;
--radius-large: 8px;
--font-size-body-1: 14px;
--font-size-body-2: 13px;
--font-size-body-3: 12px;
// CYBER 的核心排版特征:标题、页头、数据用等宽
--font-family: #{$font-ui};
--font-family-body: #{$font-ui};
--font-family-code: #{$font-code};
}
// Arco 暗色态:强制走暗色变量(属性挂在 <html> 上,Arco 官方选择器可能匹配 body)
body {
color: $text-primary;
background-color: $bg-primary;
}
// Arco 组件里的滚动条与焦点环对齐 CYBER
.arco-scrollbar-thumb {
background-color: $border-color !important;
}
html, body {
@@ -146,8 +183,8 @@ a {
}
::-webkit-scrollbar {
width: 6px;
height: 6px;
width: 8px;
height: 8px;
background: transparent;
}
@@ -158,10 +195,10 @@ a {
::-webkit-scrollbar-thumb {
background: $border-color;
border-radius: 3px;
border-radius: 4px;
&:hover {
background: $text-muted;
background: rgba(var(--accent-primary-rgb), 0.5);
}
}
+52 -117
View File
@@ -1,132 +1,64 @@
// 黑白水墨 — Pure Ink
// 纯黑白灰,无彩色
// 支持 light / dark / comic 三主题
// CYBER — 霓虹暗色单主题
// 设计语言见 prototype_reference/hermes-ui-cyber-api.html 与 hermes-ui-cyber-login.html。
// 暗色为唯一主题:亮度/风格切换器已移除,因此这里只有一组令牌,没有 .dark / .comic 变体。
// 唯一强调色是霓虹青 oklch(0.805 0.148 188),状态色仅用于语义,不参与装饰。
// ─── CSS Custom Properties ─────────────────────────────────────
:root {
// Backgrounds
--bg-primary: #fafafa;
--bg-secondary: #f0f0f0;
--bg-sidebar: #f5f5f5;
--bg-card: #ffffff;
--bg-card-hover: #fafafa;
--bg-input: #ffffff;
// Backgrounds — 由深到浅的四层
--bg-primary: oklch(0.155 0.012 250); // 页面底色
--bg-secondary: oklch(0.130 0.012 250); // 卡片 / 面板(比页面更沉,向下压)
--bg-sidebar: oklch(0.118 0.013 251); // 侧栏,比卡片更沉
--bg-card: oklch(0.185 0.014 248); // 容器 / 输入区(比页面更亮,向上浮)
--bg-card-hover: oklch(0.225 0.016 246);
--bg-input: oklch(0.105 0.012 252); // 输入框,比卡片更沉,形成凹陷感
// Borders
--border-color: #e0e0e0;
--border-light: #ebebeb;
// Borders — 发丝线分两档
--border-color: oklch(0.305 0.018 242);
--border-light: oklch(0.255 0.016 244);
// Accent
--accent-primary: #333333;
--accent-hover: #1a1a1a;
--accent-muted: #888888;
// Accent — 霓虹青,全站唯一强调色
--accent-primary: oklch(0.805 0.148 188);
--accent-hover: oklch(0.870 0.150 188);
--accent-muted: oklch(0.520 0.060 188);
--accent-soft: oklch(0.805 0.148 188 / 0.13);
--accent-ink: oklch(0.160 0.030 200); // 强调色上的文字/图标(深色,保证对比)
// Text
--text-primary: #1a1a1a;
--text-secondary: #666666;
--text-muted: #999999;
--text-primary: oklch(0.945 0.006 200);
--text-secondary: oklch(0.860 0.008 210);
--text-muted: oklch(0.705 0.014 230);
--text-faint: oklch(0.520 0.014 236); // 禁用态 / 极次要信息
// Status
--success: #2e7d32;
--error: #c62828;
--warning: #f57f17;
// Status — 语义色,不用于装饰
--success: oklch(0.760 0.140 165);
--error: oklch(0.665 0.185 22);
--warning: oklch(0.810 0.145 85);
--accent-info: oklch(0.700 0.110 245);
// Message bubbles
--msg-user-bg: #f5f5f5;
--msg-assistant-bg: #f5f5f5;
--msg-system-border: #bdbdbd;
--msg-user-bg: oklch(0.225 0.016 246);
--msg-assistant-bg: oklch(0.185 0.014 248);
--msg-system-border: oklch(0.305 0.018 242);
// Code
--code-bg: #f4f4f4;
--code-bg: oklch(0.115 0.014 250);
// Utility
--text-on-accent: #ffffff;
--text-on-overlay: #ffffff;
--accent-info: #4a90d9;
--text-on-accent: oklch(0.160 0.030 200);
--text-on-overlay: oklch(0.945 0.006 200);
// RGB components (for rgba() usage)
--accent-primary-rgb: 51, 51, 51;
--accent-hover-rgb: 26, 26, 26;
--text-primary-rgb: 26, 26, 26;
--text-muted-rgb: 153, 153, 153;
--success-rgb: 46, 125, 50;
--error-rgb: 198, 40, 40;
--warning-rgb: 245, 127, 23;
--accent-info-rgb: 74, 144, 217;
}
.dark {
// Backgrounds
--bg-primary: #1a1a1a;
--bg-secondary: #252525;
--bg-sidebar: #202020;
--bg-card: #2a2a2a;
--bg-card-hover: #333333;
--bg-input: #2a2a2a;
// Borders
--border-color: #3a3a3a;
--border-light: #333333;
// Accent
--accent-primary: #e0e0e0;
--accent-hover: #f5f5f5;
--accent-muted: #888888;
// Text
--text-primary: #e0e0e0;
--text-secondary: #a0a0a0;
--text-muted: #888888;
// Status
--success: #66bb6a;
--error: #ef5350;
--warning: #ffb74d;
// Message bubbles
--msg-user-bg: #252525;
--msg-assistant-bg: #252525;
--msg-system-border: #555555;
// Code
--code-bg: #1e1e1e;
// Utility
--text-on-accent: #1a1a1a;
--text-on-overlay: #ffffff;
--accent-info: #6ba3d6;
// RGB components
--accent-primary-rgb: 224, 224, 224;
--accent-hover-rgb: 245, 245, 245;
--text-primary-rgb: 224, 224, 224;
--text-muted-rgb: 136, 136, 136;
--success-rgb: 102, 187, 106;
--error-rgb: 239, 83, 80;
--warning-rgb: 255, 183, 77;
--accent-info-rgb: 107, 163, 214;
}
.comic {
// Borders — bold comic outlines
--border-color: #1a1a1a;
--border-light: #555555;
--msg-system-border: #1a1a1a;
// Comic-specific
--font-ui: 'Comic Neue', 'ZCOOL KuaiLe', 'Zen Maru Gothic', 'Gaegu', cursive, sans-serif;
--comic-border-width: 2.5px;
--comic-shadow: 3px 3px 0px rgba(0, 0, 0, 0.15);
}
.dark.comic {
// Borders — brighter outlines on dark
--border-color: #666666;
--border-light: #555555;
--msg-system-border: #888888;
--comic-shadow: 3px 3px 0px rgba(255, 255, 255, 0.08);
// 霓虹青 oklch(0.805 0.148 188) ≈ rgb(45, 212, 191)
--accent-primary-rgb: 45, 212, 191;
--accent-hover-rgb: 74, 226, 205;
--text-primary-rgb: 240, 246, 247;
--text-muted-rgb: 168, 180, 187;
--success-rgb: 47, 197, 138;
--error-rgb: 232, 78, 61;
--warning-rgb: 226, 175, 32;
--accent-info-rgb: 105, 160, 232;
}
// ─── SCSS Variables (delegate to CSS custom properties) ────────
@@ -157,7 +89,7 @@ $text-muted: var(--text-muted);
$success: var(--success);
$error: var(--error);
$warning: var(--warning);
$info: $accent-primary;
$info: var(--accent-info);
// Message bubbles
$msg-user-bg: var(--msg-user-bg);
@@ -168,16 +100,19 @@ $msg-system-border: var(--msg-system-border);
$code-bg: var(--code-bg);
// Typography
$font-ui: var(--font-ui, 'Inter', system-ui, -apple-system, sans-serif);
$font-code: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
// 正文用系统无衬线,标识/数据/代码用等宽——这是 CYBER 的核心排版特征。
$font-ui: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB',
'Microsoft YaHei', 'Segoe UI', Roboto, sans-serif;
$font-code: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, 'Cascadia Code', monospace;
$font-display: $font-code;
// Layout
// Layout — 与 docs/ui-baseline.md 保持一致
$sidebar-width: 240px;
$sidebar-collapsed-width: 64px;
$header-height: 60px;
$breakpoint-mobile: 768px;
// Radius
// Radius — 发丝线 + 小圆角,硬边而非圆润
$radius-sm: 6px;
$radius-md: 8px;
$radius-lg: 8px;