chore: directory migration - gateway→server, web→client/electron

This commit is contained in:
2026-09-08 19:16:35 +08:00
parent fd9f99a3f9
commit f9d757b5e5
243 changed files with 5095 additions and 228 deletions

View File

@ -0,0 +1,184 @@
/** @type {import('tailwindcss').Config} */
/**
* 颜色走 CSS 变量而不是写死的十六进制。
*
* # 为什么不逐处加 dark: 前缀
*
* 全站约 700 处颜色用法散在 21 个组件里。逐个写 `bg-white dark:bg-gray-900`
* 有两个致命问题:漏一处就是深色下的白底白字(而且只有肉眼能发现),
* 以及此后每加一个组件都要记得写两遍 —— 那种约定活不过三次改动。
*
* # 为什么改调色板就够了
*
* 这套代码里灰阶**本身就是语义色阶**
* - `white` / `gray-50` / `gray-100` = 表面层次(卡片 / 页面底 / 悬停)
* - `gray-200` / `gray-300` = 分隔线
* - `gray-900` → `gray-400` = 文字主次
*
* 深色模式要做的正是把这条色阶**反转**white 变近黑、gray-900 变近白。
* 于是零组件改动就能整体切换,新组件照常写 `bg-white text-gray-900`
* 也自动适配 —— 不需要任何人记得任何约定。
*
* # 为什么是 `rgb(var(--x) / <alpha-value>)` 而不是直接存颜色串
*
* 代码里有 `bg-blue-50/70`、`bg-gray-50/60` 这样的透明度修饰符。
* 变量若存 `#f9fafb`Tailwind 生成的 `rgb(#f9fafb / 0.7)` 是无效 CSS
* 那些半透明高亮会静默失效(不报错,只是不透明)。存 RGB 三元组才行。
*/
const withAlpha = (v) => `rgb(var(${v}) / <alpha-value>)`;
const grayScale = {
50: withAlpha('--c-gray-50'),
100: withAlpha('--c-gray-100'),
200: withAlpha('--c-gray-200'),
300: withAlpha('--c-gray-300'),
400: withAlpha('--c-gray-400'),
500: withAlpha('--c-gray-500'),
600: withAlpha('--c-gray-600'),
700: withAlpha('--c-gray-700'),
800: withAlpha('--c-gray-800'),
900: withAlpha('--c-gray-900'),
950: withAlpha('--c-gray-950')
};
/**
* 强调色blue / red / green / amber / orange / yellow
*
* 这条色阶在这套代码里也是**语义色阶**,与灰阶同理:
* - `50` `300` = 表面chip 底、提示条底、徽标底、边框)
* - `400` `900` = 前景(文字、图标、实心按钮底)
*
* 深色模式下两段的走向**相反**:表面段要变暗(照搬浅色的近白值会在深色页面上
* 糊出一块刺眼亮斑),前景段要变亮(照搬浅色的 red-700 落在深色卡片上只有
* 2.67:1读不动。所以它必须走变量不能写死 —— 见 index.css 的两组定义。
*/
const accent = (name) => ({
50: withAlpha(`--c-${name}-50`),
100: withAlpha(`--c-${name}-100`),
200: withAlpha(`--c-${name}-200`),
300: withAlpha(`--c-${name}-300`),
400: withAlpha(`--c-${name}-400`),
500: withAlpha(`--c-${name}-500`),
600: withAlpha(`--c-${name}-600`),
700: withAlpha(`--c-${name}-700`),
800: withAlpha(`--c-${name}-800`),
900: withAlpha(`--c-${name}-900`)
});
/**
* 实心按钮/徽标的底色 —— `--s-*`,两种模式下**同值**。
*
* 为什么不能跟 accent 的前景段走:那一段在深色下被提亮成了浅色
* red-600 → rgb(246,141,141)),而 `bg-red-600 text-white` 的白字落上去
* 只有 1.6:1。实心按钮的底色本来就该保持饱和 —— 深色模式下变的是页面,
* 不是「危险操作按钮是红的」这件事。
*
* 只覆盖 backgroundColor`text-red-600` / `border-red-600` 仍走 accent()。
* Tailwind 的 backgroundColor 默认继承 colors这里逐档覆写 400900
* 50300 是表面段,仍从 colors 继承 —— 它们在深色下就该变暗)。
*/
const solid = (name) => ({
400: withAlpha(`--s-${name}-400`),
500: withAlpha(`--s-${name}-500`),
600: withAlpha(`--s-${name}-600`),
700: withAlpha(`--s-${name}-700`),
800: withAlpha(`--s-${name}-800`),
900: withAlpha(`--s-${name}-900`)
});
export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
// class 而不是 media主题要能被人显式选择。跟系统走是**默认值**
// 不是唯一选项 —— 白天开深色主题是常见偏好。
darkMode: 'class',
theme: {
extend: {
/**
* textColor 单独覆盖 white。
*
* `--c-white` 服务两种**互相冲突**的用途:
* - `bg-white` = 卡片表面 → 深色模式必须变暗
* - `text-white` = 彩色按钮上的文字 → 深色模式必须**保持浅色**
*
* 只有一个变量时后者跟着变暗,白字落在 `bg-chrome-700` 的激活导航项上
* 只剩 1.34:1 —— 几乎不可见(实测发现)。按钮底色在深色模式下依然是
* blue-600 那样的彩色,上面的文字本来就该是白的。
*
* Tailwind 的 textColor 默认继承 colors这里只改 white 一项,
* 其余gray/blue/...)仍走反转的色阶。
*/
textColor: {
white: withAlpha('--c-on-accent')
},
/**
* backgroundColor 单独覆盖强调色的 400900 档,让实心按钮底保持饱和。
*
* 每个强调色的 400900 在这套代码里承担**两个冲突用途**
* - `text-red-600` / `border-red-300` = 深色下必须**提亮**才读得动
* - `bg-red-600` + `text-white` = 深色下必须**保持饱和**
*
* 共用一档时后者必坏:深色下 red-600 被提亮到 rgb(246,141,141)
* 白字落上去只有 1.6:1 —— 与 text-white/bg-white 那次是同一类错误
* (一个名字服务两种语义)。
*
* 50300 不覆盖:那是表面段,深色下就该跟着变暗。
*/
backgroundColor: {
blue: solid('blue'),
red: solid('red'),
green: solid('green'),
amber: solid('amber'),
orange: solid('orange'),
yellow: solid('yellow')
},
colors: {
white: withAlpha('--c-white'),
gray: grayScale,
// slate 在这套代码里只用于登录页与少数深色块,与 gray 同源即可 ——
// 保留两个名字是为了不改那些组件,但它们指向同一条色阶。
slate: grayScale,
/**
* chrome —— 应用框架(侧栏 / 底部导航)的专用色阶。
*
* 为什么不能跟 gray 走:这两块**在浅色模式下本来就是深色的**
* (深色侧栏配浅色内容区是这套 UI 的原本设计)。把它们并入反转的
* gray 之后,深色模式下 `bg-slate-900` 变成了近白色 —— 侧栏比内容区
* 还亮,整个层次翻了过来(实测 rgb(243,245,248),而内容区是 rgb(17,19,24))。
*
* 独立成一条色阶后:浅色模式下它是深色框架,深色模式下**微调即可**
* (比内容区略深一点,保持"框架比内容更沉"的关系),两种模式下
* 语义一致。
*/
chrome: {
100: withAlpha('--c-chrome-100'),
200: withAlpha('--c-chrome-200'),
400: withAlpha('--c-chrome-400'),
600: withAlpha('--c-chrome-600'),
700: withAlpha('--c-chrome-700'),
800: withAlpha('--c-chrome-800'),
900: withAlpha('--c-chrome-900')
},
/*
* 强调色。表面段50300在深色下变暗、前景段400900变亮
* 两段走向相反 —— 详见 accent() 的注释与 index.css 的两组定义。
*
* 实心按钮底另走 --s-*(见上面的 backgroundColor
*
* 注意这里**只能有一份定义**JS 对象字面量的重复键后者胜出,
* 而那不会报错。上一版同时写了固定 hex 与 accent() 两份,
* accent() 覆盖了 hex 那份,但 index.css 里当时没有对应的 --c-red-* 等
* 变量 —— `rgb(var(--c-red-600) / 1)` 里的变量未定义使整条声明失效,
* 于是 bg-red-600 退回透明,白字落在白卡片上:按钮看不见但点得动。
*/
blue: accent('blue'),
red: accent('red'),
green: accent('green'),
amber: accent('amber'),
orange: accent('orange'),
yellow: accent('yellow')
}
}
},
plugins: []
};