cdrop — 跨 OS 剪贴板与文件传输服务
Commilitia Drop:自托管的跨设备剪贴板同步与点对点文件传输。 - 后端 Go(chi / SQLite WAL / SSE Hub / WebRTC signaling + 状态机 / Relay ring buffer),编译进单个 distroless 镜像(前端 go:embed)。 - 前端 React + TanStack Router + Zustand,自实现 SSE + WebRTC P2P,NAT 受阻时回退服务端中继;聚珍(Juzhen)CJK 综合排版。 - 桌面端 Wails v2(macOS / Windows),瘦客户端复用 web。 - 鉴权 OIDC PKCE(自建 Casdoor 等),refresh_token 信封加密存系统密钥库;iOS Shortcut 用 HS256 scoped token。 架构文档与变更记录见 docs 分支(PROJECT_BRIEF / FRONTEND_DESIGN / CHANGELOG)。 本次为公开发布初始提交:完整开发历史(含部署细节)留存于私有归档,公开仓库自此干净起步。
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Base 层 —— html / body / 通用排印 / 滚动条基线。所有具体颜色与字体
|
||||
* 通过语义令牌读取,不写硬编码值。
|
||||
*/
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after
|
||||
{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body
|
||||
{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 14px;
|
||||
line-height: var(--lh-body);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-feature-settings: "kern", "liga";
|
||||
|
||||
transition:
|
||||
background-color var(--dur-slow) var(--ease-in-out),
|
||||
color var(--dur-slow) var(--ease-in-out);
|
||||
}
|
||||
|
||||
#root
|
||||
{
|
||||
min-height: 100vh;
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
a
|
||||
{
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
transition: color var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
a:hover
|
||||
{
|
||||
color: color-mix(in srgb, var(--accent) 80%, var(--text));
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
::selection
|
||||
{
|
||||
background: var(--accent-soft);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre
|
||||
{
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
/* 简单的滚动条美化(仅 webkit)—— 使用 surface-sunken 与 divider,跟随主题。 */
|
||||
*::-webkit-scrollbar
|
||||
{
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track
|
||||
{
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb
|
||||
{
|
||||
background: var(--divider);
|
||||
border-radius: var(--radius-pill);
|
||||
border: 2px solid var(--bg);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb:hover
|
||||
{
|
||||
background: color-mix(in srgb, var(--divider) 70%, var(--text));
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* CJK 排版精修 —— 参照 ~/Library/CloudStorage/Dropbox/工具设置/Claude/Base/
|
||||
* templates/html/CJK-TYPOGRAPHY.md
|
||||
*
|
||||
* 仅在 <html lang="zh-*" | "ja" | "ko"> 时激活。:where(:lang(...)) 保持零
|
||||
* 特异性,组件层可直接覆盖,无需 !important。i18n.ts 已经把 locale 切到
|
||||
* documentElement.lang,这套规则自动跟随。
|
||||
*
|
||||
* 字重策略(项目约定,偏离上游 spec):
|
||||
* - Body Sans(Orbit Gothic CJK)保持 font-weight 400 —— Sans 在 400 不存在
|
||||
* 过细问题。上游 spec 把 body 提到 500 是针对 Noto Serif CJK 屏幕显示偏细
|
||||
* 的特定问题,本项目 body 走 Sans,不适用。
|
||||
* - .serif opt-in 类显式拉到 500 —— Noto Serif CJK 在 400 屏幕上确实偏细,
|
||||
* 这是 Serif 路径独有的修正,留给将来 hero / marketing 长段落使用。
|
||||
* - mono / code 锁 400,且 opt-out 所有 CJK 处理(autospace / trim / justify
|
||||
* 都关掉),保 byte 对齐与 URL / token 可读性。
|
||||
*/
|
||||
|
||||
/* ===== CJK lang root:挂行 / 换行 =====
|
||||
中西间隙、标点挤压、原生 text-autospace 关闭均由聚珍(juzhen.css 的 .juzhen
|
||||
normalize + JS pass)接管,此处不再设 text-spacing-trim / text-autospace,
|
||||
以免与聚珍托管挤压双重作用。仅保留两条段落级、与聚珍原子透明兼容的规则。 */
|
||||
:where(:lang(zh), :lang(ja), :lang(ko))
|
||||
{
|
||||
/* 闭合标点在行尾溢出挂起,首字符开口标点挂在左边界,改善光学齐边 */
|
||||
hanging-punctuation: first allow-end;
|
||||
|
||||
/* JIS X 4051 / clreq §3.1.2 禁则浏览器原生兜底(聚珍 jinze 为主) */
|
||||
line-break: strict;
|
||||
}
|
||||
|
||||
/* ===== 长英文段断词 ===== 聚珍(Juzhen)longWord pass 把 [A-Za-z]{6,} 包进
|
||||
<span lang="en">,这里给浏览器一个原生英文断词字典 + 渲染连字符。
|
||||
连字符仅由 CSS 渲染、不在 DOM,选取 / 复制返回原文无污染。原先 CJK
|
||||
lang root 的 overflow-wrap: anywhere 已被本规则替代:不再硬斩单词,
|
||||
也保留连字符提示。hyphenate-character 显式 ASCII "-" 避免 serif 字
|
||||
型把 U+2010 画成全角(本项目 body 走 Sans 暂不踩此坑,但与上游模板
|
||||
规范一致)。 */
|
||||
[lang|="en"]
|
||||
{
|
||||
-webkit-hyphens: auto;
|
||||
hyphens: auto;
|
||||
hyphenate-character: "-";
|
||||
hyphenate-limit-chars: 6 3 3; /* 词长 / 断点前 / 断点后最少字符 */
|
||||
}
|
||||
|
||||
/* ===== 默认两端对齐:多行块容器(段落 / 列表项 / 引用 / 单元格 / 说明)
|
||||
===== text-align-last: left 让段末与单行不被拉伸,只有真正的中间行受益。 */
|
||||
:where(:lang(zh), :lang(ja), :lang(ko))
|
||||
:is(p, li, blockquote, td, dd, summary, figcaption, caption,
|
||||
.justify, .justify *)
|
||||
{
|
||||
text-align: justify;
|
||||
text-align-last: left;
|
||||
}
|
||||
|
||||
/* ===== 显式 opt-out:容器及其子树都不走 justify ===== */
|
||||
:where(:lang(zh), :lang(ja), :lang(ko)) :is(.no-justify, .no-justify *)
|
||||
{
|
||||
text-align: start;
|
||||
text-justify: auto;
|
||||
text-align-last: auto;
|
||||
}
|
||||
|
||||
/* ===== 显式对齐工具类 =====
|
||||
.text-left / .text-center / .text-right 覆盖默认 justify + 传播到后代;
|
||||
主要用例:<th> / <td> / <caption> 的数值列右对齐 / 居中。
|
||||
规格 (0,1,0) 自身 + (0,1,0) 后代,压过默认规则 (0,0,1);
|
||||
text-justify / text-align-last 显式 auto,清掉残留 inter-character 拉伸。 */
|
||||
.text-left, .text-left * { text-align: left; text-justify: auto; text-align-last: auto; }
|
||||
.text-center, .text-center * { text-align: center; text-justify: auto; text-align-last: auto; }
|
||||
.text-right, .text-right * { text-align: right; text-justify: auto; text-align-last: auto; }
|
||||
|
||||
/* ===== Mono opt-out:byte 对齐,关掉所有 CJK 处理 ===== */
|
||||
code, kbd, pre, samp,
|
||||
.mono, .mono *
|
||||
{
|
||||
font-weight: 400;
|
||||
text-autospace: no-autospace;
|
||||
text-spacing-trim: space-all;
|
||||
text-align: start;
|
||||
text-justify: auto;
|
||||
text-align-last: auto;
|
||||
}
|
||||
|
||||
/* ===== Serif opt-in:Noto Serif CJK 在 400 屏幕偏细,锁定 500(Medium) ===== */
|
||||
.serif
|
||||
{
|
||||
font-family: var(--font-serif);
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 本地优先字体层 —— 用 local() 覆盖 CDN 的纯 url() @font-face。
|
||||
*
|
||||
* 问题:三个字体 CDN 的 @font-face 都只有 src: url(...)、无 local()。浏览器解析到
|
||||
* 这些 family 名(如 "Orbit Gothic CJK SC")时会被该 @font-face 捕获、直接走网络
|
||||
* url,即便本机已安装同名字体也不会用 → 首屏闪烁、无谓流量。
|
||||
*
|
||||
* 修法:在 CDN <link>(见 index.html)之后,对项目实际用到的 family × 字重重新声明
|
||||
* @font-face,src 把 local() 放最前、CDN url() 兜底。同 family + 同 weight/style 的
|
||||
* @font-face 以「后定义者胜」,cdrop 打包 CSS 在 index.html 静态 <link> 之后加载,故
|
||||
* 本层覆盖 CDN:本机命中即零网络瞬时渲染,未装则回退网络(swap)。
|
||||
*
|
||||
* 覆盖范围:仅项目用到的字重(400/500/600/700)× 实际激活的 region:
|
||||
* - Orbit Gothic CJK SC / TC(Sans 正文 + Mono 的 CJK 落字)
|
||||
* - Maple Mono(Mono 拉丁 / 符号)
|
||||
* Serif 的 "Noto Serif CJK SC" 是本地专属名(gfonts 只定义 "Noto Serif SC"、不含该
|
||||
* 名),字体栈里已排最前 → 本就本地优先,无需在此覆盖。日韩(JP/KR)region 暂未激活,
|
||||
* 启用时按同样 4 字重补充即可。
|
||||
*
|
||||
* local() 同时给 family 名与 PostScript 名两种写法以提高命中率;font-display: swap
|
||||
* 与 CDN 保持一致。
|
||||
*/
|
||||
|
||||
/* ===== Orbit Gothic CJK SC(默认 / 简体 Sans + Mono CJK)===== */
|
||||
@font-face
|
||||
{
|
||||
font-family: "Orbit Gothic CJK SC";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Orbit Gothic CJK SC"),
|
||||
local("OrbitGothicCJKsc-Regular"),
|
||||
url("https://orbit-gothic.cdn.commilitia.net/WOFF2/CJKsc/OrbitGothicCJKsc-Regular.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face
|
||||
{
|
||||
font-family: "Orbit Gothic CJK SC";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Orbit Gothic CJK SC"),
|
||||
local("OrbitGothicCJKsc-Medium"),
|
||||
url("https://orbit-gothic.cdn.commilitia.net/WOFF2/CJKsc/OrbitGothicCJKsc-Medium.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face
|
||||
{
|
||||
font-family: "Orbit Gothic CJK SC";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Orbit Gothic CJK SC"),
|
||||
local("OrbitGothicCJKsc-SemiBold"),
|
||||
url("https://orbit-gothic.cdn.commilitia.net/WOFF2/CJKsc/OrbitGothicCJKsc-SemiBold.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face
|
||||
{
|
||||
font-family: "Orbit Gothic CJK SC";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Orbit Gothic CJK SC"),
|
||||
local("OrbitGothicCJKsc-Bold"),
|
||||
url("https://orbit-gothic.cdn.commilitia.net/WOFF2/CJKsc/OrbitGothicCJKsc-Bold.woff2") format("woff2");
|
||||
}
|
||||
|
||||
/* ===== Orbit Gothic CJK TC(繁体 Sans + Mono CJK)===== */
|
||||
@font-face
|
||||
{
|
||||
font-family: "Orbit Gothic CJK TC";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Orbit Gothic CJK TC"),
|
||||
local("OrbitGothicCJKtc-Regular"),
|
||||
url("https://orbit-gothic.cdn.commilitia.net/WOFF2/CJKtc/OrbitGothicCJKtc-Regular.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face
|
||||
{
|
||||
font-family: "Orbit Gothic CJK TC";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Orbit Gothic CJK TC"),
|
||||
local("OrbitGothicCJKtc-Medium"),
|
||||
url("https://orbit-gothic.cdn.commilitia.net/WOFF2/CJKtc/OrbitGothicCJKtc-Medium.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face
|
||||
{
|
||||
font-family: "Orbit Gothic CJK TC";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Orbit Gothic CJK TC"),
|
||||
local("OrbitGothicCJKtc-SemiBold"),
|
||||
url("https://orbit-gothic.cdn.commilitia.net/WOFF2/CJKtc/OrbitGothicCJKtc-SemiBold.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face
|
||||
{
|
||||
font-family: "Orbit Gothic CJK TC";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Orbit Gothic CJK TC"),
|
||||
local("OrbitGothicCJKtc-Bold"),
|
||||
url("https://orbit-gothic.cdn.commilitia.net/WOFF2/CJKtc/OrbitGothicCJKtc-Bold.woff2") format("woff2");
|
||||
}
|
||||
|
||||
/* ===== Maple Mono(Mono 拉丁 / 符号)===== */
|
||||
@font-face
|
||||
{
|
||||
font-family: "Maple Mono";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Maple Mono"),
|
||||
local("MapleMono-Regular"),
|
||||
url("https://maple-mono.cdn.commilitia.net/MapleMono-Regular.ttf.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face
|
||||
{
|
||||
font-family: "Maple Mono";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Maple Mono"),
|
||||
local("MapleMono-Medium"),
|
||||
url("https://maple-mono.cdn.commilitia.net/MapleMono-Medium.ttf.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face
|
||||
{
|
||||
font-family: "Maple Mono";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Maple Mono"),
|
||||
local("MapleMono-SemiBold"),
|
||||
url("https://maple-mono.cdn.commilitia.net/MapleMono-SemiBold.ttf.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face
|
||||
{
|
||||
font-family: "Maple Mono";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src:
|
||||
local("Maple Mono"),
|
||||
local("MapleMono-Bold"),
|
||||
url("https://maple-mono.cdn.commilitia.net/MapleMono-Bold.ttf.woff2") format("woff2");
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 字体栈 —— 逐 region 明确化。
|
||||
*
|
||||
* 聚珍(Juzhen)2.0 不再内建逐语言字体级联,字体完全交消费端 CSS 决定。命名分两类,
|
||||
* 回退策略不同:
|
||||
*
|
||||
* - 含「CJK」的合一字体("Orbit Gothic CJK <R>"、"Noto Serif CJK <R>" /
|
||||
* Source Han):泛 CJK 全字覆盖,各区变体字符集相同、仅默认 glyph(locl)不同。
|
||||
* 故每族只列「当前区一个」即可——它已覆盖全部 CJK 字,且 lang + 该区字体触发
|
||||
* 正确 locl 地域字形,多列其他区无意义。这类多为用户机器上的本地字体,优先尝试。
|
||||
* - 不含「CJK」的区域子集("Noto Serif <R>",Google Fonts 命名,适合 Web 分发):
|
||||
* 每区只覆盖该区子集、字符集不全,故须跨区列全(当前区在前取 locl,余区补字),
|
||||
* 以并集逼近全字覆盖。次于本地合一字体尝试。
|
||||
*
|
||||
* 三族(<R> = SC/TC/JP/KR;:root 默认 = SC,兼英文 / 无 lang 回退):
|
||||
* - Sans :"Orbit Gothic CJK <R>" → 系统 sans
|
||||
* - Serif:"Noto Serif CJK <R>"(本地合一)→ "Noto Serif <R>" 跨区(Web 子集)→ 系统 serif
|
||||
* - Mono :"Maple Mono" → "Orbit Gothic CJK <R>" → 系统 mono
|
||||
*
|
||||
* "Noto Serif CJK <R>"(Adobe / fontconfig,同 Source Han Serif)与 "Noto Serif <R>"
|
||||
* (Google Fonts)命名不同:前者合一全覆盖、后者区域子集;拉丁正文回退 "Noto Serif"。
|
||||
*
|
||||
* 区域变体由 :lang() 切,<html lang="zh-CN|zh-TW|en-US"> 由 uiSlice.locale 同步
|
||||
* 写入;项目 lang 取自 i18n.Locale,故繁体走 :lang(zh-TW) 而非 :lang(zh-Hant),
|
||||
* 后续新增日韩沿 BCP47 区域 tag 添加即可。
|
||||
*
|
||||
* 字体文件 woff2 由远程 CDN 拉取(index.html 里 <link rel="stylesheet"> 引入
|
||||
* @font-face 声明):
|
||||
* - Orbit Gothic CJK → orbit-gothic.cdn.commilitia.net
|
||||
* - Maple Mono → maple-mono.cdn.commilitia.net
|
||||
* - Fredoka(字标) → gfonts.commilitia.net(同代理;--font-display 用)
|
||||
* - Noto Serif(子集) → gfonts.commilitia.net(Google Fonts 的 CF Worker 代理)
|
||||
*
|
||||
* 远程加载失败时按 fallback 链回退到系统字体,整体外观仍 OK。
|
||||
*/
|
||||
|
||||
:root
|
||||
{
|
||||
--font-sans:
|
||||
"Orbit Gothic CJK SC",
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
"PingFang SC",
|
||||
"Microsoft YaHei",
|
||||
"Hiragino Sans GB",
|
||||
sans-serif;
|
||||
|
||||
--font-serif:
|
||||
"Noto Serif CJK SC",
|
||||
"Source Han Serif SC",
|
||||
"Noto Serif SC",
|
||||
"Noto Serif TC",
|
||||
"Noto Serif JP",
|
||||
"Noto Serif KR",
|
||||
"Noto Serif",
|
||||
"Songti SC",
|
||||
Georgia,
|
||||
Cambria,
|
||||
"Times New Roman",
|
||||
serif;
|
||||
|
||||
--font-mono:
|
||||
"Maple Mono",
|
||||
"Orbit Gothic CJK SC",
|
||||
"JetBrains Mono",
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
Menlo,
|
||||
Consolas,
|
||||
monospace;
|
||||
|
||||
/* display 仅用于品牌字标("Commilitia Drop")与极少数大字号陈列。Fredoka 是
|
||||
品牌资产 README 指定的字标字体(圆体 geometric sans),经 gfonts.commilitia.net
|
||||
镜像加载;无 CJK 字形,CJK 字符按 var(--font-sans) 逐字回退(字标为纯拉丁,
|
||||
不触发)。远程失败时整链回退到 sans,外观仍 OK。 */
|
||||
--font-display:
|
||||
"Fredoka",
|
||||
var(--font-sans);
|
||||
}
|
||||
|
||||
/* ===== 繁体(TC)===== */
|
||||
:lang(zh-TW),
|
||||
:lang(zh-HK),
|
||||
:lang(zh-MO)
|
||||
{
|
||||
--font-sans:
|
||||
"Orbit Gothic CJK TC",
|
||||
system-ui,
|
||||
-apple-system,
|
||||
"PingFang TC",
|
||||
"Microsoft JhengHei",
|
||||
"Hiragino Sans CNS",
|
||||
sans-serif;
|
||||
|
||||
--font-serif:
|
||||
"Noto Serif CJK TC",
|
||||
"Source Han Serif TC",
|
||||
"Noto Serif TC",
|
||||
"Noto Serif SC",
|
||||
"Noto Serif JP",
|
||||
"Noto Serif KR",
|
||||
"Noto Serif",
|
||||
"Songti TC",
|
||||
Georgia,
|
||||
serif;
|
||||
|
||||
--font-mono:
|
||||
"Maple Mono",
|
||||
"Orbit Gothic CJK TC",
|
||||
"JetBrains Mono",
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
Menlo,
|
||||
Consolas,
|
||||
monospace;
|
||||
}
|
||||
|
||||
/* ===== 日文(JP)===== */
|
||||
:lang(ja)
|
||||
{
|
||||
--font-sans:
|
||||
"Orbit Gothic CJK JP",
|
||||
system-ui,
|
||||
-apple-system,
|
||||
"Hiragino Sans",
|
||||
"Yu Gothic",
|
||||
"Meiryo",
|
||||
sans-serif;
|
||||
|
||||
--font-serif:
|
||||
"Noto Serif CJK JP",
|
||||
"Source Han Serif",
|
||||
"Noto Serif JP",
|
||||
"Noto Serif SC",
|
||||
"Noto Serif TC",
|
||||
"Noto Serif KR",
|
||||
"Noto Serif",
|
||||
"Yu Mincho",
|
||||
Georgia,
|
||||
serif;
|
||||
|
||||
--font-mono:
|
||||
"Maple Mono",
|
||||
"Orbit Gothic CJK JP",
|
||||
"JetBrains Mono",
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
Menlo,
|
||||
Consolas,
|
||||
monospace;
|
||||
}
|
||||
|
||||
/* ===== 韩文(KR)===== */
|
||||
:lang(ko)
|
||||
{
|
||||
--font-sans:
|
||||
"Orbit Gothic CJK KR",
|
||||
system-ui,
|
||||
-apple-system,
|
||||
"Apple SD Gothic Neo",
|
||||
"Malgun Gothic",
|
||||
sans-serif;
|
||||
|
||||
--font-serif:
|
||||
"Noto Serif CJK KR",
|
||||
"Source Han Serif KR",
|
||||
"Noto Serif KR",
|
||||
"Noto Serif SC",
|
||||
"Noto Serif TC",
|
||||
"Noto Serif JP",
|
||||
"Noto Serif",
|
||||
Georgia,
|
||||
serif;
|
||||
|
||||
--font-mono:
|
||||
"Maple Mono",
|
||||
"Orbit Gothic CJK KR",
|
||||
"JetBrains Mono",
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
Menlo,
|
||||
Consolas,
|
||||
monospace;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 全局样式入口。被 main.tsx 单点 import。
|
||||
*
|
||||
* 顺序约定:
|
||||
* 1. tokens.primitives 原始常量(颜色 / 字号 / 间距 / 时长)
|
||||
* 2. tokens.semantic 主题相关语义变量(--bg / --accent / ...)
|
||||
* 3. fonts 字体族变量与 :lang() 切换
|
||||
* 4. fontface 本地优先 @font-face 覆盖层(local() shadow CDN 的纯 url)
|
||||
* 5. base html/body 与通用排印基线
|
||||
* 6. juzhen 聚珍套件视觉尺寸(中西间隙 / 标点挤压 / justify 原子,必配)
|
||||
* 7. cjk CJK 排版精修(项目层覆盖:justify 选择器 / mono opt-out)
|
||||
* 8. motion 动效 utility 与 reduced-motion
|
||||
* 9. tailwind 最后,让 utilities 可覆盖 base
|
||||
*/
|
||||
|
||||
@import "./tokens.primitives.css";
|
||||
@import "./tokens.semantic.css";
|
||||
@import "./fonts.css";
|
||||
@import "./fontface.css";
|
||||
@import "./base.css";
|
||||
@import "../lib/cjk-autospace/dist/juzhen.css";
|
||||
@import "./cjk.css";
|
||||
@import "./motion.css";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Motion 层 —— 通用过渡 utility 与 reduced-motion 兜底。
|
||||
*/
|
||||
|
||||
.transition-base
|
||||
{
|
||||
transition:
|
||||
background-color var(--dur-base) var(--ease-out),
|
||||
border-color var(--dur-base) var(--ease-out),
|
||||
color var(--dur-base) var(--ease-out),
|
||||
box-shadow var(--dur-base) var(--ease-out);
|
||||
}
|
||||
|
||||
.transition-fast
|
||||
{
|
||||
transition:
|
||||
background-color var(--dur-fast) var(--ease-out),
|
||||
border-color var(--dur-fast) var(--ease-out),
|
||||
color var(--dur-fast) var(--ease-out),
|
||||
box-shadow var(--dur-fast) var(--ease-out),
|
||||
transform var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce)
|
||||
{
|
||||
*,
|
||||
*::before,
|
||||
*::after
|
||||
{
|
||||
animation-duration: 0.001ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.001ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Primitives 层 —— 原始令牌。来自 Dracula Pro 官方调色板与设计稿基础尺度。
|
||||
*
|
||||
* 组件不直接消费这一层。主题切换不动这一层。新增主题 = 在 semantic 层把
|
||||
* 这里的原始值映射给语义变量。
|
||||
*/
|
||||
|
||||
:root
|
||||
{
|
||||
/* ---- Dracula Pro · Base 色板 ---- */
|
||||
--c-purple-500: #9580FF; /* Base Purple — 大块面积/按钮背景适用 */
|
||||
--c-purple-400: #AA99FF; /* Base BrightBlue — Purple 的官方亮版,适合
|
||||
深色背景上的文字 / 链接,对比度 6.7:1 */
|
||||
--c-purple-700: #644AC9; /* Alucard Purple — 亮色主题 accent */
|
||||
--c-pink: #FF80BF; /* 扩展色 */
|
||||
--c-pink-deep: #A3144D;
|
||||
--c-red: #FF9580; /* dark · state-error */
|
||||
--c-red-deep: #CB3A2A; /* light · state-error */
|
||||
--c-orange: #FFCA80; /* dark · state-warn */
|
||||
--c-orange-deep: #A34D14; /* light · state-warn */
|
||||
--c-yellow: #FFFF80;
|
||||
--c-yellow-deep: #846E15;
|
||||
--c-green: #8AFF80; /* dark · state-online */
|
||||
--c-green-deep: #14710A; /* light · state-online */
|
||||
--c-cyan: #80FFEA;
|
||||
--c-cyan-deep: #036A96;
|
||||
|
||||
/* ---- Dracula Pro · 文字 / Comment(色 token,非表面) ---- */
|
||||
/* Dracula 官方深色 bg / surface 偏 250° 紫调,在紫色 accent 上对比度
|
||||
塌缩到 ~4.2:1。本项目改走「中性灰表面 + Dracula 色作 accent / 角色色」,
|
||||
表面色单独抽到 c-bg-dark-* 系列,这里只保留 foreground 与 comment。 */
|
||||
--c-comment-dracula: #7970A9; /* official Comment —— 仅用于"非常 muted"
|
||||
的场景(例如 hover-only 提示);作为
|
||||
UI 次要文字色对比度只有 4.3:1,临界。 */
|
||||
--c-comment-bright: #A89DD3; /* Comment 的官方派生亮版(同色相 250°,
|
||||
饱和度 35% 明度 72%),用于 UI muted 文字,
|
||||
对比度 ~7:1 (AAA Large + AA Normal)。 */
|
||||
--c-foreground-dracula: #F8F8F2; /* official Foreground */
|
||||
--c-foreground-alucard: #1F1F1F; /* official Foreground */
|
||||
--c-comment-alucard: #635D97; /* official Comment */
|
||||
|
||||
/* ---- 中性灰深色 5 阶 surface(去紫调) ----
|
||||
目标:让紫色 accent 在背景上对比度达 7:1+,避免 Dracula 官方紫底紫前景
|
||||
塌缩。色相 H≈260°(略冷,与紫色 accent 同冷调系)、饱和度 8-12% 极低、
|
||||
明度 9-22% 阶梯递进。 */
|
||||
--c-bg-dark-neutral: #17171C; /* 页面底,最深 */
|
||||
--c-sunken-dark-neutral: #1E1E24; /* 下沉(剪贴板 sunken / dropzone 底) */
|
||||
--c-surface-dark-neutral: #26262E; /* 默认 panel */
|
||||
--c-raised-dark-neutral: #2E2E37; /* hover / 主 panel */
|
||||
--c-elev2-dark-neutral: #37373F; /* 双层 elev(对话框 / 弹层) */
|
||||
--c-divider-dark-neutral: #3C3C45;
|
||||
--c-divider-strong-dark: #54545F;
|
||||
--c-text-muted-dark-neutral: #9C9CA9; /* 比 Comment 紫调更冷的中性 muted */
|
||||
|
||||
/* ---- 中性灰浅色 5 阶 surface(去紫调) ----
|
||||
原 Alucard #F5F5F5/#FFFFFF/#EAEAEA 已基本中性,这里微调成更冷一档,
|
||||
并显式拉出 5 阶,让 hover 的 surface→raised 有视觉差。
|
||||
2026-05-16 调整:bg 与 sunken 各自再深一档,把卡片"浮"起来,
|
||||
让默认白色 panel 从页面背景里明显分层(原 #F2F2F4 太接近白)。 */
|
||||
--c-bg-light-neutral: #E9E8EE; /* 页面底,更冷更深一档,与白卡形成清晰 ~9% 亮度差 */
|
||||
--c-sunken-light-neutral: #DDDCE3; /* 下沉表面,比 bg 再深一档 */
|
||||
--c-surface-light-neutral: #FAFAFB; /* 默认 panel:几乎纯白 */
|
||||
--c-raised-light-neutral: #FFFFFF; /* hover / 主 panel */
|
||||
--c-elev2-light-neutral: #FFFFFF; /* 双层(primary panel) */
|
||||
--c-divider-light-neutral: #D6D5DC;
|
||||
--c-divider-strong-light: #B5B4BD;
|
||||
--c-text-muted-light-neutral: #5B5A6E; /* 比 #635D97 略冷,饱和度更低 */
|
||||
|
||||
/* ---- 原 Dracula / Alucard 表面变量保留作 fallback,目前不消费 ---- */
|
||||
--c-bg-dracula: var(--c-bg-dark-neutral);
|
||||
--c-surface-dracula: var(--c-surface-dark-neutral);
|
||||
--c-raised-dracula: var(--c-raised-dark-neutral);
|
||||
--c-sunken-dracula: var(--c-sunken-dark-neutral);
|
||||
--c-selection-dracula: #454158; /* official Selection,仅 ::selection 还要 */
|
||||
|
||||
--c-bg-alucard: var(--c-bg-light-neutral);
|
||||
--c-surface-alucard: var(--c-surface-light-neutral);
|
||||
--c-raised-alucard: var(--c-raised-light-neutral);
|
||||
--c-sunken-alucard: var(--c-sunken-light-neutral);
|
||||
--c-selection-alucard: #CFCFDE;
|
||||
|
||||
/* ---- 字号阶梯 ---- */
|
||||
--fs-11: 11px;
|
||||
--fs-12: 12px;
|
||||
--fs-13: 13px;
|
||||
--fs-15: 15px;
|
||||
--fs-18: 18px;
|
||||
--fs-22: 22px;
|
||||
--fs-28: 28px;
|
||||
|
||||
/* ---- 行高 ---- */
|
||||
--lh-body: 1.6; /* 正文 / 多行块 */
|
||||
--lh-ui: 1.4; /* 按钮 / 单行 UI */
|
||||
--lh-title: 1.2; /* 标题 */
|
||||
|
||||
/* ---- 字距 ---- */
|
||||
--ls-brand: 0.06em;
|
||||
--ls-label: 0.08em;
|
||||
|
||||
/* ---- 间距阶梯(基 4)---- */
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 24px;
|
||||
--space-6: 32px;
|
||||
--space-7: 48px;
|
||||
--space-8: 64px;
|
||||
|
||||
/* ---- 圆角 ---- */
|
||||
--radius-card: 10px;
|
||||
--radius-control: 6px;
|
||||
--radius-pill: 999px;
|
||||
|
||||
/* ---- 动效 ---- */
|
||||
--dur-fast: 120ms;
|
||||
--dur-base: 220ms;
|
||||
--dur-slow: 320ms;
|
||||
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Semantic 层 —— 语义令牌。组件唯一应该消费的颜色 / 阴影变量。
|
||||
*
|
||||
* 主题切换 = 改一个 attribute:document.documentElement.dataset.theme = "dark"。
|
||||
* 自定义主题 = 在 :root[data-theme="custom-x"] 下覆盖同一组变量。
|
||||
*
|
||||
* 系统主题:未设 data-theme 时,跟随 @media (prefers-color-scheme: dark)。
|
||||
*
|
||||
* 2026-05 重塑:
|
||||
* - 表面切到中性灰 5 阶(去紫调),accent 仍是 Dracula Purple。
|
||||
* - 引入 5 色角色:info / success / warn / error / emphasis + 各自 soft 派生。
|
||||
* - 状态编码统一走角色色(Activity 左色条、Badge tone、Callout 等)。
|
||||
*/
|
||||
|
||||
/* ===== Light · 中性灰 + Alucard accent / 角色色(默认) ===== */
|
||||
:root,
|
||||
:root[data-theme="light"]
|
||||
{
|
||||
--bg: var(--c-bg-light-neutral);
|
||||
--surface: var(--c-surface-light-neutral);
|
||||
--surface-raised: var(--c-raised-light-neutral);
|
||||
--surface-sunken: var(--c-sunken-light-neutral);
|
||||
--surface-elev2: var(--c-elev2-light-neutral);
|
||||
--divider: var(--c-divider-light-neutral);
|
||||
--divider-strong: var(--c-divider-strong-light);
|
||||
|
||||
--text: var(--c-foreground-alucard);
|
||||
--text-muted: var(--c-text-muted-light-neutral);
|
||||
|
||||
--accent: var(--c-purple-700);
|
||||
--accent-soft: color-mix(in srgb, var(--accent) 10%, transparent);
|
||||
--accent-softer: color-mix(in srgb, var(--accent) 5%, transparent);
|
||||
--accent-ring: color-mix(in srgb, var(--accent) 26%, transparent);
|
||||
--accent-on: #FFFFFF;
|
||||
|
||||
/* 5 色角色 —— Alucard 调色板深版,作 status 编码主力 */
|
||||
--info: var(--c-cyan-deep); /* #036A96 */
|
||||
--success: var(--c-green-deep); /* #14710A */
|
||||
--warn: var(--c-yellow-deep); /* #846E15 */
|
||||
--error: var(--c-red-deep); /* #CB3A2A */
|
||||
--emphasis: var(--c-pink-deep); /* #A3144D */
|
||||
--orange: var(--c-orange-deep); /* #A34D14,备用 */
|
||||
|
||||
--info-soft: color-mix(in srgb, var(--info) 9%, transparent);
|
||||
--success-soft: color-mix(in srgb, var(--success) 9%, transparent);
|
||||
--warn-soft: color-mix(in srgb, var(--warn) 11%, transparent);
|
||||
--error-soft: color-mix(in srgb, var(--error) 9%, transparent);
|
||||
--emphasis-soft: color-mix(in srgb, var(--emphasis) 9%, transparent);
|
||||
|
||||
/* state-* 是历史 alias,保留兼容现有组件(StateDot 等) */
|
||||
--state-online: var(--success);
|
||||
--state-error: var(--error);
|
||||
--state-warn: var(--warn);
|
||||
|
||||
--shadow-card: 0 1px 2px rgba(17, 17, 20, 0.05),
|
||||
0 2px 6px rgba(17, 17, 20, 0.07);
|
||||
--shadow-raised: 0 8px 20px rgba(17, 17, 20, 0.10),
|
||||
0 2px 6px rgba(17, 17, 20, 0.06);
|
||||
--shadow-overlay: 0 12px 32px rgba(17, 17, 20, 0.18);
|
||||
--shadow-accent: 0 8px 22px rgba(100, 74, 201, 0.24);
|
||||
|
||||
/* 字母头像:浅色主题 白底黑字 */
|
||||
--avatar-bg: #FFFFFF;
|
||||
--avatar-fg: #1F1F1F;
|
||||
|
||||
/* 颜色阶 —— Mantine accent 色板用,10 阶手工对齐 Dracula Purple */
|
||||
--accent-0: #F3F0FF;
|
||||
--accent-1: #E4DEFF;
|
||||
--accent-2: #C9BEFF;
|
||||
--accent-3: #AC9BFE;
|
||||
--accent-4: #9580FF;
|
||||
--accent-5: #8369FB;
|
||||
--accent-6: #7458EE;
|
||||
--accent-7: #644AC9;
|
||||
--accent-8: #523CA8;
|
||||
--accent-9: #3F2D85;
|
||||
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
/* ===== Dark · 中性灰 + Dracula accent / 角色色 =====
|
||||
*
|
||||
* 对比度准则:
|
||||
* - 表面用 c-*-dark-neutral 5 阶,色相 260° 但饱和度 8%,接近中性灰。
|
||||
* 紫色 accent (#AA99FF) 在 #26262E 上对比度 ~8.5:1,胜过原 Dracula 紫底紫前景的 4.2:1。
|
||||
* - --text-muted 用更冷的中性 #9C9CA9,而非 Comment 紫调,避免在中性灰上仍透紫感。
|
||||
* - --accent 用 BrightBlue #AA99FF (Dracula PRO Base 官方"亮紫"),
|
||||
* 按钮背景仍可用,accent-on 是 #1B1A23 强反差。
|
||||
*/
|
||||
:root[data-theme="dark"]
|
||||
{
|
||||
--bg: var(--c-bg-dark-neutral);
|
||||
--surface: var(--c-surface-dark-neutral);
|
||||
--surface-raised: var(--c-raised-dark-neutral);
|
||||
--surface-sunken: var(--c-sunken-dark-neutral);
|
||||
--surface-elev2: var(--c-elev2-dark-neutral);
|
||||
--divider: var(--c-divider-dark-neutral);
|
||||
--divider-strong: var(--c-divider-strong-dark);
|
||||
|
||||
--text: var(--c-foreground-dracula);
|
||||
--text-muted: var(--c-text-muted-dark-neutral);
|
||||
|
||||
--accent: var(--c-purple-400);
|
||||
--accent-soft: color-mix(in srgb, var(--accent) 20%, transparent);
|
||||
--accent-softer: color-mix(in srgb, var(--accent) 10%, transparent);
|
||||
--accent-ring: color-mix(in srgb, var(--accent) 36%, transparent);
|
||||
--accent-on: #1B1A23;
|
||||
|
||||
/* 5 色角色 —— Dracula Pro neon,中性背景下饱和度足够 */
|
||||
--info: var(--c-cyan); /* #80FFEA */
|
||||
--success: var(--c-green); /* #8AFF80 */
|
||||
--warn: var(--c-yellow); /* #FFFF80 */
|
||||
--error: var(--c-red); /* #FF9580 */
|
||||
--emphasis: var(--c-pink); /* #FF80BF */
|
||||
--orange: var(--c-orange); /* #FFCA80 */
|
||||
|
||||
--info-soft: color-mix(in srgb, var(--info) 14%, transparent);
|
||||
--success-soft: color-mix(in srgb, var(--success) 14%, transparent);
|
||||
--warn-soft: color-mix(in srgb, var(--warn) 14%, transparent);
|
||||
--error-soft: color-mix(in srgb, var(--error) 14%, transparent);
|
||||
--emphasis-soft: color-mix(in srgb, var(--emphasis) 14%, transparent);
|
||||
|
||||
--state-online: var(--success);
|
||||
--state-error: var(--error);
|
||||
--state-warn: var(--warn);
|
||||
|
||||
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.45),
|
||||
0 4px 12px rgba(0, 0, 0, 0.5);
|
||||
--shadow-raised: 0 12px 28px rgba(0, 0, 0, 0.55),
|
||||
0 2px 6px rgba(0, 0, 0, 0.4);
|
||||
--shadow-overlay: 0 16px 40px rgba(0, 0, 0, 0.65);
|
||||
--shadow-accent: 0 10px 28px rgba(170, 153, 255, 0.34);
|
||||
|
||||
--avatar-bg: #000000;
|
||||
--avatar-fg: #FFFFFF;
|
||||
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/* ===== System:未指定 data-theme 时跟随系统 ===== */
|
||||
@media (prefers-color-scheme: dark)
|
||||
{
|
||||
:root:not([data-theme])
|
||||
{
|
||||
--bg: var(--c-bg-dark-neutral);
|
||||
--surface: var(--c-surface-dark-neutral);
|
||||
--surface-raised: var(--c-raised-dark-neutral);
|
||||
--surface-sunken: var(--c-sunken-dark-neutral);
|
||||
--surface-elev2: var(--c-elev2-dark-neutral);
|
||||
--divider: var(--c-divider-dark-neutral);
|
||||
--divider-strong: var(--c-divider-strong-dark);
|
||||
|
||||
--text: var(--c-foreground-dracula);
|
||||
--text-muted: var(--c-text-muted-dark-neutral);
|
||||
|
||||
--accent: var(--c-purple-400);
|
||||
--accent-soft: color-mix(in srgb, var(--accent) 20%, transparent);
|
||||
--accent-softer: color-mix(in srgb, var(--accent) 10%, transparent);
|
||||
--accent-ring: color-mix(in srgb, var(--accent) 36%, transparent);
|
||||
--accent-on: #1B1A23;
|
||||
|
||||
--info: var(--c-cyan);
|
||||
--success: var(--c-green);
|
||||
--warn: var(--c-yellow);
|
||||
--error: var(--c-red);
|
||||
--emphasis: var(--c-pink);
|
||||
--orange: var(--c-orange);
|
||||
|
||||
--info-soft: color-mix(in srgb, var(--info) 14%, transparent);
|
||||
--success-soft: color-mix(in srgb, var(--success) 14%, transparent);
|
||||
--warn-soft: color-mix(in srgb, var(--warn) 14%, transparent);
|
||||
--error-soft: color-mix(in srgb, var(--error) 14%, transparent);
|
||||
--emphasis-soft: color-mix(in srgb, var(--emphasis) 14%, transparent);
|
||||
|
||||
--state-online: var(--success);
|
||||
--state-error: var(--error);
|
||||
--state-warn: var(--warn);
|
||||
|
||||
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.45),
|
||||
0 4px 12px rgba(0, 0, 0, 0.5);
|
||||
--shadow-raised: 0 12px 28px rgba(0, 0, 0, 0.55),
|
||||
0 2px 6px rgba(0, 0, 0, 0.4);
|
||||
--shadow-overlay: 0 16px 40px rgba(0, 0, 0, 0.65);
|
||||
--shadow-accent: 0 10px 28px rgba(170, 153, 255, 0.34);
|
||||
|
||||
--avatar-bg: #000000;
|
||||
--avatar-fg: #FFFFFF;
|
||||
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user