feat(theme): 改版为清晰蓝卡片工作台并接管 UI 库尺度
All checks were successful
Build and Deploy (tool.xpcool.com) / build-and-deploy (push) Successful in 1m10s
All checks were successful
Build and Deploy (tool.xpcool.com) / build-and-deploy (push) Successful in 1m10s
主色青玉改宝石蓝,字号整体放大一档(正文 16px), 通过 --td-comp-* 与 --td-font-* 接管 TDesign 控件高度与文字, 修复 theme-mode 选择器权重不足导致深色变量被 UI 库覆盖的问题。
This commit is contained in:
parent
af233e1628
commit
dd38aeaf38
@ -1,6 +1,12 @@
|
||||
# tool.xpcool.com 变更记录
|
||||
> 倒序:最新在上。格式:YYYY-MM-DD | 类型 | 摘要
|
||||
|
||||
2026-09-14 | FIX | 修复主题失效的头号坑:TDesign 浅/深色变量声明在 :root[theme-mode='light'|'dark'](权重 0,2,0),我们此前用 html[theme-mode='dark'](0,1,1),生产构建下被 UI 库深色变量反向覆盖(卡片退回 #242424、输入框描边退回 #ddd)。tokens.scss 与 tdesign-theme.scss 全部改用 :root[theme-mode=...];另记录验证方法:切主题后 getComputedStyle(body).backgroundColor 可能命中计算值缓存,需用探针元素或刷新后再读
|
||||
|
||||
2026-09-14 | CHG | 界面改版第二版「清晰蓝 · 卡片工作台」(解决「字体/下拉/控件太小」+ 补上 UI 库主题控制):① tokens.scss:主色青玉 #0c7c59 → 宝石蓝 #2563eb、中性色调改蓝灰(浅 f4f6fb/fff/0f1729,深 0b0f18/131926/e9eef8)、圆角放大一档(xs5/sm7/md10/lg12/xl16/2xl22)、字号整体上移一档(正文 14→16px、最小 13px、标题阶梯同步)、密度收敛(space-9 80→68、版心 1120→1200、顶栏 64→68、侧栏 248→268)、卡片由「无框发丝线」改回「白底 + 细描边」;② tdesign-theme.scss:品牌/中性/语义色阶换蓝体系 + 深色反向中性色阶,新增「UI 库尺度控制」段(--td-comp-size-m 32→40 即默认输入框/按钮/下拉高度、xl 40→48、xs/xxs/xxxs 24/20/16→28/24/18、--td-font-size-body-medium 14→16px、body-small 12→13px、title 14/16/18→16/18/20px、行高成对放宽、paddingTB/LR 放宽一档),控件尺度只改变量不再写死高度;③ 27 个 .vue 里残留的 11/12px 硬编码字号提升为 var(--tk-text-xs);④ 外壳与组件跟随:MainLayout(顶栏 68、命令入口 44、内容留白 32/48/72、抽屉 300)、SideNav(筛选框 42、导航项 padding 9/12、图标 19)、HomeView(hero 52、搜索底线 60、索引行 20、服务端卡片 38px 图标)、CommandPalette(搜索行 64、结果项 58、kbd 22)、UploadDrop(缩略图 56、删除热区 36)、AppBrand 36、ThemeSwitch/LocaleSwitch 40、HistoryPanel 条目 48;⑤ index.html 的 theme-color 与 unocss 的 panel/code-block/hint-text 同步。构建通过(vue-tsc + vite,22.86s)
|
||||
|
||||
2026-09-14 | CHG | 界面改版为「更松弛的编辑感」(design-taste-frontend 技能,DESIGN_VARIANCE 6 / MOTION 3 / DENSITY 3):① tokens.scss 新增发丝线 --tk-hairline、排版令牌(--tk-leading-*/-track-*、--tk-text-5xl、--tk-font-display、--tk-space-9=80px)、顶栏高 60→64,并移除未用的 --tk-track-caps;② 全局排版:page-title 改 clamp(26,3vw,34)+640 字重、page-desc 15px/1.75、tool-section 间距 16→32、滚动条 10→8px、reveal 位移 14→10px;③ 容器去框化:t-card / UnoCSS panel 改 2xl 圆角 + hairline 描边 + 大内边距(.t-card .t-card__body 24/32,窄屏收回),品牌图标去掉全站唯一渐变改实色方块;④ 首页重写:删掉 eyebrow 与重复的 hero 链接,标题 58px 左对齐 + 底线式搜索框(聚焦加粗为 2px),事实条去边框,58 个工具改为「发丝线索引行」(图标常态中性灰、hover 才上主色);⑤ 外壳:侧栏去底色与右侧竖线(hover 改用 --tk-surface 统一「抬起」方向)、筛选框改底线式、内容区留白 28→40/48/88、页脚改左右分列 + hairline;⑥ HistoryPanel 改用 --tk-* 令牌与发丝线分行(此前有硬编码 --td-* 与 6px 圆角);⑦ 清理 4 份语言包中已失效的 home.eyebrow/ctaPrimary/ctaSecondary。构建通过(vue-tsc + vite,32.17s)
|
||||
|
||||
2026-09-14 | CHG | 视觉重设计 + 中英日韩四语言 + 三态昼夜主题 + 服务端接口对接(一大批):① 新增 tokens.scss(--tk-* 设计令牌:青玉 Jade 单一强调色、圆角/间距/字号/z-index/动效规范、按语言切换字体栈)+ tdesign-theme.scss(--tk-* → --td-* 桥接,含完整冷调灰阶),60+ 未改动工具页零成本继承新配色;② vue-i18n 11.4.10 + zh-CN/en-US/ja-JP/ko-KR 四份完整语言包 + helpers 中文兜底;③ MainLayout/SideNav/HomeView 重写、新增 CommandPalette(⌘K)/LocaleSwitch(显示 ZH/EN/JA/KO 不用国旗)/ThemeSwitch(三态);④ 新增 openTools.ts 对接 service.xpcool.com /api/service/open/tools 的 5 个接口(实测全 200),改造 ip-info/uuid/hash/timestamp 并新增 random-string/api-status 两工具(均本机+服务端双模式,默认本机);⑤ 修复空分类渲染缺陷(分类 9→6)、panel 快捷类缺内边距、IpInfoView 漏 onBeforeUnmount;⑥ 新增 src/config.ts 集中站点常量,页脚补备案号 黔ICP备2026003933号;⑦ 重写 docs/api-contract.md(旧契约 /api/tools/* 与实际不符)。构建通过(vue-tsc + vite,33.88s)
|
||||
|
||||
2026-09-01 | CHG | 全站接入图片预览(TDesign ImageViewer):新增共享 AppImageViewer 组件 + useImageViewer 组合式函数;接入 9 处——UploadDrop 缩略图、Base64 还原图、图片裁剪结果、图片转 ICO 预览(多尺寸成组)、OCR 待识别图、二维码结果、图片压缩(原直连 t-image-viewer 重构为共享组件)、代码转图片画布、占位图画布;全局样式新增 .img-previewable
|
||||
|
||||
12
agents.md
12
agents.md
@ -77,11 +77,19 @@ export interface ToolItem {
|
||||
- 文本类工具:`text` 存输入原文,点击历史回填输入框。
|
||||
- 文件类工具(图片压缩/裁剪等):文件本体**不要**存入 localStorage(会撑爆配额),改为存「文件名 | 关键参数」形式的摘要,点击历史只恢复参数。
|
||||
|
||||
### 3.3 主题机制
|
||||
### 3.3 主题机制(色彩 + UI 库尺度两套变量)
|
||||
|
||||
- TDesign 通过 `document.documentElement.setAttribute('theme-mode', 'dark')` 切换暗黑模式。
|
||||
- `stores/theme.ts` 负责切换 + 持久化(key:`tool-theme`);`index.html` 内联脚本在挂载前预置属性防闪烁;`main.ts` 再同步一次。
|
||||
- **自定义样式一律使用 `var(--td-*)` 令牌**(如 `--td-bg-color-page`、`--td-text-color-primary`、`--td-brand-color`),禁止硬编码颜色,否则暗黑模式会破版。
|
||||
- **两套变量各管一段**:
|
||||
- `src/assets/styles/tokens.scss` 定义 `--tk-*`(配色 / 圆角 / 间距 / **字号** / 动效),是全站唯一真相源;
|
||||
- `src/assets/styles/tdesign-theme.scss` 把 `--tk-*` 映射为 `--td-*`,并**额外接管 UI 库自身的尺度**:
|
||||
`--td-comp-size-*`(控件高度,默认控件 40px)、`--td-comp-paddingTB/LR-*`、`--td-font-size-*`、
|
||||
`--td-line-height-*`。放大按钮 / 输入框 / 下拉请改这里,**不要给单个组件写死 height**。
|
||||
- **自定义样式一律使用 `var(--tk-*)`(或 `var(--td-*)`)令牌**,禁止硬编码颜色与字号,否则暗黑模式会破版。
|
||||
- **头号坑**:主题变量必须声明在 `:root[theme-mode='light'|'dark']` 里,**不能写 `html[theme-mode='dark']`** ——
|
||||
TDesign 自己用的是前者(权重 0,2,0 且后加载者胜),写成后者(0,1,1)会被 UI 库的深色变量盖掉,
|
||||
表现为「深色下组件退回 UI 库自带灰(卡片 #242424、描边 #ddd)」。
|
||||
|
||||
### 3.4 公共组件
|
||||
|
||||
|
||||
@ -5,6 +5,115 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-14 第八批:修「字太小」+ 换风格为「清晰蓝 · 卡片工作台」+ 补上 UI 库主题控制
|
||||
|
||||
> 用户反馈:① 界面字体、下拉与功能控件都太小;② 再换一种风格;③ UI 库(TDesign)的主题此前没有真正接上,
|
||||
> 需要用主题变量控制组件的色彩与风格。
|
||||
> 三者一并处理:换主色与密度(风格)→ 放大令牌与控件(太小)→ 用 `--td-comp-*` / `--td-font-*` 接管 TDesign 尺度(主题控制)。
|
||||
> 构建通过(`vue-tsc --noEmit` + `vite build`,22.86s)。
|
||||
|
||||
### ⚠️ 本轮挖出的头号坑:`theme-mode` 选择器权重
|
||||
TDesign 的浅/深色变量声明在 `:root[theme-mode='light'|'dark']`(权重 0,2,0),
|
||||
而上一版我们写的是 `html[theme-mode='dark']`(0,1,1)—— 生产构建里即便我们的 CSS 排在 vendor CSS 之后,
|
||||
**仍会被 UI 库的深色变量盖掉**。表现:深色下卡片/输入框用回 TDesign 自带灰(卡片 `#242424`、描边 `#ddd`)。
|
||||
**修复:tokens.scss 与 tdesign-theme.scss 全部改用 `:root[theme-mode=...]`(与 TDesign 同级权重 + 后加载者胜)。**
|
||||
(验证提醒:切完主题别只看 `getComputedStyle(body).backgroundColor`,它可能命中计算值缓存,需用探针元素或刷新后再读。)
|
||||
|
||||
### 🎨 风格:青玉编辑感 → 宝石蓝 · 卡片工作台
|
||||
- 主色:青玉 `#0c7c59` → 宝石蓝 `#2563eb`(与白底 5.17:1 过 AA;深色下按钮底同色 + 白字,链接另用 `#7aa8ff`)。
|
||||
- 中性色:青灰 → 蓝灰(浅 `#f4f6fb / #ffffff / #0f1729`,深 `#0b0f18 / #131926 / #e9eef8`);
|
||||
品牌/中性/语义三级色板与 `index.html` 的 `theme-color` 同步更新。
|
||||
- 形状:圆角整体放大一档(xs5 / sm7 / md10 / lg12 / xl16 / 2xl22);
|
||||
卡片由「无框发丝线」改回「白底 + 细描边」,重新有边界感(仅列表条目分隔线仍用 `--tk-hairline`)。
|
||||
- 密度:`--tk-space-9` 80→68、版心 1120→1200、顶栏 64→68、侧栏 248→268。
|
||||
|
||||
### 🔠 字号整体放大一档(tokens.scss)
|
||||
- 旧 → 新:`12→13 / 13→14 / 14→16 / 15→17 / 17→19 / 20→22 / 26→28 / 34→36 / 44→46 / 58→52`。
|
||||
- **正文基准 16px**,全站最小字号 13px(`--tk-text-xs`)。
|
||||
- **工具页里残留的 11px/12px 硬编码字号一次性提升为 `var(--tk-text-xs)`**(27 个 `.vue` 文件)。
|
||||
|
||||
### 🧩 UI 库主题控制(tdesign-theme.scss 新增第 ③ 段,本轮核心)
|
||||
TDesign 组件样式完全建立在 `--td-*` 变量之上(例如 `.t-input.t-size-l { height: var(--td-comp-size-xl) }`),
|
||||
所以「控件太小」的正解是改变量,而不是逐个组件写 CSS:
|
||||
|
||||
| 变量分组 | TDesign 默认 | 本版 | 影响面 |
|
||||
| --- | --- | --- | --- |
|
||||
| `--td-comp-size-m`(默认控件) | 32px | **40px** | 输入框 / 按钮 / 下拉 / 日期选择器 |
|
||||
| `--td-comp-size-xl`(large) | 40px | 48px | large 尺寸控件 |
|
||||
| `--td-comp-size-xs / xxs / xxxs` | 24 / 20 / 16px | 28 / 24 / 18px | 小按钮、标签、复选框 |
|
||||
| `--td-font-size-body-medium` | 14px | **16px** | 控件文字(与正文同号) |
|
||||
| `--td-font-size-body-small` | 12px | 13px | 小标签、说明 |
|
||||
| `--td-font-size-title-*` | 14/16/18px | 16/18/20px | 卡片标题、分组标题 |
|
||||
| `--td-line-height-body-*` | 20/22/24px | 22/24/26px | **必须与字号成对改**,否则大字号被行高挤住 |
|
||||
| `--td-comp-paddingTB / LR-*` | size 阶梯 | 放宽一档 | 控件内边距,避免大控件里文字贴边 |
|
||||
|
||||
- 只覆盖 `--td-comp-*`,**不动 `--td-size-*` 原子刻度**(图标/徽标/间距都在用它)。
|
||||
- 深色中性色阶(反向,1 最深 → 14 最浅)补进 `:root[theme-mode='dark']`,避免组件在深色下退回 UI 库冷灰。
|
||||
- 下拉:`--td-comp-size-m` 放大后 `.t-select-option` / `.t-dropdown__item` 行高自动跟随,仅补一条字号保险。
|
||||
|
||||
### 🧱 外壳、首页与公共组件(跟随新尺度)
|
||||
- `MainLayout`:顶栏 68px、命令面板入口 44px、图标按钮 40px、内容区留白 32/48/72、移动端抽屉宽 300。
|
||||
- `SideNav`:筛选框 42px、导航项 padding 9/12(行高约 43px)、图标 19px、活动指示条 3×18px。
|
||||
- `HomeView`:hero 标题 52px、搜索底线 60px、工具索引行 padding 20px、服务端卡片图标 38px + 轻投影。
|
||||
- 公共组件:`CommandPalette`(搜索行 64px、结果项 58px、kbd 22px)、`UploadDrop`(拖拽区加大、
|
||||
缩略图 56px、删除按钮热区 36px)、`AppBrand`(标记 36px)、`ThemeSwitch` / `LocaleSwitch`(40px 高)、
|
||||
`HistoryPanel`(条目最小高 48px、操作按钮热区撑大)。
|
||||
- `unocss.config.ts` 的 `panel`(改实描边 + 更大内边距)、`code-block`(14px)、`hint-text`(13px)跟随放大。
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-14 第七批:界面改版 →「更松弛的编辑感」
|
||||
|
||||
> 用户反馈对现有主题界面仍不满意,经方向选择后定为**编辑感(Editorial)**:
|
||||
> 加大留白与字号反差,卡片几乎无边框,靠排版与呼吸感建立层次。
|
||||
> 依据 `design-taste-frontend` 技能执行,旋钮 `DESIGN_VARIANCE 6` / `MOTION_INTENSITY 3` / `VISUAL_DENSITY 3`。
|
||||
> 构建通过(`vue-tsc --noEmit` + `vite build`,32.17s)。
|
||||
|
||||
### 🎯 本轮解决的三个真实问题
|
||||
1. **首页 hero 超标**:eyebrow + 标题 + 描述 + 搜索框 + 文字链接共 5 个文字元素(超过 hero 上限 4),
|
||||
且 eyebrow 是模板化开场 → **删掉 eyebrow 与重复的 hero 链接**(服务端专区入口已在下方,同意图不出现两次)。
|
||||
2. **58 张卡片长得一模一样**:全部 14px 圆角 + 1px 描边 + 绿底图标,重复得没有节奏
|
||||
→ 改为**发丝线索引行**(像书末索引),图标常态中性灰、hover 才上主色。
|
||||
3. **层次全靠边框与底纹堆出来**,排版本身承担的信息层级偏弱
|
||||
→ 标题 58px / 说明 17px 拉开反差,分区之间改用 80px 留白而非横线。
|
||||
|
||||
### 🧱 令牌层(tokens.scss)
|
||||
- 新增 `--tk-hairline`(浅 `#e7ecea` / 深 `#232b29`):内容容器与条目分隔线用它;
|
||||
**功能控件仍用 `--tk-border`**,两类不要互换。
|
||||
- 新增排版令牌:`--tk-text-5xl`(58px)、`--tk-leading-display/title/body`、
|
||||
`--tk-track-display/title`、`--tk-font-display`。其中展示字体显式等于系统字体族 ——
|
||||
中文环境下可用的展示字体就是系统字体本身,引入网络 CJK 展示字体要多付 2MB+ 首屏成本。
|
||||
- `--tk-space-9`(80px) 专用于分区之间;`--tk-header-h` 60 → 64。
|
||||
- 移除未使用的 `--tk-track-caps`(不在 CJK 场景下用宽字距)。
|
||||
|
||||
### 📐 全局排版与容器(index.scss / unocss.config.ts / tdesign-theme.scss)
|
||||
- `.page-title` → `clamp(26px, 3vw, 34px)` + 字重 640;`.page-desc` → 15px / 1.75 / `max-width: 62ch`;
|
||||
`.tool-section` 间距 16 → 32px;新增 `.section-heading` 与 `.rule`。
|
||||
- `t-card` / UnoCSS `panel`:**圆角 → 2xl、描边 → 发丝线、内边距放大一档**(`p-5 md:p-7`、
|
||||
`.t-card .t-card__body` 24/32,窄屏在 `@media (max-width: 767px)` 收回)。
|
||||
- 滚动条 10 → 8px;`.reveal` 位移 14 → 10px(编辑感入场要「不动声色」)。
|
||||
|
||||
### 🏠 首页重写(HomeView.vue)
|
||||
- 抬头区:58px 左对齐标题 + 一句说明 + **底线式搜索框**(聚焦时底线加粗为 2px,
|
||||
用 `box-shadow` 叠加避免 `border-width` 变化引起 1px 位移;输入框自身不画焦点环)。
|
||||
- 事实条去掉全部边框与底纹,数字直接摊在纸面上。
|
||||
- 服务端专区保留青玉底纹(全站唯一),是分区层级的锚点。
|
||||
- 分类目录 = 标题 + 数量注记 + 说明 + 发丝线索引网格(列间距 52px,避免两栏读串行)。
|
||||
|
||||
### 🧭 外壳(MainLayout / SideNav / AppBrand)
|
||||
- 侧栏**去掉底色块与右侧竖线**,与正文落在同一张纸面上;hover 改用 `--tk-surface`
|
||||
(浅色下更亮、深色下也更亮,两种主题都是同一种「抬起」方向)。
|
||||
- 侧栏筛选框改为底线式;内容区留白 28/28/56 → **40/48/88**;页脚改左右分列 + 发丝线。
|
||||
- 品牌图标**去掉全站唯一渐变**,改实色小方块(渐变会把视线从标题上拽走),
|
||||
品牌辨识度改由字重与字距承担。
|
||||
|
||||
### ♻️ 顺带修复
|
||||
- `HistoryPanel.vue` 此前混用硬编码的 `--td-*` 与写死的 `border-radius: 6px`,
|
||||
违反「样式一律用 `--tk-*`」的硬规则,且每条历史都套一个描边盒子;已改为 `--tk-*` 令牌 + 发丝线分行。
|
||||
- 清理 4 份语言包中已失效的 `home.eyebrow` / `home.ctaPrimary` / `home.ctaSecondary`。
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-14 第六批:视觉重设计 + 中英日韩四语言 + 昼夜主题 + 服务端接口对接
|
||||
|
||||
> 依据 `design-taste-frontend` 技能(BRIEF INFERENCE / 单一强调色 / 形状一致性 / 反 AI-slop 清单)重建全站视觉体系,
|
||||
|
||||
@ -49,13 +49,36 @@
|
||||
- `ToolItem.needsNetwork` 标记需联网工具(侧栏圆点 + 首页服务端工具专区)。
|
||||
- `groupedTools()` / `networkTools()` / `searchTools()` 供首页、服务端专区、命令面板使用。
|
||||
|
||||
### 3.2 视觉体系(令牌 → TDesign 桥接)
|
||||
### 3.2 视觉体系(令牌 → TDesign 桥接 + UI 库主题控制)
|
||||
- **唯一真相源**:`src/assets/styles/tokens.scss` 定义全部 `--tk-*` 令牌(颜色/圆角/间距/字号/z-index/动效/字体栈)。
|
||||
- **桥接层**:`src/assets/styles/tdesign-theme.scss` 把 `--tk-*` 映射到 TDesign 的 `--td-*`。
|
||||
**改主题只需改 tokens.scss 一处,60+ 个未改动的工具页会自动继承新配色与圆角** —— 这是低风险全站改造的关键机制。
|
||||
- **硬规则**:自定义样式一律引用 `--tk-*`(或 `--td-*`),**禁止硬编码色值/圆角/阴影**,否则昼夜切换会花掉。
|
||||
- **形状一致性**:全站只用 tokens 里那一套圆角(xs4/sm6/md8/lg10/xl14/2xl20/pill999)。
|
||||
- **单一强调色**:青玉 Jade。全站只允许一处渐变(品牌图标)。
|
||||
- **桥接层 + UI 库主题控制**:`src/assets/styles/tdesign-theme.scss` 除了把 `--tk-*` 映射到 `--td-*`,
|
||||
还接管两件「UI 库自身」的事:
|
||||
1. **组件尺度**:`--td-comp-size-*`(控件高度阶梯)、`--td-comp-paddingTB/LR-*`、`--td-font-size-*`、
|
||||
`--td-line-height-*`。**TDesign 的输入框 / 按钮 / 下拉 / 日期选择器高度全部取自这些变量**
|
||||
(例如 `.t-input.t-size-l { height: var(--td-comp-size-xl) }`),所以放大控件只需改这一处,
|
||||
**不要再给单个组件写死高度或字号**。
|
||||
2. **色阶**:品牌 / 中性 / 语义的 10 级色板。注意深色下的中性色阶是**反向**的(1 最深 → 14 最浅),
|
||||
且必须显式覆盖,否则组件会退回 TDesign 自带的冷灰。
|
||||
**改主题 = 改 tokens.scss(配色/字号/圆角/间距)+ tdesign-theme.scss(UI 库尺度与色阶)两处**,
|
||||
60+ 个未改动的工具页自动继承 —— 这是低风险全站改造的关键机制。
|
||||
- **硬规则**:自定义样式一律引用 `--tk-*`(或 `--td-*`),**禁止硬编码色值/圆角/阴影/字号**,否则昼夜切换会花掉。
|
||||
- **形状一致性**:全站只用 tokens 里那一套圆角(xs5/sm7/md10/lg12/xl16/2xl22/pill999)。
|
||||
- **单一强调色**:宝石蓝 `#2563eb`。**全站已无任何渐变**(品牌图标为实色圆角方块)。
|
||||
强调色只出现在需要「指向」的地方:hover 的条目、活动导航项、服务端专区底纹、正文链接。
|
||||
- **设计方向(2026-09-14 第二次改版):清晰蓝 · 卡片工作台**。四条硬规则:
|
||||
1. **字号整体放大一档**:正文 16px 起、说明文字不低于 13px(上一版 12/13/14 的密度在 1080p 屏上偏小);
|
||||
2. **控件大于内容**:输入框 / 按钮 / 下拉默认 40px 高、16px 文字,可点区域永远比正文更「实」;
|
||||
3. **容器有边界**:卡片用「白底 + 细描边(`--tk-border`)」与页面底(`--tk-bg`)拉开层次,不再追求无框纸面;
|
||||
4. **主色单一**:全站只有一个强调色。
|
||||
- **发丝线 `--tk-hairline`**:只用于**列表条目之间的分隔线**(历史列表、首页索引行);
|
||||
内容容器边界与功能控件(输入框/按钮/下拉)一律用 `--tk-border`。
|
||||
- **留白节奏**:分区之间用 `--tk-space-9`(68px)分隔,**不画横线** —— 留白本身就是分隔符。
|
||||
窄屏按断点阶梯式收回(`≤1279px` → `≤1023px` → `≤767px`),不要一档切到底。
|
||||
- **排版令牌必须成对使用**:大字号配负字距 + 紧行高(`--tk-track-display/-title` +
|
||||
`--tk-leading-display/-body`),小字号配正字距 + 松行高。混用(大字号配松行高)会让层级立刻垮掉。
|
||||
- **首页是目录,不是卡片墙**:58 个工具用「索引行」呈现(发丝线分隔),不做 58 个描边盒子;
|
||||
图标常态中性灰,主色只在 hover 时出现(58 个图标全染主色会把主色的含义稀释掉);
|
||||
仅「服务端工具专区」用整块底纹 + 卡片与其它分区区分。
|
||||
- **动效档位**:克制(上浮淡入 + 颜色过渡),`prefers-reduced-motion` 下全部降为 0ms。
|
||||
- `v-reveal` 指令(`src/directives/reveal.ts`)做滚动进入,传数字错峰。
|
||||
|
||||
@ -113,6 +136,8 @@
|
||||
- **Vue 模板 attribute**:内嵌字符串不能直接写 `\"`(HTML 解析器会把 `"` 当属性结束符导致编译错误),字符串常量移到 `<script>` 定义。
|
||||
- **`in` 运算符**:`k in obj` 中 obj 需先断言为 `Record<string, unknown>`,否则 strict 模式 TS2638/TS18048。
|
||||
- **构建安全删除保护**:`pnpm build` 清理 dist 会触发沙箱批量删除拦截(>50 文件),先 `mv dist dist.bak-<ts>` 再构建。
|
||||
- **备份目录同样删不掉**:`dist.bak-*` 的递归删除也会被 bulk guard 拦(`SAFE_DELETE_BULK_GUARD_ERROR`),
|
||||
因此多次构建会堆积若干 `dist.bak-*`,属正常现象;需要清理时逐个目录删,或交由人手动处理。
|
||||
- **大整数**:进制转换等工具禁用 `parseInt/Number`,用 BigInt 逐位解析(现有 radix-convert 已实现)。
|
||||
- **pnpm 调用方式**:`corepack` 的 `pnpm` shell wrapper 在 Git Bash 下会把 `/c/...` 误算成 `E:\c\...`
|
||||
(报 `Cannot find module 'E:\c\...\corepack\dist\pnpm.js'`)。绕过 wrapper,显式调用:
|
||||
@ -123,10 +148,22 @@
|
||||
|
||||
### 6.2 样式与主题
|
||||
- **禁止硬编码色值**:一律用 `--tk-*`(自定义样式)或 `--td-*`(TDesign 组件覆盖)。
|
||||
- **UnoCSS `panel` 快捷类自带内边距**(`p-5 md:p-6`)。页面 scoped 样式(如 `.block[data-v-*]`)权重更高,
|
||||
需要单独覆盖 padding 时照常写即可。
|
||||
- **UnoCSS `panel` 快捷类自带内边距**(`p-5 md:p-7`,编辑感改版后放大一档)且描边为发丝线。
|
||||
页面 scoped 样式(如 `.block[data-v-*]`)权重更高,需要单独覆盖 padding 时照常写即可。
|
||||
- **卡片内边距的覆盖写法**:`.t-card .t-card__body`(两个类)比 TDesign 自身的 `.t-card__body` 高一级,
|
||||
能稳定覆盖;只写一个类会被 TDesign 的规则压掉。窄屏需在 `@media (max-width: 767px)` 里收回。
|
||||
- **`--td-radius-*` 刻意不跟随主题**(浅深共用),改圆角请改 `tokens.scss` 的 `--tk-radius-*`。
|
||||
- **canvas 绘制**:注意主题切换,尽量用令牌色或中性色,不要写死黑白。
|
||||
- **头号坑:`theme-mode` 选择器必须写 `:root[theme-mode='dark']`,不能写 `html[theme-mode='dark']`**。
|
||||
TDesign 自己的浅/深色变量声明用的是 `:root[theme-mode=...]`(权重 0,2,0),
|
||||
而 `html[theme-mode=...]` 只有 (0,1,1) —— **生产构建里我们的 CSS 虽然排在 vendor CSS 之后,仍会被 UI 库盖掉**。
|
||||
表现:深色下组件用回 TDesign 自带灰(卡片 `#242424`、描边 `#ddd`),只有少量手写规则生效。
|
||||
新增主题变量时请放进 tokens.scss / tdesign-theme.scss 里那两个 `:root[theme-mode=...]` 块。
|
||||
- **验证主题是否真的生效**:切完 `theme-mode` 后**不要**只看 `getComputedStyle(document.body).backgroundColor`,
|
||||
它可能命中浏览器的计算值缓存、读出「上一次主题」的颜色。可靠做法:插入探针元素读
|
||||
(`div.style.backgroundColor = 'var(--tk-bg)'` 后读 computed),或**刷新页面后再读**。
|
||||
- **控件尺度统一由 `--td-comp-size-*` 控制**:放大/缩小按钮、输入框、下拉请改 `tdesign-theme.scss` 的尺度段,
|
||||
不要给单个组件写死 `height`(会与 UI 库其它组件脱节)。
|
||||
|
||||
### 6.3 国际化
|
||||
- **`html` 的 `lang` 属性写的是完整标签**(`ja-JP` / `ko-KR`),因此 `tokens.scss` 里的字体栈选择器
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
name="description"
|
||||
content="纯前端在线工具箱:图片压缩与裁剪、二维码、JSON 格式化、时间戳、Base64、JWT、Cron、进制转换、哈希、身份证解析、OCR 等 56 个实用工具,支持中英日韩四种语言与日间夜间主题,数据全部在浏览器本地处理。"
|
||||
/>
|
||||
<meta name="theme-color" content="#f6f8f8" media="(prefers-color-scheme: light)" />
|
||||
<meta name="theme-color" content="#0c100f" media="(prefers-color-scheme: dark)" />
|
||||
<meta name="theme-color" content="#f4f6fb" media="(prefers-color-scheme: light)" />
|
||||
<meta name="theme-color" content="#0b0f18" media="(prefers-color-scheme: dark)" />
|
||||
<title>在线工具箱 · Tool</title>
|
||||
<!--
|
||||
防闪烁(FOUC)内联脚本:必须在样式与模块加载之前同步执行。
|
||||
|
||||
@ -3,11 +3,14 @@
|
||||
* ------------------------------------------------------------
|
||||
* 引入顺序很重要:
|
||||
* 1. tokens.scss —— 设计令牌(--tk-*)
|
||||
* 2. tdesign-theme.scss —— 令牌 → TDesign 变量(--td-*)
|
||||
* 2. tdesign-theme.scss —— 令牌 → TDesign 变量(--td-*)+ UI 库尺度控制
|
||||
* 3. 本文件其余部分 —— 基础样式与工具页通用类
|
||||
*
|
||||
* 所有颜色只允许来自 --tk-* / --td-*,禁止硬编码色值,
|
||||
* 这样昼夜主题切换才能全站一致生效。
|
||||
*
|
||||
* 字号只允许来自 --tk-text-*:本版把正文提到 16px,
|
||||
* 新增样式请勿写死 12/13px,否则又会回到「字太小」的老问题。
|
||||
* ============================================================ */
|
||||
|
||||
@use './tokens';
|
||||
@ -38,8 +41,10 @@ body {
|
||||
|
||||
body {
|
||||
font-family: var(--tk-font-sans);
|
||||
/* 正文基准字号 = --tk-text-base(16px)。全站最小可见文字是 13px(--tk-text-xs),
|
||||
* 主要出现在辅助说明与元信息上,不再有 12px 的正文。 */
|
||||
font-size: var(--tk-text-base);
|
||||
line-height: 1.6;
|
||||
line-height: 1.7;
|
||||
background-color: var(--tk-bg);
|
||||
color: var(--tk-text);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
@ -77,7 +82,7 @@ body,
|
||||
left: 12px;
|
||||
top: 12px;
|
||||
z-index: var(--tk-z-palette);
|
||||
padding: 8px 14px;
|
||||
padding: 10px 16px;
|
||||
border-radius: var(--tk-radius-md);
|
||||
background: var(--tk-surface-raised);
|
||||
border: 1px solid var(--tk-border);
|
||||
@ -102,14 +107,15 @@ img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* ---------- 滚动条美化 ---------- */
|
||||
/* ---------- 滚动条美化 ----------
|
||||
* 字号与控件都放大了一档,滚动条同步加粗到 10px,否则不好点中。 */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--td-scrollbar-color);
|
||||
border: 3px solid transparent;
|
||||
border: 2px solid transparent;
|
||||
background-clip: content-box;
|
||||
border-radius: var(--tk-radius-pill);
|
||||
}
|
||||
@ -125,7 +131,7 @@ img {
|
||||
* 工具页通用区块(60+ 个页面共用,改这里即全站生效)
|
||||
* ============================================================ */
|
||||
.tool-section {
|
||||
margin-bottom: var(--tk-space-4);
|
||||
margin-bottom: var(--tk-space-6);
|
||||
}
|
||||
|
||||
/* 结果展示区:长文本自动换行 */
|
||||
@ -134,23 +140,43 @@ img {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* 页面标题与描述 */
|
||||
/* 页面标题与描述
|
||||
* 标题明显大于正文,层级靠字号反差立住;移动端通过 clamp 下限回到 28px,
|
||||
* 避免中文标题在窄屏被拆成三行。 */
|
||||
.page-title {
|
||||
margin: 0 0 6px;
|
||||
font-size: var(--tk-text-2xl);
|
||||
margin: 0 0 12px;
|
||||
font-family: var(--tk-font-display);
|
||||
font-size: clamp(28px, 3vw, 38px);
|
||||
font-weight: 650;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.25;
|
||||
letter-spacing: var(--tk-track-title);
|
||||
line-height: var(--tk-leading-title);
|
||||
color: var(--tk-text);
|
||||
}
|
||||
.page-desc {
|
||||
margin: 0 0 var(--tk-space-5);
|
||||
max-width: 68ch;
|
||||
margin: 0 0 var(--tk-space-7);
|
||||
max-width: 62ch;
|
||||
font-size: var(--tk-text-base);
|
||||
line-height: 1.65;
|
||||
line-height: var(--tk-leading-body);
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
|
||||
/* 分区标题:比页面标题低一档,用于工具页内部把长表单切成若干段落 */
|
||||
.section-heading {
|
||||
margin: var(--tk-space-6) 0 var(--tk-space-4);
|
||||
font-size: var(--tk-text-lg);
|
||||
font-weight: 620;
|
||||
letter-spacing: var(--tk-track-title);
|
||||
color: var(--tk-text);
|
||||
}
|
||||
|
||||
/* 行分隔线:统一用发丝线,不要画成带底色的长条 */
|
||||
.rule {
|
||||
height: 1px;
|
||||
border: none;
|
||||
margin: 0;
|
||||
background: var(--tk-hairline);
|
||||
}
|
||||
|
||||
/* 表单标签(各工具页统一宽度,保证左侧对齐) */
|
||||
.form-label {
|
||||
flex-shrink: 0;
|
||||
@ -162,14 +188,15 @@ img {
|
||||
.form-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--tk-space-2);
|
||||
margin-top: var(--tk-space-4);
|
||||
gap: var(--tk-space-3);
|
||||
margin-top: var(--tk-space-5);
|
||||
}
|
||||
|
||||
/* 单行说明/元信息 */
|
||||
.field-hint {
|
||||
margin: var(--tk-space-2) 0 0;
|
||||
font-size: var(--tk-text-sm);
|
||||
line-height: 1.6;
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
|
||||
@ -189,7 +216,7 @@ img {
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 滚动进入动效(MOTION 档位 4/10:只做轻微上浮淡入,不做视差/钉住)
|
||||
* 滚动进入动效(只做轻微上浮淡入,不做视差/钉住)
|
||||
* 由 v-reveal 指令添加 .is-revealed;无 JS 时元素始终可见,不会白屏。
|
||||
* ============================================================ */
|
||||
.reveal {
|
||||
@ -199,7 +226,7 @@ img {
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(14px);
|
||||
transform: translateY(12px);
|
||||
transition:
|
||||
opacity var(--tk-dur-slow) var(--tk-ease-out),
|
||||
transform var(--tk-dur-slow) var(--tk-ease-out);
|
||||
|
||||
@ -1,11 +1,25 @@
|
||||
/* ============================================================
|
||||
* TDesign 主题桥接层
|
||||
* TDesign 主题控制层(UI 库主题)
|
||||
* ------------------------------------------------------------
|
||||
* 把 tokens.scss 里的 --tk-* 设计令牌映射到 TDesign 的 --td-* 变量。
|
||||
* 本文件负责「控制 UI 库组件本身的色彩与风格」,分四件事:
|
||||
* ① 色阶:品牌蓝 / 中性蓝灰 / 语义色的 10 级色板(我们的色,喂给 TDesign)
|
||||
* ② 语义角色:把 tokens.scss 的 --tk-* 映射到 TDesign 的 --td-*
|
||||
* ③ 组件尺度:控件高度 / 内边距 / 字号 / 行高(本版为解决「控件太小」新增)
|
||||
* ④ 组件级微调:卡片、输入框、按钮、弹层的外形修正
|
||||
*
|
||||
* 这样做的好处:60+ 个工具页面里已有的大量 t-card / t-button /
|
||||
* t-input / t-tag 无需改一行代码,就整体换上了新的青玉配色与圆角体系,
|
||||
* 属于「一次改主题、全站生效」的低风险改造方式。
|
||||
* 为什么这样做:60+ 个工具页面里已有大量 t-card / t-button / t-input /
|
||||
* t-select / t-tag,全部改一遍既不现实也容易漏。TDesign 的组件样式
|
||||
* 完全建立在 --td-* 变量之上(例如 .t-input.t-size-l 的高度取自
|
||||
* var(--td-comp-size-xl)),所以改变量 = 全站组件改版,属最小风险改造。
|
||||
*
|
||||
* ⚠️ 三条必须遵守的约束(踩过坑):
|
||||
* 1. 只覆盖 TDesign 真实存在的变量名。写错名字不会报错,只是静默不生效。
|
||||
* 新增前请在 node_modules/tdesign-vue-next 的样式产物里确认。
|
||||
* 2. --td-font-body-* 是复合变量(size/line-height 简写),
|
||||
* 所以放大字号时必须同步改 --td-font-size-* 与 --td-line-height-*,
|
||||
* 只改前者会出现「大字号被小行高挤住」的破版。
|
||||
* 3. 尺度变量不要动 --td-size-N 阶梯本身(那是 TDesign 的原子刻度,
|
||||
* 图标、徽标、间距都在用);只覆盖 --td-comp-size-* 一层。
|
||||
*
|
||||
* 命名桥接对应关系(速查):
|
||||
* --tk-accent → --td-brand-color (按钮/主色底)
|
||||
@ -19,18 +33,18 @@
|
||||
* 浅色主题
|
||||
* ============================================================ */
|
||||
:root,
|
||||
html[theme-mode='light'] {
|
||||
/* ---------- 品牌色阶(青玉,1 最浅 → 10 最深) ---------- */
|
||||
--td-brand-color-1: #e9f7f2;
|
||||
--td-brand-color-2: #c6eddf;
|
||||
--td-brand-color-3: #99dfc8;
|
||||
--td-brand-color-4: #66cead;
|
||||
--td-brand-color-5: #36bc93;
|
||||
--td-brand-color-6: #17a37b;
|
||||
--td-brand-color-7: #0c7c59;
|
||||
--td-brand-color-8: #096548;
|
||||
--td-brand-color-9: #074e38;
|
||||
--td-brand-color-10: #053827;
|
||||
:root[theme-mode='light'] {
|
||||
/* ---------- 品牌色阶(宝石蓝,1 最浅 → 10 最深) ---------- */
|
||||
--td-brand-color-1: #eff4ff;
|
||||
--td-brand-color-2: #dbe6fe;
|
||||
--td-brand-color-3: #c0d4fe;
|
||||
--td-brand-color-4: #96b6fd;
|
||||
--td-brand-color-5: #6294fb;
|
||||
--td-brand-color-6: #3b76f6;
|
||||
--td-brand-color-7: #2563eb;
|
||||
--td-brand-color-8: #1d4ed8;
|
||||
--td-brand-color-9: #1e3fb0;
|
||||
--td-brand-color-10: #1b3690;
|
||||
|
||||
/* ---------- 语义色阶 ---------- */
|
||||
--td-success-color-1: #e6f7ee;
|
||||
@ -66,30 +80,29 @@ html[theme-mode='light'] {
|
||||
--td-error-color-9: #4c1613;
|
||||
--td-error-color-10: #320d0b;
|
||||
|
||||
/* ---------- 中性色阶(冷调,1 最浅 → 14 最深)
|
||||
/* ---------- 中性色阶(蓝灰,1 最浅 → 14 最深)
|
||||
* 深浅两套主题共用同一条色阶:浅色主题取低号,深色主题取高号 ---------- */
|
||||
--td-gray-color-1: #f6f8f8;
|
||||
--td-gray-color-2: #eef1f0;
|
||||
--td-gray-color-3: #e3e8e6;
|
||||
--td-gray-color-4: #d3dad7;
|
||||
--td-gray-color-5: #c2cbc8;
|
||||
--td-gray-color-6: #a3afab;
|
||||
--td-gray-color-7: #7c8885;
|
||||
--td-gray-color-8: #5a6562;
|
||||
--td-gray-color-9: #3e4845;
|
||||
--td-gray-color-10: #333c39;
|
||||
--td-gray-color-11: #252d2b;
|
||||
--td-gray-color-12: #1a201e;
|
||||
--td-gray-13: #131817;
|
||||
--td-gray-color-13: #131817;
|
||||
--td-gray-color-14: #0c100f;
|
||||
--td-gray-color-1: #f4f6fb;
|
||||
--td-gray-color-2: #eef2f9;
|
||||
--td-gray-color-3: #e2e8f3;
|
||||
--td-gray-color-4: #ccd7e8;
|
||||
--td-gray-color-5: #b1bdd3;
|
||||
--td-gray-color-6: #8f9cb4;
|
||||
--td-gray-color-7: #6e7994;
|
||||
--td-gray-color-8: #55617a;
|
||||
--td-gray-color-9: #3d4759;
|
||||
--td-gray-color-10: #2e3747;
|
||||
--td-gray-color-11: #263147;
|
||||
--td-gray-color-12: #1b2333;
|
||||
--td-gray-color-13: #131926;
|
||||
--td-gray-color-14: #0b0f18;
|
||||
|
||||
/* ---------- 语义角色 ---------- */
|
||||
--td-brand-color: var(--tk-accent);
|
||||
--td-brand-color-hover: var(--tk-accent-hover);
|
||||
--td-brand-color-active: var(--tk-accent-active);
|
||||
--td-brand-color-focus: var(--tk-accent-surface-strong);
|
||||
--td-brand-color-disabled: #99dfc8;
|
||||
--td-brand-color-disabled: #96b6fd;
|
||||
--td-brand-color-light: var(--tk-accent-surface);
|
||||
--td-brand-color-light-hover: var(--tk-accent-surface-strong);
|
||||
|
||||
@ -136,19 +149,19 @@ html[theme-mode='light'] {
|
||||
--td-bg-color-specialcomponent: var(--tk-surface-raised);
|
||||
|
||||
/* 遮罩:用带色相的深色,避免死黑 */
|
||||
--td-mask-active: rgba(9, 20, 16, 0.55);
|
||||
--td-mask-disabled: rgba(252, 253, 253, 0.6);
|
||||
--td-mask-active: rgba(9, 15, 32, 0.55);
|
||||
--td-mask-disabled: rgba(255, 255, 255, 0.6);
|
||||
--td-mask-background: var(--tk-surface);
|
||||
|
||||
/* ---------- 文字 ---------- */
|
||||
--td-text-color-primary: var(--tk-text);
|
||||
--td-text-color-secondary: var(--tk-text-secondary);
|
||||
--td-text-color-placeholder: var(--tk-text-placeholder);
|
||||
--td-text-color-disabled: #a3afab;
|
||||
--td-text-color-disabled: #b1bdd3;
|
||||
--td-text-color-anti: var(--tk-text-on-accent);
|
||||
--td-text-color-brand: var(--tk-accent-text);
|
||||
--td-text-color-link: var(--tk-accent-text);
|
||||
--td-text-color-watermark: rgba(17, 26, 23, 0.08);
|
||||
--td-text-color-watermark: rgba(15, 23, 41, 0.08);
|
||||
|
||||
/* ---------- 描边 ---------- */
|
||||
--td-border-level-1-color: var(--tk-border);
|
||||
@ -173,11 +186,11 @@ html[theme-mode='light'] {
|
||||
--td-shadow-inset-right: inset 0.5px 0 0 var(--tk-border);
|
||||
--td-shadow-inset-bottom: inset 0 -0.5px 0 var(--tk-border);
|
||||
--td-shadow-inset-left: inset -0.5px 0 0 var(--tk-border);
|
||||
--td-table-shadow-color: rgba(16, 32, 28, 0.08);
|
||||
--td-table-shadow-color: rgba(15, 23, 41, 0.08);
|
||||
|
||||
/* ---------- 滚动条 ---------- */
|
||||
--td-scrollbar-color: rgba(17, 26, 23, 0.16);
|
||||
--td-scrollbar-hover-color: rgba(17, 26, 23, 0.32);
|
||||
--td-scrollbar-color: rgba(15, 23, 41, 0.18);
|
||||
--td-scrollbar-hover-color: rgba(15, 23, 41, 0.34);
|
||||
--td-scroll-track-color: transparent;
|
||||
|
||||
/* ---------- 字体 ---------- */
|
||||
@ -188,18 +201,18 @@ html[theme-mode='light'] {
|
||||
/* ============================================================
|
||||
* 深色主题
|
||||
* ============================================================ */
|
||||
html[theme-mode='dark'] {
|
||||
:root[theme-mode='dark'] {
|
||||
/* ---------- 品牌色阶(深色下 1 最深 → 10 最浅) ---------- */
|
||||
--td-brand-color-1: #0a2e22;
|
||||
--td-brand-color-2: #0b3d2d;
|
||||
--td-brand-color-3: #0c4d39;
|
||||
--td-brand-color-4: #0d5c44;
|
||||
--td-brand-color-5: #0e6f52;
|
||||
--td-brand-color-6: #0e8460;
|
||||
--td-brand-color-7: #10946b;
|
||||
--td-brand-color-8: #2aa87c;
|
||||
--td-brand-color-9: #3fc79b;
|
||||
--td-brand-color-10: #78dfba;
|
||||
--td-brand-color-1: #16244d;
|
||||
--td-brand-color-2: #1a2d63;
|
||||
--td-brand-color-3: #1e377c;
|
||||
--td-brand-color-4: #234298;
|
||||
--td-brand-color-5: #2a4fb5;
|
||||
--td-brand-color-6: #3059d6;
|
||||
--td-brand-color-7: #2563eb;
|
||||
--td-brand-color-8: #4a80f5;
|
||||
--td-brand-color-9: #7aa8ff;
|
||||
--td-brand-color-10: #b3ccff;
|
||||
|
||||
/* ---------- 语义色阶 ---------- */
|
||||
--td-success-color-1: #06291a;
|
||||
@ -236,13 +249,13 @@ html[theme-mode='dark'] {
|
||||
--td-error-color-10: #f2aca4;
|
||||
|
||||
/* ---------- 语义角色 ----------
|
||||
* 注意:深色下按钮底压深到 #0e8460(与白字 4.68:1),
|
||||
* 注意:深色下按钮底保持 #2563eb(与白字 5.17:1),
|
||||
* 而链接/图标另用更亮的 --tk-accent-text 保证在深底上可读。 */
|
||||
--td-brand-color: var(--tk-accent);
|
||||
--td-brand-color-hover: var(--tk-accent-hover);
|
||||
--td-brand-color-active: var(--tk-accent-active);
|
||||
--td-brand-color-focus: var(--tk-accent-surface-strong);
|
||||
--td-brand-color-disabled: #0c4d39;
|
||||
--td-brand-color-disabled: #1e377c;
|
||||
--td-brand-color-light: var(--tk-accent-surface);
|
||||
--td-brand-color-light-hover: var(--tk-accent-surface-strong);
|
||||
|
||||
@ -279,28 +292,28 @@ html[theme-mode='dark'] {
|
||||
--td-bg-color-secondarycontainer: var(--tk-surface-sunken);
|
||||
--td-bg-color-secondarycontainer-hover: var(--tk-surface-hover);
|
||||
--td-bg-color-secondarycontainer-active: var(--tk-surface-active);
|
||||
--td-bg-color-component: #1b2220;
|
||||
--td-bg-color-component-hover: #222a28;
|
||||
--td-bg-color-component-active: #2b3331;
|
||||
--td-bg-color-component-disabled: #171d1b;
|
||||
--td-bg-color-secondarycomponent: #222a28;
|
||||
--td-bg-color-secondarycomponent-hover: #2b3331;
|
||||
--td-bg-color-secondarycomponent-active: #343c3a;
|
||||
--td-bg-color-component: var(--tk-surface-sunken);
|
||||
--td-bg-color-component-hover: var(--tk-surface-hover);
|
||||
--td-bg-color-component-active: var(--tk-surface-active);
|
||||
--td-bg-color-component-disabled: var(--tk-surface-sunken);
|
||||
--td-bg-color-secondarycomponent: var(--tk-surface-hover);
|
||||
--td-bg-color-secondarycomponent-hover: var(--tk-surface-active);
|
||||
--td-bg-color-secondarycomponent-active: var(--tk-border-strong);
|
||||
--td-bg-color-specialcomponent: var(--tk-surface-raised);
|
||||
|
||||
--td-mask-active: rgba(0, 0, 0, 0.72);
|
||||
--td-mask-disabled: rgba(19, 24, 23, 0.6);
|
||||
--td-mask-disabled: rgba(19, 25, 38, 0.6);
|
||||
--td-mask-background: var(--tk-surface);
|
||||
|
||||
/* ---------- 文字 ---------- */
|
||||
--td-text-color-primary: var(--tk-text);
|
||||
--td-text-color-secondary: var(--tk-text-secondary);
|
||||
--td-text-color-placeholder: var(--tk-text-placeholder);
|
||||
--td-text-color-disabled: #5a6562;
|
||||
--td-text-color-disabled: #55617a;
|
||||
--td-text-color-anti: var(--tk-text-on-accent);
|
||||
--td-text-color-brand: var(--tk-accent-text);
|
||||
--td-text-color-link: var(--tk-accent-text);
|
||||
--td-text-color-watermark: rgba(232, 237, 235, 0.1);
|
||||
--td-text-color-watermark: rgba(233, 238, 248, 0.1);
|
||||
|
||||
/* ---------- 描边 ---------- */
|
||||
--td-border-level-1-color: var(--tk-border);
|
||||
@ -308,7 +321,7 @@ html[theme-mode='dark'] {
|
||||
--td-border-level-2-color: var(--tk-border-strong);
|
||||
--td-component-border: var(--tk-border-strong);
|
||||
|
||||
/* ---------- 圆角 / 阴影 / 滚动条 / 字体(与主题无关,沿用令牌) ---------- */
|
||||
/* ---------- 圆角 / 阴影 / 滚动条 / 字体 ---------- */
|
||||
--td-radius-small: var(--tk-radius-sm);
|
||||
--td-radius-default: var(--tk-radius-md);
|
||||
--td-radius-medium: var(--tk-radius-lg);
|
||||
@ -326,8 +339,8 @@ html[theme-mode='dark'] {
|
||||
--td-shadow-inset-left: inset -0.5px 0 0 var(--tk-border);
|
||||
--td-table-shadow-color: rgba(0, 0, 0, 0.3);
|
||||
|
||||
--td-scrollbar-color: rgba(232, 237, 235, 0.16);
|
||||
--td-scrollbar-hover-color: rgba(232, 237, 235, 0.32);
|
||||
--td-scrollbar-color: rgba(233, 238, 248, 0.18);
|
||||
--td-scrollbar-hover-color: rgba(233, 238, 248, 0.34);
|
||||
--td-scroll-track-color: transparent;
|
||||
|
||||
--td-font-family: var(--tk-font-sans);
|
||||
@ -335,23 +348,131 @@ html[theme-mode='dark'] {
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 少量组件级微调
|
||||
* —— TDesign 默认的字重/描边在青玉配色下略重,这里做轻量化处理
|
||||
* ③ UI 库组件尺度控制(本版新增,解决「控件太小」)
|
||||
* ------------------------------------------------------------
|
||||
* 与主题无关,因此三个选择器都要写(且必须用 :root[theme-mode=...] 这一
|
||||
* 与 TDesign 同级权重的写法,否则会被 UI 库自身的深色变量盖掉):
|
||||
* 直接改 --td-size-* 会连带影响图标/徽标,所以只覆盖 --td-comp-* 一层。
|
||||
*
|
||||
* 放大前后对照(TDesign 默认 → 本版):
|
||||
* 默认输入框/按钮/下拉 32px → 40px
|
||||
* 大号控件 40px → 48px
|
||||
* 小号控件 24px → 28px
|
||||
* 控件文字 14px → 16px(与正文同号,读起来不再「小一号」)
|
||||
* ============================================================ */
|
||||
/* ============================================================
|
||||
* 深色主题的中性色阶(补在主题块之外,便于与尺度控制放在一起)
|
||||
* ------------------------------------------------------------
|
||||
* TDesign 在深色下使用「反向灰阶」(1 最深 → 14 最浅),
|
||||
* 不覆盖的话组件会退回 UI 库自带的冷灰,与本主题的蓝灰体系脱节。
|
||||
* ============================================================ */
|
||||
:root[theme-mode='dark'] {
|
||||
--td-gray-color-1: #0b0f18;
|
||||
--td-gray-color-2: #131926;
|
||||
--td-gray-color-3: #1b2333;
|
||||
--td-gray-color-4: #222c3f;
|
||||
--td-gray-color-5: #263147;
|
||||
--td-gray-color-6: #35415c;
|
||||
--td-gray-color-7: #4a5670;
|
||||
--td-gray-color-8: #6e7994;
|
||||
--td-gray-color-9: #8f9cb4;
|
||||
--td-gray-color-10: #b1bdd3;
|
||||
--td-gray-color-11: #ccd7e8;
|
||||
--td-gray-color-12: #e2e8f3;
|
||||
--td-gray-color-13: #eef2f9;
|
||||
--td-gray-color-14: #f4f6fb;
|
||||
}
|
||||
|
||||
:root,
|
||||
:root[theme-mode='light'],
|
||||
:root[theme-mode='dark'] {
|
||||
/* ---------- 控件高度阶梯 ---------- */
|
||||
--td-comp-size-xxxs: 18px; /* 复选框、小圆点 */
|
||||
--td-comp-size-xxs: 24px; /* 小号控件(t-tag / 紧凑按钮) */
|
||||
--td-comp-size-xs: 28px;
|
||||
--td-comp-size-s: 32px;
|
||||
--td-comp-size-m: 40px; /* ★ 默认控件:输入框 / 按钮 / 下拉 / 日期选择 */
|
||||
--td-comp-size-l: 44px;
|
||||
--td-comp-size-xl: 48px; /* large 尺寸输入框、下拉触发框 */
|
||||
--td-comp-size-xxl: 52px;
|
||||
--td-comp-size-xxxl: 60px;
|
||||
|
||||
/* ---------- 控件内边距:随高度同步放宽,否则大控件里的文字会贴边 ---------- */
|
||||
--td-comp-paddingTB-xxs: 4px;
|
||||
--td-comp-paddingTB-xs: 6px;
|
||||
--td-comp-paddingTB-s: 8px;
|
||||
--td-comp-paddingTB-m: 12px;
|
||||
--td-comp-paddingTB-l: 14px;
|
||||
--td-comp-paddingTB-xl: 20px;
|
||||
--td-comp-paddingLR-xxs: 6px;
|
||||
--td-comp-paddingLR-xs: 8px;
|
||||
--td-comp-paddingLR-s: 12px;
|
||||
--td-comp-paddingLR-m: 16px;
|
||||
--td-comp-paddingLR-l: 20px;
|
||||
--td-comp-paddingLR-xl: 28px;
|
||||
|
||||
/* ---------- 字号:控件文字提到 16px,与正文同号 ---------- */
|
||||
--td-font-size-body-small: 13px;
|
||||
--td-font-size-body-medium: 16px;
|
||||
--td-font-size-body-large: 17px;
|
||||
--td-font-size-title-small: 16px;
|
||||
--td-font-size-title-medium: 18px;
|
||||
--td-font-size-title-large: 20px;
|
||||
--td-font-size-link-small: 13px;
|
||||
--td-font-size-link-medium: 15px;
|
||||
--td-font-size-link-large: 17px;
|
||||
--td-font-size-mark-small: 13px;
|
||||
--td-font-size-mark-medium: 15px;
|
||||
|
||||
/* ---------- 行高:必须与上面成对修改,否则大字号会被小行高挤住 ---------- */
|
||||
--td-line-height-body-small: 22px;
|
||||
--td-line-height-body-medium: 24px;
|
||||
--td-line-height-body-large: 26px;
|
||||
--td-line-height-title-small: 24px;
|
||||
--td-line-height-title-medium: 28px;
|
||||
--td-line-height-title-large: 30px;
|
||||
--td-line-height-link-small: 22px;
|
||||
--td-line-height-link-medium: 24px;
|
||||
--td-line-height-link-large: 26px;
|
||||
--td-line-height-mark-small: 22px;
|
||||
--td-line-height-mark-medium: 24px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* ④ 组件级微调(清晰蓝 · 卡片工作台)
|
||||
* ------------------------------------------------------------
|
||||
* 一条贯穿全站的原则:
|
||||
* 内容容器(卡片/面板)→ 白底 + 细描边 + 轻投影,与页面底拉开层次
|
||||
* 功能控件(输入框/按钮/下拉)→ 更实的描边 + 明确的聚焦反馈
|
||||
* 本版与上一版最大的区别就在这里:卡片重新「有形」,不再是无框纸面。
|
||||
* ============================================================ */
|
||||
|
||||
/* 卡片:默认无投影,靠描边区分层次;hover 才浮起(避免整页都是悬浮块) */
|
||||
/* 卡片:轻投影 + 细描边;hover 时抬到第二档阴影 */
|
||||
.t-card {
|
||||
box-shadow: none;
|
||||
border-radius: var(--tk-radius-xl);
|
||||
box-shadow: var(--tk-shadow-1);
|
||||
}
|
||||
.t-card--bordered {
|
||||
border-color: var(--tk-border);
|
||||
box-shadow: none;
|
||||
}
|
||||
.t-card--shadow {
|
||||
box-shadow: var(--tk-shadow-2);
|
||||
}
|
||||
.t-card__header {
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
font-weight: 620;
|
||||
font-size: var(--tk-text-lg);
|
||||
letter-spacing: var(--tk-track-title);
|
||||
}
|
||||
.t-card__title {
|
||||
letter-spacing: var(--tk-track-title);
|
||||
}
|
||||
/* 卡片内边距放大一档;窄屏收回默认值,否则手机上内容可用宽度会不够 */
|
||||
.t-card .t-card__body {
|
||||
padding: var(--tk-space-5) var(--tk-space-6);
|
||||
}
|
||||
|
||||
/* 输入框:聚焦时用主色描边 + 柔和外发光,替代默认的粗描边 */
|
||||
/* 输入框 / 文本域:聚焦时用主色描边 + 柔和外发光 */
|
||||
.t-input__wrap,
|
||||
.t-textarea__inner {
|
||||
transition:
|
||||
@ -368,6 +489,12 @@ html[theme-mode='dark'] {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
/* 下拉/选择器的选项行:默认行高偏挤,放大后一屏可见项变少但扫读更快 */
|
||||
.t-select-option,
|
||||
.t-dropdown__item {
|
||||
font-size: var(--td-font-size-body-medium);
|
||||
}
|
||||
|
||||
/* 弹层:统一使用较大的圆角与柔和阴影 */
|
||||
.t-popup__content,
|
||||
.t-dialog,
|
||||
@ -375,5 +502,13 @@ html[theme-mode='dark'] {
|
||||
border-radius: var(--tk-radius-xl);
|
||||
}
|
||||
.t-dialog__header {
|
||||
font-weight: 600;
|
||||
font-weight: 620;
|
||||
letter-spacing: var(--tk-track-title);
|
||||
}
|
||||
|
||||
/* 窄屏:大内边距在手机上会挤掉内容宽度,这里整体收回到常规密度 */
|
||||
@media (max-width: 767px) {
|
||||
.t-card .t-card__body {
|
||||
padding: var(--tk-space-4);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,54 +4,87 @@
|
||||
* 站点唯一的「视觉真相」来源。所有自定义样式只允许引用本文件
|
||||
* 定义的 --tk-* 变量,禁止在组件里硬编码颜色/圆角/阴影。
|
||||
*
|
||||
* 风格定位:清晰蓝 · 卡片工作台(Clear Blue Workspace)
|
||||
* ------------------------------------------------------------
|
||||
* 与上一版「编辑感(青玉 + 大留白 + 发丝线)」的区别,也是本版的四条硬规则:
|
||||
* ① 字号整体放大一档:正文 16px 起步、说明文字不低于 13px,
|
||||
* 上一版 12/13/14 的密度在 1080p 以上屏幕上确实偏小。
|
||||
* ② 控件大于内容:输入框/按钮/下拉默认 40px 高、15px 文字,
|
||||
* 控件是「手上要碰的东西」,必须比正文更实、更大。
|
||||
* ③ 容器有边界:卡片用「白底 + 细描边 + 轻投影」与页面底拉开层次,
|
||||
* 不再追求上一版那种「一张纸铺到底」的无框版式。
|
||||
* ④ 主色单一:全站只有一个强调色(宝石蓝),不做第二强调色。
|
||||
*
|
||||
* 主题通过 html 上的属性决定:
|
||||
* theme-mode = light | dark → 由 theme store 计算后的「实际生效」主题
|
||||
* theme-lang = zh-CN | en-US | ja-JP | ko-KR → 与 lang 属性同步
|
||||
*
|
||||
* 中性色:冷调石板灰(不带暖黄),与青玉主色同色系。
|
||||
* 强调色:青玉 Jade(单一强调色,全站一致,不做第二强调色)。
|
||||
* 改动本文件前请先读 tdesign-theme.scss 顶部的桥接说明:
|
||||
* 这里只放「我们的」令牌,TDesign 组件的尺度与色阶在那边控制。
|
||||
* ============================================================ */
|
||||
|
||||
/* ---------- 与主题无关的常量令牌 ---------- */
|
||||
:root {
|
||||
/* 圆角规范(形状一致性:全站只用这一套)
|
||||
* 控件 8px / 小控件 6px / 弹层 10px / 卡片 14px / 大面板 20px / 胶囊 999px */
|
||||
--tk-radius-xs: 4px;
|
||||
--tk-radius-sm: 6px;
|
||||
--tk-radius-md: 8px;
|
||||
--tk-radius-lg: 10px;
|
||||
--tk-radius-xl: 14px;
|
||||
--tk-radius-2xl: 20px;
|
||||
* 控件 10px / 小控件 7px / 弹层 12px / 卡片 16px / 大面板 22px / 胶囊 999px
|
||||
* 比上一版整体加大一档,配合放大的字号,观感更圆润友好。 */
|
||||
--tk-radius-xs: 5px;
|
||||
--tk-radius-sm: 7px;
|
||||
--tk-radius-md: 10px;
|
||||
--tk-radius-lg: 12px;
|
||||
--tk-radius-xl: 16px;
|
||||
--tk-radius-2xl: 22px;
|
||||
--tk-radius-pill: 999px;
|
||||
|
||||
/* 间距节奏(8px 基准) */
|
||||
/* 间距节奏(4px 基准,常用档位是 4 的倍数)
|
||||
* 本版留白比「编辑感」版式收敛:字号已经变大,留白再拉满会让页面显得空。 */
|
||||
--tk-space-1: 4px;
|
||||
--tk-space-2: 8px;
|
||||
--tk-space-3: 12px;
|
||||
--tk-space-4: 16px;
|
||||
--tk-space-5: 24px;
|
||||
--tk-space-6: 32px;
|
||||
--tk-space-7: 48px;
|
||||
--tk-space-8: 64px;
|
||||
--tk-space-5: 20px;
|
||||
--tk-space-6: 28px;
|
||||
--tk-space-7: 40px;
|
||||
--tk-space-8: 56px;
|
||||
/* space-9 专门用于「分区之间」的留白 */
|
||||
--tk-space-9: 68px;
|
||||
|
||||
/* 版心与栅格 */
|
||||
--tk-shell-max: 1560px;
|
||||
--tk-content-max: 1120px;
|
||||
--tk-header-h: 60px;
|
||||
--tk-aside-w: 248px;
|
||||
--tk-shell-max: 1600px;
|
||||
--tk-content-max: 1200px;
|
||||
--tk-header-h: 68px;
|
||||
--tk-aside-w: 268px;
|
||||
|
||||
/* 字号阶梯 */
|
||||
--tk-text-xs: 12px;
|
||||
--tk-text-sm: 13px;
|
||||
--tk-text-base: 14px;
|
||||
--tk-text-md: 15px;
|
||||
--tk-text-lg: 17px;
|
||||
--tk-text-xl: 20px;
|
||||
--tk-text-2xl: 26px;
|
||||
/* 字号阶梯(本版的核心改动:整体上移一档)
|
||||
* 旧 → 新:12→13 / 13→14 / 14→16 / 15→17 / 17→19 / 20→22 / 26→28 ...
|
||||
* 请勿在组件里退回 12px:那正是上一版「字太小」的根因。 */
|
||||
--tk-text-xs: 13px;
|
||||
--tk-text-sm: 14px;
|
||||
--tk-text-base: 16px;
|
||||
--tk-text-md: 17px;
|
||||
--tk-text-lg: 19px;
|
||||
--tk-text-xl: 22px;
|
||||
--tk-text-2xl: 28px;
|
||||
--tk-text-3xl: 34px;
|
||||
--tk-text-4xl: 44px;
|
||||
--tk-text-4xl: 42px;
|
||||
--tk-text-5xl: 52px;
|
||||
|
||||
/* 数字等宽(统计值用,避免数字跳动) */
|
||||
/* 行高与字距
|
||||
* 规则成对使用:大字标题用 leading-title + 负字距;正文用 leading-body + 正常字距。
|
||||
* 本版负字距比编辑感版式收得更小(-0.022em),因为字号变大后过紧的中文标题会糊。 */
|
||||
--tk-leading-display: 1.12;
|
||||
--tk-leading-title: 1.3;
|
||||
--tk-leading-body: 1.72;
|
||||
--tk-track-display: -0.022em;
|
||||
--tk-track-title: -0.012em;
|
||||
|
||||
/* 展示字体:中文环境下「可用的展示字体」就是系统字体本身
|
||||
*(PingFang / 微软雅黑 的中黑字重在标题上已经足够有性格)。
|
||||
* 引入网络 CJK 展示字体会让首屏多出 2MB+ 负担,得不偿失。
|
||||
* 因此这里显式声明为与正文同族,留出未来替换的单一改点。 */
|
||||
--tk-font-display: var(--tk-font-sans);
|
||||
|
||||
/* 数字等宽(统计值/时间用,避免数字跳动) */
|
||||
--tk-num-font: ui-monospace, 'SF Mono', 'JetBrains Mono', 'Cascadia Mono', Consolas,
|
||||
'Liberation Mono', monospace;
|
||||
|
||||
@ -65,7 +98,7 @@
|
||||
--tk-z-toast: 400;
|
||||
--tk-z-palette: 500;
|
||||
|
||||
/* 动效曲线:进出对称的缓出曲线,时长克制(MOTION 档位 4/10) */
|
||||
/* 动效曲线:进出对称的缓出曲线,时长克制 */
|
||||
--tk-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
--tk-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
||||
--tk-dur-fast: 140ms;
|
||||
@ -102,102 +135,104 @@ html {
|
||||
* 浅色主题
|
||||
* ============================================================ */
|
||||
:root,
|
||||
html[theme-mode='light'] {
|
||||
:root[theme-mode='light'] {
|
||||
color-scheme: light;
|
||||
|
||||
/* 强调色:青玉。与白底对比度 5.19:1(正文级 AA 通过) */
|
||||
--tk-accent: #0c7c59;
|
||||
--tk-accent-hover: #0a6b4c;
|
||||
--tk-accent-active: #085a40;
|
||||
--tk-accent-ring: rgba(12, 124, 89, 0.24);
|
||||
/* 低饱和青玉底纹:分类图标底、选中态、标签底 */
|
||||
--tk-accent-surface: rgba(12, 124, 89, 0.08);
|
||||
--tk-accent-surface-strong: rgba(12, 124, 89, 0.14);
|
||||
/* 强调色:宝石蓝。与白底对比 5.17:1(正文级 AA 通过) */
|
||||
--tk-accent: #2563eb;
|
||||
--tk-accent-hover: #1d4ed8;
|
||||
--tk-accent-active: #1e40af;
|
||||
/* 强调文字(链接/图标):比按钮底色略深,保证在白底上的可读性 */
|
||||
--tk-accent-text: #1d4ed8;
|
||||
--tk-accent-ring: rgba(37, 99, 235, 0.22);
|
||||
/* 低饱和蓝底纹:分类图标底、选中态、标签底 */
|
||||
--tk-accent-surface: rgba(37, 99, 235, 0.07);
|
||||
--tk-accent-surface-strong: rgba(37, 99, 235, 0.13);
|
||||
|
||||
/* 中性表面(冷调) */
|
||||
--tk-bg: #f6f8f8;
|
||||
--tk-surface: #fcfdfd;
|
||||
/* 中性表面(冷调蓝灰,与主色同色系)
|
||||
* 页面底刻意比容器暗一档:卡片要靠这个差值「浮」起来,而不是只靠投影。 */
|
||||
--tk-bg: #f4f6fb;
|
||||
--tk-surface: #ffffff;
|
||||
--tk-surface-raised: #ffffff;
|
||||
--tk-surface-sunken: #f1f4f3;
|
||||
--tk-surface-hover: #f3f6f5;
|
||||
--tk-surface-active: #ebefee;
|
||||
--tk-surface-sunken: #eef2f9;
|
||||
--tk-surface-hover: #f2f6fd;
|
||||
--tk-surface-active: #e6edfa;
|
||||
|
||||
/* 描边 */
|
||||
--tk-border: #e3e8e6;
|
||||
--tk-border-strong: #d3dad7;
|
||||
--tk-border-subtle: #eef1f0;
|
||||
--tk-border: #e2e8f3;
|
||||
--tk-border-strong: #ccd7e8;
|
||||
--tk-border-subtle: #edf1f8;
|
||||
/* 发丝线:只用于列表条目的分隔线(历史列表、首页索引),
|
||||
* 不用于容器边界——容器边界请用 --tk-border。 */
|
||||
--tk-hairline: #e9eef7;
|
||||
|
||||
/* 文字:primary 16.6:1 / secondary 5.5:1 / placeholder 4.6:1(均过 AA) */
|
||||
--tk-text: #111a17;
|
||||
--tk-text-secondary: #5b6763;
|
||||
--tk-text-placeholder: #66736f;
|
||||
/* 文字:primary 高对比 / secondary 与 placeholder 均过 AA */
|
||||
--tk-text: #0f1729;
|
||||
--tk-text-secondary: #55617a;
|
||||
--tk-text-placeholder: #6e7994;
|
||||
--tk-text-on-accent: #ffffff;
|
||||
|
||||
/* 语义色(仅用于状态,不参与品牌表达) */
|
||||
--tk-success: #0c7c59;
|
||||
--tk-warning: #9a6212;
|
||||
--tk-danger: #b4302c;
|
||||
--tk-info: #1d5fa8;
|
||||
--tk-success: #047857;
|
||||
--tk-warning: #b45309;
|
||||
--tk-danger: #b91c1c;
|
||||
--tk-info: #1d4ed8;
|
||||
|
||||
/* 阴影:带背景色相的冷调投影,不用纯黑 */
|
||||
--tk-shadow-1: 0 1px 2px rgba(16, 32, 28, 0.05), 0 2px 6px rgba(16, 32, 28, 0.04);
|
||||
--tk-shadow-2: 0 4px 12px rgba(16, 32, 28, 0.07), 0 12px 28px rgba(16, 32, 28, 0.05);
|
||||
--tk-shadow-3: 0 8px 24px rgba(16, 32, 28, 0.09), 0 24px 56px rgba(16, 32, 28, 0.07);
|
||||
--tk-shadow-1: 0 1px 2px rgba(15, 23, 41, 0.06), 0 1px 3px rgba(15, 23, 41, 0.05);
|
||||
--tk-shadow-2: 0 4px 10px rgba(15, 23, 41, 0.08), 0 12px 24px rgba(15, 23, 41, 0.06);
|
||||
--tk-shadow-3: 0 8px 20px rgba(15, 23, 41, 0.1), 0 24px 48px rgba(15, 23, 41, 0.08);
|
||||
|
||||
/* 顶栏/侧栏的半透明玻璃底 */
|
||||
--tk-glass: rgba(252, 253, 253, 0.82);
|
||||
--tk-glass-border: rgba(17, 26, 23, 0.07);
|
||||
--tk-glass: rgba(255, 255, 255, 0.82);
|
||||
--tk-glass-border: rgba(15, 23, 41, 0.08);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 深色主题
|
||||
* ============================================================ */
|
||||
html[theme-mode='dark'] {
|
||||
:root[theme-mode='dark'] {
|
||||
color-scheme: dark;
|
||||
|
||||
/* 强调色:深色下按钮底压深到 #0e8460,与白字对比 4.68:1;
|
||||
* 正文/链接用更亮的 #3fc79b,与页面底 8.85:1 */
|
||||
--tk-accent: #0e8460;
|
||||
--tk-accent-hover: #10946b;
|
||||
--tk-accent-active: #0c6f51;
|
||||
--tk-accent-text: #3fc79b;
|
||||
--tk-accent-ring: rgba(63, 199, 155, 0.3);
|
||||
--tk-accent-surface: rgba(63, 199, 155, 0.12);
|
||||
--tk-accent-surface-strong: rgba(63, 199, 155, 0.2);
|
||||
/* 强调色:深色下按钮底保持 #2563eb(与白字 5.17:1);
|
||||
* 链接/图标另用更亮的 #7aa8ff,保证在深底上可读。 */
|
||||
--tk-accent: #2563eb;
|
||||
--tk-accent-hover: #3b82f6;
|
||||
--tk-accent-active: #1d4ed8;
|
||||
--tk-accent-text: #7aa8ff;
|
||||
--tk-accent-ring: rgba(122, 168, 255, 0.3);
|
||||
--tk-accent-surface: rgba(122, 168, 255, 0.12);
|
||||
--tk-accent-surface-strong: rgba(122, 168, 255, 0.2);
|
||||
|
||||
--tk-bg: #0c100f;
|
||||
--tk-surface: #131817;
|
||||
--tk-surface-raised: #171d1b;
|
||||
--tk-surface-sunken: #171d1b;
|
||||
--tk-surface-hover: #1a201e;
|
||||
--tk-surface-active: #202725;
|
||||
--tk-bg: #0b0f18;
|
||||
--tk-surface: #131926;
|
||||
--tk-surface-raised: #18202f;
|
||||
--tk-surface-sunken: #18202f;
|
||||
--tk-surface-hover: #1b2333;
|
||||
--tk-surface-active: #222c3f;
|
||||
|
||||
--tk-border: #252d2b;
|
||||
--tk-border-strong: #333c39;
|
||||
--tk-border-subtle: #1d2422;
|
||||
--tk-border: #263147;
|
||||
--tk-border-strong: #35415c;
|
||||
--tk-border-subtle: #1d2536;
|
||||
/* 深色下取比卡片底(#131926)略亮一档,才看得见分隔线但不刺眼 */
|
||||
--tk-hairline: #212a3c;
|
||||
|
||||
/* primary 高对比 / secondary 8.46:1 / placeholder 5.21:1 */
|
||||
--tk-text: #e8edeb;
|
||||
--tk-text-secondary: #a3afab;
|
||||
--tk-text-placeholder: #7c8885;
|
||||
--tk-text: #e9eef8;
|
||||
--tk-text-secondary: #a4afc5;
|
||||
--tk-text-placeholder: #7d88a0;
|
||||
--tk-text-on-accent: #ffffff;
|
||||
|
||||
--tk-success: #3fc79b;
|
||||
--tk-warning: #d9a441;
|
||||
--tk-danger: #f08a84;
|
||||
--tk-info: #6fa8e6;
|
||||
--tk-success: #34d399;
|
||||
--tk-warning: #fbbf24;
|
||||
--tk-danger: #f87171;
|
||||
--tk-info: #7aa8ff;
|
||||
|
||||
--tk-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.3);
|
||||
--tk-shadow-2: 0 4px 12px rgba(0, 0, 0, 0.45), 0 12px 28px rgba(0, 0, 0, 0.35);
|
||||
--tk-shadow-3: 0 8px 24px rgba(0, 0, 0, 0.5), 0 24px 56px rgba(0, 0, 0, 0.4);
|
||||
--tk-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.32);
|
||||
--tk-shadow-2: 0 4px 10px rgba(0, 0, 0, 0.45), 0 12px 24px rgba(0, 0, 0, 0.35);
|
||||
--tk-shadow-3: 0 8px 20px rgba(0, 0, 0, 0.5), 0 24px 48px rgba(0, 0, 0, 0.4);
|
||||
|
||||
--tk-glass: rgba(19, 24, 23, 0.82);
|
||||
--tk-glass-border: rgba(232, 237, 235, 0.08);
|
||||
}
|
||||
|
||||
/* 浅色主题下的强调文字色(深色主题单独覆盖) */
|
||||
html[theme-mode='light'] {
|
||||
--tk-accent-text: #0c7c59;
|
||||
--tk-glass: rgba(19, 25, 38, 0.82);
|
||||
--tk-glass-border: rgba(233, 238, 248, 0.09);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
|
||||
@ -17,7 +17,10 @@ import { AppIcon } from 'tdesign-icons-vue-next'
|
||||
*
|
||||
* 图标刻意复用 TDesign 图标库的 AppIcon(九宫格语义,贴合「工具箱」),
|
||||
* 而不是手绘 SVG —— 手绘图标在不同字号下容易变形,也不便与其它图标统一线宽。
|
||||
* 底纹使用青玉渐变,是整站唯一使用渐变的位置,用于强化品牌记忆点。
|
||||
*
|
||||
* 编辑感版式下品牌标记改用「实色小方块 + 文字标」:
|
||||
* 原来的青玉渐变在这套版式里是唯一一处高饱和色块,会把视线从标题上拽走;
|
||||
* 实色方块更安静,品牌的辨识度改由字重与字距承担(标题族同源)。
|
||||
*/
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
@ -26,34 +29,36 @@ const { t } = useI18n()
|
||||
.brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: 9px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--tk-radius-md);
|
||||
font-size: 19px;
|
||||
color: #fff;
|
||||
/* 唯一渐变:青玉由深到浅,与强调色同色系 */
|
||||
background: linear-gradient(140deg, var(--tk-accent) 0%, #2fb98d 100%);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
|
||||
transition: transform var(--tk-dur-base) var(--tk-ease-out);
|
||||
font-size: 21px;
|
||||
color: var(--tk-text-on-accent);
|
||||
background: var(--tk-accent);
|
||||
transition:
|
||||
background-color var(--tk-dur-base) var(--tk-ease-out),
|
||||
transform var(--tk-dur-base) var(--tk-ease-out);
|
||||
}
|
||||
.brand:hover .brand-mark {
|
||||
transform: rotate(-6deg) scale(1.04);
|
||||
background: var(--tk-accent-hover);
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
font-family: var(--tk-font-display);
|
||||
font-size: var(--tk-text-lg);
|
||||
font-weight: 650;
|
||||
letter-spacing: -0.02em;
|
||||
letter-spacing: var(--tk-track-title);
|
||||
color: var(--tk-text);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@ -252,8 +252,8 @@ onBeforeUnmount(() => {
|
||||
|
||||
.palette-panel {
|
||||
width: 100%;
|
||||
max-width: 620px;
|
||||
max-height: 68vh;
|
||||
max-width: 680px;
|
||||
max-height: 70vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
@ -267,14 +267,14 @@ onBeforeUnmount(() => {
|
||||
.palette-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 0 12px 0 16px;
|
||||
height: 56px;
|
||||
gap: 12px;
|
||||
padding: 0 14px 0 18px;
|
||||
height: 64px;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid var(--tk-border-subtle);
|
||||
}
|
||||
.search-icon {
|
||||
font-size: 18px;
|
||||
font-size: 20px;
|
||||
color: var(--tk-text-placeholder);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@ -286,7 +286,7 @@ onBeforeUnmount(() => {
|
||||
outline: none;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: var(--tk-text-md);
|
||||
font-size: var(--tk-text-lg);
|
||||
color: var(--tk-text);
|
||||
}
|
||||
.search-input::placeholder {
|
||||
@ -294,17 +294,17 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.close-btn {
|
||||
flex-shrink: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
border-radius: var(--tk-radius-sm);
|
||||
border-radius: var(--tk-radius-md);
|
||||
background: transparent;
|
||||
color: var(--tk-text-placeholder);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.close-btn:hover {
|
||||
background: var(--tk-surface-hover);
|
||||
@ -315,15 +315,16 @@ onBeforeUnmount(() => {
|
||||
.palette-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 6px;
|
||||
padding: 8px;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
.palette-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 9px 12px;
|
||||
gap: 14px;
|
||||
min-height: 58px;
|
||||
padding: 10px 14px;
|
||||
border: none;
|
||||
border-radius: var(--tk-radius-md);
|
||||
background: transparent;
|
||||
@ -337,13 +338,13 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.item-icon {
|
||||
flex-shrink: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--tk-radius-sm);
|
||||
font-size: 17px;
|
||||
border-radius: var(--tk-radius-md);
|
||||
font-size: 19px;
|
||||
color: var(--tk-accent-text);
|
||||
background: var(--tk-surface-sunken);
|
||||
}
|
||||
@ -355,7 +356,7 @@ onBeforeUnmount(() => {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
gap: 2px;
|
||||
}
|
||||
.item-name {
|
||||
font-size: var(--tk-text-base);
|
||||
@ -366,7 +367,7 @@ onBeforeUnmount(() => {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.item-desc {
|
||||
font-size: var(--tk-text-xs);
|
||||
font-size: var(--tk-text-sm);
|
||||
color: var(--tk-text-placeholder);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@ -374,9 +375,9 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.item-badge {
|
||||
flex-shrink: 0;
|
||||
padding: 1px 7px;
|
||||
padding: 2px 9px;
|
||||
border-radius: var(--tk-radius-pill);
|
||||
font-size: 11px;
|
||||
font-size: var(--tk-text-xs);
|
||||
font-weight: 500;
|
||||
color: var(--tk-accent-text);
|
||||
background: var(--tk-accent-surface-strong);
|
||||
@ -408,8 +409,8 @@ onBeforeUnmount(() => {
|
||||
.palette-foot {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
padding: 8px 16px;
|
||||
gap: 18px;
|
||||
padding: 10px 18px;
|
||||
border-top: 1px solid var(--tk-border-subtle);
|
||||
background: var(--tk-surface);
|
||||
font-size: var(--tk-text-xs);
|
||||
@ -418,20 +419,20 @@ onBeforeUnmount(() => {
|
||||
.foot-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 5px;
|
||||
}
|
||||
.palette-foot kbd {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
padding: 0 4px;
|
||||
min-width: 22px;
|
||||
height: 22px;
|
||||
padding: 0 5px;
|
||||
border: 1px solid var(--tk-border);
|
||||
border-radius: var(--tk-radius-xs);
|
||||
background: var(--tk-surface-raised);
|
||||
font-family: inherit;
|
||||
font-size: 11px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
<div v-if="props.items.length" class="history-panel">
|
||||
<div class="history-head">
|
||||
<span class="history-title">历史记录</span>
|
||||
<t-button variant="text" size="small" theme="danger" @click="emit('clear')">清空</t-button>
|
||||
<t-button variant="text" theme="danger" class="history-clear" @click="emit('clear')">
|
||||
清空
|
||||
</t-button>
|
||||
</div>
|
||||
<ul class="history-list">
|
||||
<li
|
||||
@ -56,19 +58,29 @@ function formatTime(ts: number): string {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 历史记录跟随全站的编辑感版式:条目之间用发丝线分行,
|
||||
* 不再给每条历史套一个描边盒子(列表一长就会变成一排小方块)。
|
||||
* 注意这里必须用 --tk-* 令牌,写死色值会在昼夜切换时花掉。 */
|
||||
.history-panel {
|
||||
margin-top: 16px;
|
||||
margin-top: var(--tk-space-6);
|
||||
}
|
||||
.history-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
gap: 12px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.history-title {
|
||||
font-size: 14px;
|
||||
font-size: var(--tk-text-base);
|
||||
font-weight: 600;
|
||||
color: var(--td-text-color-primary);
|
||||
letter-spacing: var(--tk-track-title);
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
/* 清空按钮:给足热区,但压住高度避免把标题行撑得过重 */
|
||||
.history-clear {
|
||||
min-height: 34px;
|
||||
font-size: var(--tk-text-sm);
|
||||
}
|
||||
.history-list {
|
||||
margin: 0;
|
||||
@ -78,17 +90,12 @@ function formatTime(ts: number): string {
|
||||
.history-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
margin-bottom: 6px;
|
||||
border: 1px solid var(--td-border-level-1-color);
|
||||
border-radius: 6px;
|
||||
background: var(--td-bg-color-container);
|
||||
gap: 12px;
|
||||
min-height: 48px;
|
||||
padding: 10px 2px;
|
||||
border-top: 1px solid var(--tk-hairline);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
.history-item:hover {
|
||||
border-color: var(--td-brand-color);
|
||||
transition: color var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.history-text {
|
||||
flex: 1;
|
||||
@ -96,15 +103,25 @@ function formatTime(ts: number): string {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
color: var(--td-text-color-primary);
|
||||
font-size: var(--tk-text-base);
|
||||
color: var(--tk-text-secondary);
|
||||
transition: color var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.history-item:hover .history-text {
|
||||
color: var(--tk-accent-text);
|
||||
}
|
||||
.history-time {
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
color: var(--td-text-color-placeholder);
|
||||
font-family: var(--tk-num-font);
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
/* 历史条目里的删除按钮热区撑到 34px,避免在密集列表里点不准 */
|
||||
.history-del {
|
||||
flex-shrink: 0;
|
||||
min-width: 34px;
|
||||
min-height: 34px;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -66,9 +66,9 @@ function onSelect(item: { value?: string | number }) {
|
||||
.switch-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 0 8px;
|
||||
height: 34px;
|
||||
gap: 5px;
|
||||
padding: 0 12px;
|
||||
height: 40px;
|
||||
color: var(--tk-text-secondary);
|
||||
border-radius: var(--tk-radius-md);
|
||||
}
|
||||
@ -77,15 +77,15 @@ function onSelect(item: { value?: string | number }) {
|
||||
background: var(--tk-surface-hover);
|
||||
}
|
||||
.switch-text {
|
||||
font-size: var(--tk-text-sm);
|
||||
font-size: var(--tk-text-base);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
/* 语言代码固定宽度,切换时按钮不会左右跳动 */
|
||||
min-width: 22px;
|
||||
min-width: 26px;
|
||||
text-align: center;
|
||||
}
|
||||
.switch-caret {
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
/* 窄屏只留图标,避免顶栏拥挤 */
|
||||
|
||||
@ -57,9 +57,9 @@ function onSelect(item: { value?: string | number }) {
|
||||
.switch-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 0 8px;
|
||||
height: 34px;
|
||||
gap: 5px;
|
||||
padding: 0 12px;
|
||||
height: 40px;
|
||||
color: var(--tk-text-secondary);
|
||||
border-radius: var(--tk-radius-md);
|
||||
}
|
||||
@ -68,11 +68,11 @@ function onSelect(item: { value?: string | number }) {
|
||||
background: var(--tk-surface-hover);
|
||||
}
|
||||
.switch-text {
|
||||
font-size: var(--tk-text-sm);
|
||||
font-size: var(--tk-text-base);
|
||||
font-weight: 500;
|
||||
}
|
||||
.switch-caret {
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
<span class="name" :title="f.name">{{ f.name }}</span>
|
||||
<span class="size">{{ formatBytes(f.size) }}</span>
|
||||
</span>
|
||||
<t-button variant="text" size="small" theme="danger" class="remove-btn" @click.stop="removeAt(i)">
|
||||
<t-button variant="text" theme="danger" class="remove-btn" @click.stop="removeAt(i)">
|
||||
<template #icon><DeleteIcon /></template>
|
||||
</t-button>
|
||||
</div>
|
||||
@ -192,13 +192,13 @@ function removeAt(i: number) {
|
||||
.drop-zone {
|
||||
position: relative;
|
||||
border: 2px dashed var(--td-border-level-2-color);
|
||||
border-radius: 8px;
|
||||
padding: 24px 16px;
|
||||
border-radius: var(--tk-radius-lg);
|
||||
padding: 32px 20px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
background-color 0.2s;
|
||||
border-color var(--tk-dur-fast) var(--tk-ease-out),
|
||||
background-color var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.drop-zone:hover,
|
||||
.drop-zone.dragging {
|
||||
@ -209,17 +209,17 @@ function removeAt(i: number) {
|
||||
display: none;
|
||||
}
|
||||
.empty-icon {
|
||||
font-size: 40px;
|
||||
font-size: 46px;
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
.tip {
|
||||
margin: 8px 0 4px;
|
||||
font-size: 14px;
|
||||
margin: 10px 0 6px;
|
||||
font-size: var(--tk-text-md);
|
||||
color: var(--td-text-color-primary);
|
||||
}
|
||||
.sub {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-sm);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.drop-overlay {
|
||||
@ -233,10 +233,10 @@ function removeAt(i: number) {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
gap: 10px;
|
||||
border-radius: var(--tk-radius-lg);
|
||||
font-size: var(--tk-text-md);
|
||||
font-weight: 550;
|
||||
color: var(--td-brand-color);
|
||||
background: var(--td-brand-color-light);
|
||||
pointer-events: none;
|
||||
@ -244,22 +244,22 @@ function removeAt(i: number) {
|
||||
.file-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 10px;
|
||||
gap: 12px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--td-border-level-1-color);
|
||||
border-radius: 6px;
|
||||
border-radius: var(--tk-radius-md);
|
||||
background: var(--td-bg-color-container);
|
||||
}
|
||||
.thumb {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 6px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: var(--tk-radius-sm);
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
border: 1px solid var(--td-border-level-1-color);
|
||||
@ -268,14 +268,14 @@ function removeAt(i: number) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
font-size: 24px;
|
||||
color: var(--td-text-color-placeholder);
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
}
|
||||
/* 可点击预览的缩略图 */
|
||||
.thumb-clickable {
|
||||
cursor: zoom-in;
|
||||
transition: transform 0.15s;
|
||||
transition: transform var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.thumb-clickable:hover {
|
||||
transform: scale(1.06);
|
||||
@ -285,25 +285,30 @@ function removeAt(i: number) {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
gap: 3px;
|
||||
}
|
||||
.name {
|
||||
font-size: 13px;
|
||||
font-size: var(--tk-text-base);
|
||||
color: var(--td-text-color-primary);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.size {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-sm);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
/* 删除按钮:t-button size=small 在这版主题下只有 24px,热区太小,
|
||||
* 这里把它撑到 36px,仍保持「图标按钮」的形态 */
|
||||
.remove-btn {
|
||||
flex-shrink: 0;
|
||||
min-width: 36px;
|
||||
min-height: 36px;
|
||||
font-size: 17px;
|
||||
}
|
||||
.add-more {
|
||||
margin: 2px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-sm);
|
||||
color: var(--td-text-color-placeholder);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -63,11 +63,8 @@ export default {
|
||||
},
|
||||
|
||||
home: {
|
||||
eyebrow: 'Runs locally in your browser',
|
||||
title: 'A toolbox that lives in your browser',
|
||||
desc: '{count} everyday utilities. Every calculation and file operation happens on your own machine.',
|
||||
ctaPrimary: 'Browse all tools',
|
||||
ctaSecondary: 'Server tools',
|
||||
statTools: 'Available tools',
|
||||
statCategories: 'Categories',
|
||||
statUpload: 'Data uploaded to a server',
|
||||
|
||||
@ -63,11 +63,8 @@ export default {
|
||||
},
|
||||
|
||||
home: {
|
||||
eyebrow: 'ブラウザ内で動作',
|
||||
title: 'ブラウザだけで使えるツールボックス',
|
||||
desc: '{count} の日常ツール。計算もファイル処理もすべて端末内で完結します。',
|
||||
ctaPrimary: 'すべてのツール',
|
||||
ctaSecondary: 'サーバー連携ツール',
|
||||
statTools: '利用できるツール',
|
||||
statCategories: 'カテゴリ',
|
||||
statUpload: 'サーバーへの送信データ',
|
||||
|
||||
@ -63,11 +63,8 @@ export default {
|
||||
},
|
||||
|
||||
home: {
|
||||
eyebrow: '브라우저에서 바로 실행',
|
||||
title: '브라우저만 있으면 되는 툴박스',
|
||||
desc: '일상 도구 {count}개. 계산과 파일 처리가 모두 내 기기에서 끝납니다.',
|
||||
ctaPrimary: '전체 도구 보기',
|
||||
ctaSecondary: '서버 연동 도구',
|
||||
statTools: '사용 가능한 도구',
|
||||
statCategories: '분류',
|
||||
statUpload: '서버로 전송되는 데이터',
|
||||
|
||||
@ -76,11 +76,8 @@ export default {
|
||||
},
|
||||
|
||||
home: {
|
||||
eyebrow: '浏览器本地运行',
|
||||
title: '在浏览器里就能用的工具箱',
|
||||
desc: '{count} 个日常工具,计算与文件处理全部在本机完成,数据不上传服务器。',
|
||||
ctaPrimary: '浏览全部工具',
|
||||
ctaSecondary: '服务端工具',
|
||||
statTools: '可用工具',
|
||||
statCategories: '工具分类',
|
||||
statUpload: '上传到服务器的数据',
|
||||
|
||||
@ -75,7 +75,7 @@
|
||||
<t-drawer
|
||||
v-model:visible="drawerVisible"
|
||||
placement="left"
|
||||
:size="272"
|
||||
:size="300"
|
||||
:footer="false"
|
||||
:header="false"
|
||||
:close-btn="true"
|
||||
@ -103,13 +103,16 @@ import SideNav from './SideNav.vue'
|
||||
/**
|
||||
* 应用外壳布局。
|
||||
*
|
||||
* 结构:固定顶栏(60px)+ 左侧固定导航(248px)+ 内容区 + 页脚。
|
||||
* 结构:固定顶栏(68px)+ 左侧固定导航(268px)+ 内容区 + 页脚。
|
||||
* 移动端(<768px)侧栏换成抽屉,顶栏保留品牌、命令面板与主题/语言切换。
|
||||
*
|
||||
* 版心约定:
|
||||
* --tk-shell-max = 1560px 顶栏 / 页脚 / 主体三者的最大宽度
|
||||
* --tk-content-max = 1120px 内容型工具页的版心(由 page-wrap 约束)
|
||||
* --tk-shell-max = 1600px 顶栏 / 页脚 / 主体三者的最大宽度
|
||||
* --tk-content-max = 1200px 内容型工具页的版心(由 page-wrap 约束)
|
||||
* 首页因为要铺开卡片网格,会自行放宽,不受 page-wrap 限制。
|
||||
*
|
||||
* 尺寸一律跟随令牌(--tk-header-h / --tk-aside-w),不要在组件里写死 px:
|
||||
* 本版的整体放大是改令牌完成的,写死会让顶栏与内容脱节。
|
||||
*/
|
||||
const { t } = useI18n()
|
||||
const palette = useCommandPalette()
|
||||
@ -151,10 +154,10 @@ const shortcutLabel = computed(() => {
|
||||
height: 100%;
|
||||
max-width: var(--tk-shell-max);
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
padding: 0 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 14px;
|
||||
}
|
||||
.header-left {
|
||||
display: flex;
|
||||
@ -177,8 +180,8 @@ const shortcutLabel = computed(() => {
|
||||
|
||||
/* 顶栏图标按钮(移动端菜单) */
|
||||
.icon-btn {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -187,7 +190,7 @@ const shortcutLabel = computed(() => {
|
||||
background: transparent;
|
||||
color: var(--tk-text-secondary);
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
font-size: 20px;
|
||||
}
|
||||
.icon-btn:hover {
|
||||
background: var(--tk-surface-hover);
|
||||
@ -197,18 +200,22 @@ const shortcutLabel = computed(() => {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 命令面板入口:长得像搜索框,本质是按钮(点开面板而非直接输入) */
|
||||
/* 命令面板入口:长得像搜索框,本质是按钮(点开面板而非直接输入)。
|
||||
* 用无描边的「安静底纹」而不是带边框的输入框:
|
||||
* 它不是一个可以直接打字的输入框,画成输入框会让人先去点它再发现要跳面板。
|
||||
* 放大镜图标 + 占位文案 + 快捷键角标三重提示已经足够表达可搜索。
|
||||
* 高度 44px 与站点默认控件同高,顶栏一眼看去是一排等高的可点区域。 */
|
||||
.palette-trigger {
|
||||
width: 100%;
|
||||
max-width: 460px;
|
||||
height: 36px;
|
||||
max-width: 520px;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
padding: 0 10px 0 12px;
|
||||
border: 1px solid var(--tk-border);
|
||||
gap: 10px;
|
||||
padding: 0 10px 0 14px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--tk-radius-md);
|
||||
background: var(--tk-surface);
|
||||
background: var(--tk-surface-sunken);
|
||||
color: var(--tk-text-placeholder);
|
||||
font-family: inherit;
|
||||
font-size: var(--tk-text-base);
|
||||
@ -218,15 +225,14 @@ const shortcutLabel = computed(() => {
|
||||
background-color var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.palette-trigger:hover {
|
||||
border-color: var(--tk-border-strong);
|
||||
background: var(--tk-surface-hover);
|
||||
background: var(--tk-surface-active);
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
.palette-trigger:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
.trigger-icon {
|
||||
font-size: 16px;
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.trigger-text {
|
||||
@ -239,13 +245,13 @@ const shortcutLabel = computed(() => {
|
||||
}
|
||||
.trigger-kbd {
|
||||
flex-shrink: 0;
|
||||
padding: 1px 6px;
|
||||
padding: 2px 7px;
|
||||
border: 1px solid var(--tk-border);
|
||||
border-radius: var(--tk-radius-xs);
|
||||
background: var(--tk-surface-sunken);
|
||||
font-family: inherit;
|
||||
font-size: 11px;
|
||||
line-height: 16px;
|
||||
font-size: var(--tk-text-xs);
|
||||
line-height: 18px;
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
|
||||
@ -259,7 +265,10 @@ const shortcutLabel = computed(() => {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 左侧固定导航(PC 端) */
|
||||
/* 左侧固定导航(PC 端)
|
||||
* 侧栏与正文共用同一张页面底,仅靠留白与字号区分层级;
|
||||
* 导航项 hover 用 --tk-surface(浅色下更亮的白、深色下同样更亮),
|
||||
* 两种主题下都是「抬起」的方向感。 */
|
||||
.app-aside {
|
||||
width: var(--tk-aside-w);
|
||||
flex-shrink: 0;
|
||||
@ -268,31 +277,34 @@ const shortcutLabel = computed(() => {
|
||||
height: calc(100dvh - var(--tk-header-h));
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
border-right: 1px solid var(--tk-border);
|
||||
padding: 14px 10px 32px;
|
||||
background: var(--tk-surface);
|
||||
padding: 26px 20px 56px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* 内容区留白:字号放大后正文变宽,这里的留白比上一版收敛一档,
|
||||
* 避免 1080p 屏幕上正文被挤成窄窄一条。 */
|
||||
.app-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 28px 28px 56px;
|
||||
padding: 32px 48px 72px;
|
||||
}
|
||||
|
||||
/* ==================== 页脚 ==================== */
|
||||
/* ==================== 页脚 ====================
|
||||
* 承诺在左、备案号在右:两者是不同性质的信息(产品承诺 / 监管信息),
|
||||
* 居中堆叠会让它们看起来像同一组。 */
|
||||
.app-footer {
|
||||
border-top: 1px solid var(--tk-border);
|
||||
background: var(--tk-surface);
|
||||
border-top: 1px solid var(--tk-hairline);
|
||||
}
|
||||
.footer-inner {
|
||||
max-width: var(--tk-shell-max);
|
||||
margin: 0 auto;
|
||||
padding: 18px 16px;
|
||||
padding: 32px 24px 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: var(--tk-text-xs);
|
||||
justify-content: space-between;
|
||||
gap: 10px 24px;
|
||||
font-size: var(--tk-text-sm);
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
.footer-promises {
|
||||
@ -300,16 +312,16 @@ const shortcutLabel = computed(() => {
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 14px;
|
||||
gap: 16px;
|
||||
}
|
||||
.footer-divider {
|
||||
width: 1px;
|
||||
height: 11px;
|
||||
background: var(--tk-border-strong);
|
||||
height: 12px;
|
||||
background: var(--tk-border);
|
||||
}
|
||||
/* 备案号:监管信息,压到比产品承诺更弱的视觉层级,但仍要可读、可点 */
|
||||
.footer-icp {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--tk-text-placeholder);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid transparent;
|
||||
@ -322,6 +334,13 @@ const shortcutLabel = computed(() => {
|
||||
}
|
||||
|
||||
/* ==================== 响应式 ==================== */
|
||||
/* 中等宽度:收窄留白,但保持「正文两侧有空白」的基本观感 */
|
||||
@media (max-width: 1279px) {
|
||||
.app-content {
|
||||
padding: 28px 32px 64px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.app-aside {
|
||||
display: none;
|
||||
@ -330,17 +349,22 @@ const shortcutLabel = computed(() => {
|
||||
display: inline-flex;
|
||||
}
|
||||
.app-content {
|
||||
padding: 18px 14px 44px;
|
||||
padding: 20px 16px 56px;
|
||||
}
|
||||
.footer-inner {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 24px 16px 32px;
|
||||
}
|
||||
/* 移动端搜索入口收成图标按钮:整条搜索框会挤掉品牌与主题切换 */
|
||||
.palette-trigger {
|
||||
max-width: 36px;
|
||||
height: 34px;
|
||||
max-width: 40px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
font-size: 18px;
|
||||
font-size: 20px;
|
||||
}
|
||||
.palette-trigger:hover {
|
||||
background: var(--tk-surface-hover);
|
||||
|
||||
@ -96,6 +96,9 @@ import { toolName, toolDesc, categoryLabel } from '@/i18n/helpers'
|
||||
* - 需要联网的工具在右侧点一个小圆点标记,且这是全站唯一使用状态点的地方,
|
||||
* 语义明确(是否需联网),不是装饰。
|
||||
* - 筛选对「已翻译的名称」生效,因此英文/日文/韩文界面下用对应语言也能搜到。
|
||||
*
|
||||
* 尺寸:导航项行高 40px 起步(上一版 34px 偏小,鼠标点起来费劲),
|
||||
* 字号与正文同号,避免侧栏比正文「小一号」。
|
||||
*/
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
@ -171,24 +174,28 @@ const emit = defineEmits<{
|
||||
.side-nav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
/* ---------- 筛选框 ---------- */
|
||||
/* ---------- 筛选框 ----------
|
||||
* 只留一条底线而不是画一个框:侧栏里的框会和下方导航项的背景块打架,
|
||||
* 视觉上把「筛选」误读成「一个大按钮」。底线 + 聚焦变色已经足够提示可输入。
|
||||
* 高度 42px:与导航项同档,点起来有足够的热区。 */
|
||||
.nav-filter {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
height: 34px;
|
||||
margin-bottom: 10px;
|
||||
padding: 0 8px 0 10px;
|
||||
border: 1px solid var(--tk-border);
|
||||
border-radius: var(--tk-radius-md);
|
||||
background: var(--tk-surface-sunken);
|
||||
gap: 10px;
|
||||
height: 42px;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 2px;
|
||||
border-bottom: 1px solid var(--tk-border);
|
||||
transition: border-color var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.nav-filter:focus-within {
|
||||
border-bottom-color: var(--tk-accent);
|
||||
}
|
||||
.filter-icon {
|
||||
font-size: 15px;
|
||||
font-size: 17px;
|
||||
color: var(--tk-text-placeholder);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@ -199,7 +206,7 @@ const emit = defineEmits<{
|
||||
outline: none;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: var(--tk-text-sm);
|
||||
font-size: var(--tk-text-base);
|
||||
color: var(--tk-text);
|
||||
}
|
||||
.filter-input::placeholder {
|
||||
@ -207,33 +214,35 @@ const emit = defineEmits<{
|
||||
}
|
||||
.filter-clear {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
border-radius: var(--tk-radius-xs);
|
||||
border-radius: var(--tk-radius-sm);
|
||||
background: transparent;
|
||||
color: var(--tk-text-placeholder);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.filter-clear:hover {
|
||||
color: var(--tk-text);
|
||||
background: var(--tk-surface-active);
|
||||
}
|
||||
|
||||
/* ---------- 分组标题 ---------- */
|
||||
/* ---------- 分组标题 ----------
|
||||
* 分组之间靠 16px 留白拉开,不画横线;
|
||||
* 分类名是「目录行」,字号比导航项小一档但字距略开,扫读时先看到分组再看到工具。 */
|
||||
.nav-group {
|
||||
margin-top: 6px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.group-head {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 8px;
|
||||
padding: 8px 10px 8px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: var(--tk-radius-sm);
|
||||
@ -244,7 +253,6 @@ const emit = defineEmits<{
|
||||
}
|
||||
.group-head:hover {
|
||||
color: var(--tk-text-secondary);
|
||||
background: var(--tk-surface-hover);
|
||||
}
|
||||
.group-caret {
|
||||
font-size: 13px;
|
||||
@ -259,28 +267,32 @@ const emit = defineEmits<{
|
||||
min-width: 0;
|
||||
font-size: var(--tk-text-xs);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
/* 0.06em:中文分组名(如「工作效率」)在这个字距下刚好成组,
|
||||
* 再宽就会散成四个孤立的字。 */
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
.group-count {
|
||||
flex-shrink: 0;
|
||||
font-size: 11px;
|
||||
font-size: var(--tk-text-xs);
|
||||
font-variant-numeric: tabular-nums;
|
||||
opacity: 0.75;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ---------- 导航项 ---------- */
|
||||
/* ---------- 导航项 ----------
|
||||
* 行高 40px:这是全站点击最密集的列表,必须给足热区。
|
||||
* hover 用 --tk-surface(浅色下更亮、深色下也更亮)而不是 hover 灰,
|
||||
* 这样两种主题下都是同一种「抬起」的方向感。 */
|
||||
.group-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
gap: 2px;
|
||||
}
|
||||
.nav-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 7px 10px;
|
||||
gap: 11px;
|
||||
padding: 9px 12px;
|
||||
border-radius: var(--tk-radius-md);
|
||||
font-size: var(--tk-text-base);
|
||||
color: var(--tk-text-secondary);
|
||||
@ -290,32 +302,33 @@ const emit = defineEmits<{
|
||||
color var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.nav-item:hover {
|
||||
background: var(--tk-surface-hover);
|
||||
background: var(--tk-surface);
|
||||
color: var(--tk-text);
|
||||
}
|
||||
/* 活动态:青玉底纹 + 左侧 2px 指示条,比纯文字变色更容易在长列表里定位 */
|
||||
/* 活动态:主色底纹 + 左侧 3px 指示条,比纯文字变色更容易在长列表里定位。
|
||||
* 指示条压在条目左侧外 11px 处,贴在侧栏留白里,像书页边的一条书签。 */
|
||||
.nav-item.active {
|
||||
background: var(--tk-accent-surface-strong);
|
||||
background: var(--tk-accent-surface);
|
||||
color: var(--tk-accent-text);
|
||||
font-weight: 550;
|
||||
}
|
||||
.nav-item.active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -10px;
|
||||
left: -11px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
width: 3px;
|
||||
height: 18px;
|
||||
border-radius: var(--tk-radius-pill);
|
||||
background: var(--tk-accent);
|
||||
}
|
||||
.home-item {
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.nav-icon {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.nav-label {
|
||||
@ -327,8 +340,8 @@ const emit = defineEmits<{
|
||||
}
|
||||
/* 唯一的语义状态点:该工具需要联网 */
|
||||
.nav-dot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
background: var(--tk-accent);
|
||||
@ -337,27 +350,28 @@ const emit = defineEmits<{
|
||||
|
||||
/* ---------- 空状态 ---------- */
|
||||
.nav-empty {
|
||||
padding: 20px 10px;
|
||||
padding: 28px 10px;
|
||||
}
|
||||
.empty-title {
|
||||
margin: 0 0 4px;
|
||||
margin: 0 0 6px;
|
||||
font-size: var(--tk-text-sm);
|
||||
font-weight: 600;
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
.empty-hint {
|
||||
margin: 0 0 10px;
|
||||
margin: 0 0 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
line-height: 1.55;
|
||||
line-height: 1.6;
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
.empty-btn {
|
||||
padding: 5px 10px;
|
||||
min-height: 34px;
|
||||
padding: 6px 14px;
|
||||
border: 1px solid var(--tk-border);
|
||||
border-radius: var(--tk-radius-sm);
|
||||
border-radius: var(--tk-radius-md);
|
||||
background: var(--tk-surface);
|
||||
font-family: inherit;
|
||||
font-size: var(--tk-text-xs);
|
||||
font-size: var(--tk-text-sm);
|
||||
color: var(--tk-text-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<!-- ==================== 抬头区 ====================
|
||||
左对齐单列:这一页的主角是「快速找到工具」,不是品牌宣言,
|
||||
所以把真实的搜索框放进抬头区作为主操作,而不是摆两个装饰按钮。 -->
|
||||
编辑感版式里抬头区只留三件东西:标题、一句说明、真实可用的搜索框。
|
||||
不放 eyebrow 小标签(那是模板化开场,且和标题说的是同一件事),
|
||||
也不放第二个入口按钮:服务端工具的入口就在下方专区里,
|
||||
同一意图出现两次只会让人犹豫点哪个。 -->
|
||||
<section class="hero">
|
||||
<p class="hero-eyebrow">{{ t('home.eyebrow') }}</p>
|
||||
<h1 class="hero-title">{{ t('home.title') }}</h1>
|
||||
<p class="hero-desc">{{ t('home.desc', { count: tools.length }) }}</p>
|
||||
|
||||
@ -28,29 +29,28 @@
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<router-link to="/api-status" class="hero-link">
|
||||
{{ t('home.ctaSecondary') }}
|
||||
<ChevronRightIcon aria-hidden="true" />
|
||||
</router-link>
|
||||
</section>
|
||||
|
||||
<!-- ==================== 概览数字条 ====================
|
||||
四组数字用竖线分列,数字统一等宽,刷新时不会左右跳动。 -->
|
||||
<section v-if="!isFiltering" class="stats">
|
||||
<div v-for="stat in stats" :key="stat.label" class="stat">
|
||||
<span class="stat-value mono-num">{{ stat.value }}</span>
|
||||
<span class="stat-label">{{ stat.label }}</span>
|
||||
<!-- ==================== 事实条 ====================
|
||||
四组数字直接摊在纸面上,不加边框也不加底纹:
|
||||
数字本身就是视觉元素,再套一层容器反而把它们锁住了。
|
||||
数值一律等宽,切换语言或刷新时不会左右跳动。 -->
|
||||
<section v-if="!isFiltering" class="facts">
|
||||
<div v-for="stat in stats" :key="stat.label" class="fact">
|
||||
<span class="fact-value mono-num">{{ stat.value }}</span>
|
||||
<span class="fact-label">{{ stat.label }}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ==================== 服务端工具专区 ====================
|
||||
整块用青玉底纹与其它分区区分开:这是本期唯一需要联网、且有真实后端支撑的部分。
|
||||
整块用青玉底纹与其它分区区分开:这是唯一需要联网、且有真实后端支撑的部分。
|
||||
开始筛选时隐藏,避免与下方 online 分类重复出现同一批工具。 -->
|
||||
<section v-if="!isFiltering && serverTools.length" class="server-zone">
|
||||
<header class="zone-head">
|
||||
<div class="zone-heading">
|
||||
<h2 class="zone-title">{{ t('category.online.label') }}</h2>
|
||||
<p class="zone-desc">{{ t('category.online.desc') }}</p>
|
||||
</div>
|
||||
<router-link to="/api-status" class="zone-link">
|
||||
{{ t('page.api-status.title') }}
|
||||
<ChevronRightIcon aria-hidden="true" />
|
||||
@ -76,9 +76,10 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ==================== 分类分区 ====================
|
||||
56 个工具的目录页必然是重复的卡片网格,这里把重复做「整齐」而不是做「变化」:
|
||||
分区标题行统一结构,卡片网格统一列宽,用户可以靠固定的视觉节奏快速扫读。 -->
|
||||
<!-- ==================== 分类目录 ====================
|
||||
58 个工具不再做成 58 个盒子,而是像书末索引一样用发丝线分行:
|
||||
扫读时视线沿着一列文字往下走,比逐个辨认边框快得多,
|
||||
也让「大量同类条目」这件事本身成为版式的一部分,而不是一堆重复的框。 -->
|
||||
<section
|
||||
v-for="(group, index) in visibleGroups"
|
||||
:key="group.category.key"
|
||||
@ -86,30 +87,34 @@
|
||||
class="category"
|
||||
>
|
||||
<header class="category-head">
|
||||
<h2 class="category-title">{{ categoryLabel(group.category.key, group.category.label) }}</h2>
|
||||
<h2 class="category-title">
|
||||
{{ categoryLabel(group.category.key, group.category.label) }}
|
||||
<span class="category-count mono-num">
|
||||
{{ t('nav.toolsCount', { count: group.items.length }) }}
|
||||
</span>
|
||||
</h2>
|
||||
<p class="category-desc">{{ categoryDesc(group.category.key, group.category.desc) }}</p>
|
||||
<span class="category-count">{{ t('nav.toolsCount', { count: group.items.length }) }}</span>
|
||||
</header>
|
||||
|
||||
<div class="tool-grid">
|
||||
<div class="tool-index">
|
||||
<router-link
|
||||
v-for="tool in group.items"
|
||||
:key="tool.path"
|
||||
:to="`/${tool.path}`"
|
||||
class="tool-card"
|
||||
class="tool-entry"
|
||||
>
|
||||
<span class="card-head">
|
||||
<span class="card-icon"><component :is="tool.icon" aria-hidden="true" /></span>
|
||||
<span v-if="tool.needsNetwork" class="card-tag">{{ t('home.serverBadge') }}</span>
|
||||
<span class="entry-icon"><component :is="tool.icon" aria-hidden="true" /></span>
|
||||
<span class="entry-body">
|
||||
<span class="entry-name">{{ toolName(tool.path, tool.name) }}</span>
|
||||
<span class="entry-desc">{{ toolDesc(tool.path, tool.desc) }}</span>
|
||||
</span>
|
||||
<span class="card-name">{{ toolName(tool.path, tool.name) }}</span>
|
||||
<span class="card-desc">{{ toolDesc(tool.path, tool.desc) }}</span>
|
||||
<span v-if="tool.needsNetwork" class="entry-badge">{{ t('home.serverBadge') }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ==================== 筛选无结果 ====================
|
||||
给出可执行的下一步(换个关键词 / 一键重置),而不是只显示一句「无结果」。 -->
|
||||
给出可执行的下一步(换关键词 / 一键重置),而不是只显示一句「无结果」。 -->
|
||||
<div v-if="isFiltering && !visibleGroups.length" class="empty">
|
||||
<p class="empty-title">{{ t('home.emptyTitle') }}</p>
|
||||
<p class="empty-hint">{{ t('home.emptyHint') }}</p>
|
||||
@ -132,12 +137,13 @@ import { SUPPORTED_LOCALES } from '@/i18n'
|
||||
/**
|
||||
* 首页:工具目录。
|
||||
*
|
||||
* 版式取舍:
|
||||
* - 抬头区左对齐而不是居中。居中大标题在目录型产品上会把视线拉向空处,
|
||||
* 左对齐能让「标题 → 搜索框 → 卡片」形成一条自然的阅读动线。
|
||||
* - 抬头区放真实可用的搜索框(直接过滤下方列表),不是装饰性按钮。
|
||||
* - 服务端工具单独成区并加底纹,是本期唯一需要联网的部分,值得与本地工具区分。
|
||||
* - 筛选期间隐藏概览数字与服务端专区,避免同一批工具在一页里出现两次。
|
||||
* 版式取舍(编辑感方向):
|
||||
* - 抬头区左对齐、不放 eyebrow,标题的字号反差就是这一页的开场。
|
||||
* - 搜索框画成一条底线而不是一个输入框盒子,和整页的「纸面感」一致。
|
||||
* - 工具列表做成带发丝线的索引,而不是 58 个描边卡片:
|
||||
* 条目越多,越应该靠排版而不是靠容器来组织。
|
||||
* - 服务端工具单独成区并加底纹,是唯一需要联网的部分,值得与本地工具区分。
|
||||
* - 筛选期间隐藏事实条与服务端专区,避免同一批工具在一页里出现两次。
|
||||
*/
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -199,58 +205,58 @@ function endpointOf(path: string): string {
|
||||
|
||||
<style scoped>
|
||||
.home {
|
||||
max-width: 1180px;
|
||||
max-width: 1260px;
|
||||
}
|
||||
|
||||
/* ==================== 抬头区 ==================== */
|
||||
/* ==================== 抬头区 ====================
|
||||
* 字号反差是这一页最主要的层次手段:标题 52px / 说明 19px(均取自令牌),
|
||||
* 中间没有第三个档位,视线不会被夹在两层之间。
|
||||
* 搜索框画成一条底线而不是盒子:它是「搜索」这件事本身,不该长成表单控件。 */
|
||||
.hero {
|
||||
padding: 8px 0 36px;
|
||||
max-width: 720px;
|
||||
}
|
||||
.hero-eyebrow {
|
||||
margin: 0 0 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--tk-accent-text);
|
||||
padding: 16px 0 0;
|
||||
}
|
||||
.hero-title {
|
||||
margin: 0 0 14px;
|
||||
font-size: var(--tk-text-4xl);
|
||||
font-weight: 680;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.03em;
|
||||
margin: 0;
|
||||
max-width: 20ch;
|
||||
font-family: var(--tk-font-display);
|
||||
font-size: var(--tk-text-5xl);
|
||||
font-weight: 650;
|
||||
line-height: var(--tk-leading-display);
|
||||
letter-spacing: var(--tk-track-display);
|
||||
color: var(--tk-text);
|
||||
}
|
||||
.hero-desc {
|
||||
margin: 0 0 26px;
|
||||
max-width: 54ch;
|
||||
font-size: var(--tk-text-md);
|
||||
line-height: 1.6;
|
||||
margin: 18px 0 0;
|
||||
max-width: 56ch;
|
||||
font-size: var(--tk-text-lg);
|
||||
line-height: var(--tk-leading-body);
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
|
||||
/* 抬头区搜索框:页面的主操作,尺寸比顶栏的入口更大更醒目 */
|
||||
/* 抬头区搜索框:页面的主操作。
|
||||
* 聚焦时底线加粗成 2px(用 box-shadow 叠加,避免改 border-width 引起 1px 位移)。 */
|
||||
.hero-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
height: 48px;
|
||||
padding: 0 12px 0 16px;
|
||||
border: 1px solid var(--tk-border-strong);
|
||||
border-radius: var(--tk-radius-xl);
|
||||
background: var(--tk-surface);
|
||||
box-shadow: var(--tk-shadow-1);
|
||||
gap: 14px;
|
||||
height: 60px;
|
||||
max-width: 640px;
|
||||
margin-top: 36px;
|
||||
padding: 0 2px;
|
||||
border-bottom: 1px solid var(--tk-border-strong);
|
||||
transition:
|
||||
border-color var(--tk-dur-fast) var(--tk-ease-out),
|
||||
box-shadow var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.hero-search:hover {
|
||||
border-bottom-color: var(--tk-text-placeholder);
|
||||
}
|
||||
.hero-search:focus-within {
|
||||
border-color: var(--tk-accent);
|
||||
box-shadow: 0 0 0 3px var(--tk-accent-ring);
|
||||
border-bottom-color: var(--tk-accent);
|
||||
box-shadow: 0 1px 0 0 var(--tk-accent);
|
||||
}
|
||||
.hero-search-icon {
|
||||
font-size: 18px;
|
||||
font-size: 21px;
|
||||
color: var(--tk-text-placeholder);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@ -262,7 +268,7 @@ function endpointOf(path: string): string {
|
||||
outline: none;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: var(--tk-text-md);
|
||||
font-size: var(--tk-text-xl);
|
||||
color: var(--tk-text);
|
||||
/* 去掉 type=search 在 Safari / Chrome 上的原生清除按钮,统一用自绘按钮 */
|
||||
appearance: none;
|
||||
@ -270,140 +276,142 @@ function endpointOf(path: string): string {
|
||||
.hero-search-input::-webkit-search-cancel-button {
|
||||
display: none;
|
||||
}
|
||||
/* 焦点已由整条底线表达,输入框自身不再画一遍焦点环(会在地线上多出一个方框) */
|
||||
.hero-search-input:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
.hero-search-input::placeholder {
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
.hero-search-clear {
|
||||
flex-shrink: 0;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
border-radius: var(--tk-radius-sm);
|
||||
border-radius: var(--tk-radius-md);
|
||||
background: transparent;
|
||||
color: var(--tk-text-placeholder);
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
font-size: 17px;
|
||||
}
|
||||
.hero-search-clear:hover {
|
||||
background: var(--tk-surface-hover);
|
||||
background: var(--tk-surface-sunken);
|
||||
color: var(--tk-text);
|
||||
}
|
||||
|
||||
.hero-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
margin-top: 16px;
|
||||
font-size: var(--tk-text-base);
|
||||
font-weight: 550;
|
||||
color: var(--tk-accent-text);
|
||||
text-decoration: none;
|
||||
/* ==================== 事实条 ==================== */
|
||||
.facts {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px 48px;
|
||||
padding: 44px 0 56px;
|
||||
}
|
||||
.hero-link:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
/* ==================== 概览数字条 ==================== */
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 1px;
|
||||
margin-bottom: 40px;
|
||||
border-top: 1px solid var(--tk-border);
|
||||
border-bottom: 1px solid var(--tk-border);
|
||||
background: var(--tk-border);
|
||||
}
|
||||
.stat {
|
||||
.fact {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 18px 20px;
|
||||
background: var(--tk-bg);
|
||||
}
|
||||
.stat-value {
|
||||
.fact-value {
|
||||
font-size: var(--tk-text-xl);
|
||||
font-weight: 650;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.2;
|
||||
color: var(--tk-text);
|
||||
}
|
||||
.stat-label {
|
||||
.fact-label {
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
|
||||
/* ==================== 服务端工具专区 ==================== */
|
||||
/* ==================== 服务端工具专区 ====================
|
||||
* 全页唯一有底纹的区块:它需要和本地工具区分开,这个区分值得用一整块底色来表达。 */
|
||||
.server-zone {
|
||||
margin-bottom: 44px;
|
||||
padding: 26px 26px 28px;
|
||||
border: 1px solid var(--tk-border);
|
||||
margin-bottom: var(--tk-space-9);
|
||||
padding: 28px 30px 32px;
|
||||
border-radius: var(--tk-radius-2xl);
|
||||
background: var(--tk-accent-surface);
|
||||
}
|
||||
.zone-head {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.zone-heading {
|
||||
min-width: 0;
|
||||
}
|
||||
.zone-title {
|
||||
margin: 0 0 6px;
|
||||
font-family: var(--tk-font-display);
|
||||
font-size: var(--tk-text-xl);
|
||||
font-weight: 650;
|
||||
letter-spacing: -0.02em;
|
||||
font-weight: 630;
|
||||
letter-spacing: var(--tk-track-title);
|
||||
color: var(--tk-text);
|
||||
}
|
||||
.zone-desc {
|
||||
margin: 0 0 10px;
|
||||
max-width: 60ch;
|
||||
margin: 0;
|
||||
max-width: 56ch;
|
||||
font-size: var(--tk-text-base);
|
||||
line-height: 1.7;
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
.zone-link {
|
||||
flex-shrink: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-size: var(--tk-text-sm);
|
||||
margin-top: 3px;
|
||||
font-size: var(--tk-text-base);
|
||||
font-weight: 550;
|
||||
color: var(--tk-accent-text);
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.zone-link:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
/* 横向卡片:与服务端专区「接口」的气质一致,也与下方分类的竖向卡片区分开 */
|
||||
|
||||
/* 专区内的卡片:实底 + 轻投影抬起,与服务端专区「接口」的气质一致,
|
||||
* 也与下方分类的索引行(无底色)明显不同。 */
|
||||
.zone-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.server-card {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 13px;
|
||||
padding: 15px 16px;
|
||||
border: 1px solid var(--tk-border);
|
||||
gap: 14px;
|
||||
padding: 20px 22px;
|
||||
border-radius: var(--tk-radius-xl);
|
||||
background: var(--tk-surface);
|
||||
text-decoration: none;
|
||||
box-shadow: var(--tk-shadow-1);
|
||||
transition:
|
||||
border-color var(--tk-dur-fast) var(--tk-ease-out),
|
||||
box-shadow var(--tk-dur-fast) var(--tk-ease-out),
|
||||
transform var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.server-card:hover {
|
||||
border-color: var(--tk-accent);
|
||||
box-shadow: var(--tk-shadow-2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.server-card:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.server-card-icon {
|
||||
flex-shrink: 0;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--tk-radius-md);
|
||||
font-size: 19px;
|
||||
font-size: 20px;
|
||||
color: var(--tk-accent-text);
|
||||
background: var(--tk-accent-surface-strong);
|
||||
}
|
||||
@ -412,7 +420,7 @@ function endpointOf(path: string): string {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
gap: 4px;
|
||||
}
|
||||
.server-card-name {
|
||||
font-size: var(--tk-text-base);
|
||||
@ -421,127 +429,140 @@ function endpointOf(path: string): string {
|
||||
}
|
||||
.server-card-desc {
|
||||
font-size: var(--tk-text-sm);
|
||||
line-height: 1.5;
|
||||
line-height: 1.6;
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
.server-card-endpoint {
|
||||
margin-top: 3px;
|
||||
font-size: 11px;
|
||||
margin-top: 4px;
|
||||
font-size: var(--tk-text-xs);
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
|
||||
/* ==================== 分类分区 ==================== */
|
||||
/* ==================== 分类目录 ====================
|
||||
* 分区之间用 80px 留白分隔,不加横线:留白本身就是分隔符,
|
||||
* 再画一条线等于把刚空出来的呼吸感又收回去了。 */
|
||||
.category {
|
||||
margin-bottom: 40px;
|
||||
margin-bottom: var(--tk-space-9);
|
||||
}
|
||||
.category-head {
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--tk-border);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.category-title {
|
||||
margin: 0 0 3px;
|
||||
font-size: var(--tk-text-lg);
|
||||
font-weight: 650;
|
||||
letter-spacing: -0.015em;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
font-family: var(--tk-font-display);
|
||||
font-size: var(--tk-text-2xl);
|
||||
font-weight: 640;
|
||||
letter-spacing: var(--tk-track-title);
|
||||
line-height: 1.25;
|
||||
color: var(--tk-text);
|
||||
}
|
||||
.category-desc {
|
||||
margin: 0;
|
||||
font-size: var(--tk-text-sm);
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
/* 数量作为标题旁的弱化注记,而不是单独占一行 */
|
||||
.category-count {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
font-size: var(--tk-text-xs);
|
||||
font-size: var(--tk-text-sm);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
.category-desc {
|
||||
margin: 8px 0 0;
|
||||
max-width: 56ch;
|
||||
font-size: var(--tk-text-base);
|
||||
line-height: 1.7;
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
|
||||
/* ==================== 工具卡片网格 ==================== */
|
||||
.tool-grid {
|
||||
/* 索引网格:条目之间只用「上边线」一条规则(不用四边描边,也不用上下双线)。
|
||||
* 多列时每列各自成栏,列间距 48px 保证两栏文字不会读串行。 */
|
||||
.tool-index {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
|
||||
gap: 12px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
column-gap: 48px;
|
||||
}
|
||||
.tool-card {
|
||||
.tool-entry {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--tk-border);
|
||||
border-radius: var(--tk-radius-xl);
|
||||
background: var(--tk-surface);
|
||||
align-items: flex-start;
|
||||
gap: 14px;
|
||||
padding: 20px 0;
|
||||
border-top: 1px solid var(--tk-hairline);
|
||||
text-decoration: none;
|
||||
transition:
|
||||
border-color var(--tk-dur-fast) var(--tk-ease-out),
|
||||
transform var(--tk-dur-fast) var(--tk-ease-out),
|
||||
box-shadow var(--tk-dur-fast) var(--tk-ease-out);
|
||||
transition: transform var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.tool-card:hover {
|
||||
border-color: var(--tk-accent);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--tk-shadow-2);
|
||||
}
|
||||
.card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
.card-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
/* 图标常态是中性灰:58 个图标全部染成主色会把主色的含义稀释掉,
|
||||
* 主色只在 hover 时出现,指向「你正在看的这一条」。 */
|
||||
.entry-icon {
|
||||
flex-shrink: 0;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-top: 1px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--tk-radius-md);
|
||||
font-size: 18px;
|
||||
color: var(--tk-accent-text);
|
||||
background: var(--tk-surface-sunken);
|
||||
color: var(--tk-text-placeholder);
|
||||
transition: color var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.tool-card:hover .card-icon {
|
||||
background: var(--tk-accent-surface-strong);
|
||||
.entry-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.card-tag {
|
||||
flex-shrink: 0;
|
||||
padding: 1px 7px;
|
||||
border-radius: var(--tk-radius-pill);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--tk-accent-text);
|
||||
background: var(--tk-surface-sunken);
|
||||
}
|
||||
.card-name {
|
||||
.entry-name {
|
||||
font-size: var(--tk-text-base);
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--tk-text);
|
||||
transition: color var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.card-desc {
|
||||
.entry-desc {
|
||||
font-size: var(--tk-text-sm);
|
||||
line-height: 1.55;
|
||||
line-height: 1.6;
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
.entry-badge {
|
||||
flex-shrink: 0;
|
||||
margin-top: 3px;
|
||||
padding: 2px 9px;
|
||||
border-radius: var(--tk-radius-pill);
|
||||
font-size: var(--tk-text-xs);
|
||||
font-weight: 500;
|
||||
color: var(--tk-accent-text);
|
||||
background: var(--tk-accent-surface-strong);
|
||||
}
|
||||
.tool-entry:hover .entry-name,
|
||||
.tool-entry:hover .entry-icon {
|
||||
color: var(--tk-accent-text);
|
||||
}
|
||||
.tool-entry:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
/* ==================== 空状态 ==================== */
|
||||
.empty {
|
||||
padding: 56px 20px;
|
||||
padding: 64px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.empty-title {
|
||||
margin: 0 0 6px;
|
||||
margin: 0 0 10px;
|
||||
font-family: var(--tk-font-display);
|
||||
font-size: var(--tk-text-lg);
|
||||
font-weight: 650;
|
||||
font-weight: 640;
|
||||
letter-spacing: var(--tk-track-title);
|
||||
color: var(--tk-text);
|
||||
}
|
||||
.empty-hint {
|
||||
margin: 0 0 18px;
|
||||
margin: 0 0 24px;
|
||||
font-size: var(--tk-text-base);
|
||||
color: var(--tk-text-secondary);
|
||||
}
|
||||
.empty-btn {
|
||||
padding: 8px 16px;
|
||||
min-height: 44px;
|
||||
padding: 10px 22px;
|
||||
border: 1px solid var(--tk-border-strong);
|
||||
border-radius: var(--tk-radius-md);
|
||||
background: var(--tk-surface);
|
||||
@ -550,55 +571,86 @@ function endpointOf(path: string): string {
|
||||
font-weight: 550;
|
||||
color: var(--tk-text);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color var(--tk-dur-fast) var(--tk-ease-out),
|
||||
color var(--tk-dur-fast) var(--tk-ease-out);
|
||||
}
|
||||
.empty-btn:hover {
|
||||
border-color: var(--tk-accent);
|
||||
color: var(--tk-accent-text);
|
||||
}
|
||||
|
||||
/* ==================== 响应式 ==================== */
|
||||
/* ==================== 响应式 ====================
|
||||
* 留白随断点阶梯式收敛,但每一级都保持「标题明显大于正文」这条底线。 */
|
||||
@media (max-width: 1279px) {
|
||||
.tool-index {
|
||||
column-gap: 36px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.hero-title {
|
||||
font-size: var(--tk-text-3xl);
|
||||
font-size: 40px;
|
||||
}
|
||||
.stats {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
.hero-search-input {
|
||||
font-size: var(--tk-text-lg);
|
||||
}
|
||||
.facts {
|
||||
gap: 16px 36px;
|
||||
padding: 40px 0 48px;
|
||||
}
|
||||
.category {
|
||||
margin-bottom: 56px;
|
||||
}
|
||||
.server-zone {
|
||||
margin-bottom: 56px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.hero {
|
||||
padding: 4px 0 28px;
|
||||
padding: 8px 0 0;
|
||||
}
|
||||
.hero-title {
|
||||
font-size: 28px;
|
||||
max-width: none;
|
||||
font-size: 32px;
|
||||
}
|
||||
.hero-desc {
|
||||
margin-top: 14px;
|
||||
font-size: var(--tk-text-base);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.hero-search {
|
||||
height: 44px;
|
||||
border-radius: var(--tk-radius-lg);
|
||||
height: 52px;
|
||||
margin-top: 26px;
|
||||
}
|
||||
.stats {
|
||||
margin-bottom: 32px;
|
||||
.hero-search-input {
|
||||
font-size: var(--tk-text-md);
|
||||
}
|
||||
.stat {
|
||||
padding: 14px 16px;
|
||||
.facts {
|
||||
gap: 16px 32px;
|
||||
padding: 36px 0 40px;
|
||||
}
|
||||
.fact-value {
|
||||
font-size: var(--tk-text-lg);
|
||||
}
|
||||
.server-zone {
|
||||
padding: 20px 16px;
|
||||
padding: 20px 18px 22px;
|
||||
border-radius: var(--tk-radius-xl);
|
||||
margin-bottom: 34px;
|
||||
margin-bottom: 44px;
|
||||
}
|
||||
.zone-head {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.zone-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.tool-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.category {
|
||||
margin-bottom: 32px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.category-title {
|
||||
font-size: var(--tk-text-xl);
|
||||
}
|
||||
.tool-index {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -225,7 +225,7 @@ function clearAll() {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.info-label {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.info-value {
|
||||
|
||||
@ -284,14 +284,14 @@ onMounted(() => {
|
||||
.endpoint-path {
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
font-size: 11px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--tk-text-placeholder);
|
||||
word-break: break-all;
|
||||
}
|
||||
.method-badge {
|
||||
padding: 2px 7px;
|
||||
border-radius: var(--tk-radius-xs);
|
||||
font-size: 11px;
|
||||
font-size: var(--tk-text-xs);
|
||||
font-weight: 600;
|
||||
color: var(--tk-text-secondary);
|
||||
background: var(--tk-surface-sunken);
|
||||
@ -352,7 +352,7 @@ onMounted(() => {
|
||||
}
|
||||
.latency-unit {
|
||||
margin-left: 2px;
|
||||
font-size: 11px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--tk-text-placeholder);
|
||||
}
|
||||
/* 无底槽细条:只表达相对快慢 */
|
||||
|
||||
@ -199,7 +199,7 @@ function onHistorySelect(text: string) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.size-tip {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.ascii-block {
|
||||
@ -209,7 +209,7 @@ function onHistorySelect(text: string) {
|
||||
border-radius: 6px;
|
||||
background: var(--td-bg-color-container);
|
||||
font-family: ui-monospace, 'Courier New', Consolas, monospace;
|
||||
font-size: 11px;
|
||||
font-size: var(--tk-text-xs);
|
||||
line-height: 1.1;
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
|
||||
@ -157,7 +157,7 @@ function onHistorySelect(text: string) {
|
||||
}
|
||||
.form-tip {
|
||||
margin: 12px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -234,7 +234,7 @@ function copySwatch(hex: string) {
|
||||
.palette-name {
|
||||
display: block;
|
||||
padding: 4px 6px;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-secondary);
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
|
||||
@ -267,7 +267,7 @@ function onHistorySelect(text: string) {
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
}
|
||||
.field-label {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.field-value {
|
||||
|
||||
@ -398,7 +398,7 @@ function onHistorySelect(text: string) {
|
||||
}
|
||||
.form-tip {
|
||||
margin: 12px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
/* 日期选择器:窄屏下占满可用宽度,避免固定宽度撑出横向滚动条 */
|
||||
|
||||
@ -141,7 +141,7 @@ async function query() {
|
||||
}
|
||||
.w-updated {
|
||||
margin: 8px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -91,7 +91,7 @@ async function query() {
|
||||
}
|
||||
.hint {
|
||||
margin: 12px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.head {
|
||||
@ -143,7 +143,7 @@ async function query() {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.trace-time {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ function copyItem(item: LangItem) {
|
||||
border-radius: 6px;
|
||||
background: var(--td-bg-color-container);
|
||||
font-family: ui-monospace, SFMono-Regular, Consolas, 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
|
||||
@ -192,7 +192,7 @@ function clearAll() {
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
}
|
||||
.info-label {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.info-value {
|
||||
|
||||
@ -428,7 +428,7 @@ onBeforeUnmount(() => {
|
||||
width: 200px;
|
||||
}
|
||||
.form-suffix {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.form-tip {
|
||||
@ -489,7 +489,7 @@ onBeforeUnmount(() => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-error-color);
|
||||
border: 1px dashed var(--td-border-level-2-color);
|
||||
background: var(--td-bg-color-container);
|
||||
@ -509,12 +509,12 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.result-size {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
.result-error {
|
||||
margin: 4px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-error-color);
|
||||
}
|
||||
.result-actions {
|
||||
|
||||
@ -404,7 +404,7 @@ onBeforeUnmount(() => {
|
||||
font-weight: 600;
|
||||
}
|
||||
.form-suffix {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.form-actions {
|
||||
@ -470,7 +470,7 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.summary-tip {
|
||||
margin: 4px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-warning-color);
|
||||
}
|
||||
.result-actions {
|
||||
|
||||
@ -184,7 +184,7 @@ onBeforeUnmount(() => {
|
||||
.block-tag {
|
||||
padding: 1px 8px;
|
||||
border-radius: var(--tk-radius-pill);
|
||||
font-size: 11px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--tk-text-placeholder);
|
||||
background: var(--tk-surface-sunken);
|
||||
}
|
||||
|
||||
@ -217,7 +217,7 @@ function clearAll() {
|
||||
font-size: 13px;
|
||||
}
|
||||
.info-label {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.info-value {
|
||||
|
||||
@ -271,7 +271,7 @@ function onHistorySelect(text: string) {
|
||||
}
|
||||
.note {
|
||||
margin: 12px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -246,7 +246,7 @@ function onHistorySelect(text: string) {
|
||||
}
|
||||
.form-tip {
|
||||
margin: 12px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
|
||||
@ -222,7 +222,7 @@ function detailLabel(k: string): string {
|
||||
}
|
||||
.hint {
|
||||
margin: 10px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.res-main {
|
||||
@ -259,7 +259,7 @@ function detailLabel(k: string): string {
|
||||
border-radius: 8px;
|
||||
}
|
||||
.ri span {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.ri b {
|
||||
|
||||
@ -224,7 +224,7 @@ onMounted(() => {
|
||||
font-weight: 600;
|
||||
}
|
||||
.form-suffix {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.form-actions {
|
||||
@ -253,7 +253,7 @@ onMounted(() => {
|
||||
font-weight: 600;
|
||||
}
|
||||
.strength-entropy {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.strength-bar {
|
||||
|
||||
@ -106,7 +106,7 @@ async function query() {
|
||||
border-radius: 8px;
|
||||
}
|
||||
.pi span {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.pi b {
|
||||
|
||||
@ -234,7 +234,7 @@ watch([width, height, bgColor, textColor, text, fontRatio], draw, { immediate: t
|
||||
}
|
||||
.hex-text {
|
||||
font-family: ui-monospace, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.text-input {
|
||||
|
||||
@ -215,7 +215,7 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.form-tip {
|
||||
margin: 12px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
|
||||
@ -213,7 +213,7 @@ function onHistorySelect(raw: string) {
|
||||
}
|
||||
.hint-text {
|
||||
margin: 8px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.no-match {
|
||||
@ -252,7 +252,7 @@ function onHistorySelect(raw: string) {
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
.match-text {
|
||||
@ -264,7 +264,7 @@ function onHistorySelect(raw: string) {
|
||||
word-break: break-all;
|
||||
}
|
||||
.match-pos {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.match-groups {
|
||||
@ -275,7 +275,7 @@ function onHistorySelect(raw: string) {
|
||||
}
|
||||
.group-tag {
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
|
||||
@ -295,7 +295,7 @@ function onHistorySelect(text: string) {
|
||||
}
|
||||
.tag {
|
||||
flex-shrink: 0;
|
||||
font-size: 11px;
|
||||
font-size: var(--tk-text-xs);
|
||||
padding: 1px 6px;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
@ -334,7 +334,7 @@ function onHistorySelect(text: string) {
|
||||
}
|
||||
.desc {
|
||||
color: var(--td-text-color-secondary);
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
}
|
||||
.match-list {
|
||||
margin-top: 12px;
|
||||
@ -352,7 +352,7 @@ function onHistorySelect(text: string) {
|
||||
}
|
||||
.m-index {
|
||||
color: var(--td-text-color-placeholder);
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
}
|
||||
.m-text {
|
||||
font-family: ui-monospace, Consolas, monospace;
|
||||
|
||||
@ -695,7 +695,7 @@ function clearAll() {
|
||||
|
||||
.char-count {
|
||||
margin: 4px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
text-align: right;
|
||||
}
|
||||
@ -753,12 +753,12 @@ function clearAll() {
|
||||
}
|
||||
.dict-example {
|
||||
margin: 6px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
.dict-src {
|
||||
margin: 10px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
@ -782,7 +782,7 @@ function clearAll() {
|
||||
}
|
||||
.s-hint {
|
||||
margin: 8px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
@ -796,7 +796,7 @@ function clearAll() {
|
||||
}
|
||||
.attr {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
|
||||
@ -242,7 +242,7 @@ function onHistorySelect(text: string) {
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
}
|
||||
.info-label {
|
||||
font-size: 12px;
|
||||
font-size: var(--tk-text-xs);
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
.info-value {
|
||||
|
||||
@ -6,6 +6,9 @@ import { defineConfig, presetUno, transformerDirectives } from 'unocss'
|
||||
* 颜色一律走设计令牌 CSS 变量(--tk-* / --td-*),不要写死色值,
|
||||
* 这样昼夜主题切换与品牌改色都只需改 tokens.scss 一处。
|
||||
* 颜色写法用 arbitrary value,例如:bg-[var(--tk-surface)]
|
||||
*
|
||||
* 尺寸同理:字号用 text-13px / text-16px 这种「与令牌同一档位」的值,
|
||||
* 不要在这里写回 12px,那是上一版「字太小」的根因。
|
||||
*/
|
||||
export default defineConfig({
|
||||
presets: [presetUno()],
|
||||
@ -17,13 +20,14 @@ export default defineConfig({
|
||||
'form-row': 'flex flex-col gap-2 md:flex-row md:items-center md:gap-4',
|
||||
// 结果代码块:等宽字体 + 面板块底色
|
||||
'code-block':
|
||||
'rounded-[var(--tk-radius-md)] bg-[var(--tk-surface-sunken)] px-3 py-2.5 font-mono text-13px leading-relaxed break-all whitespace-pre-wrap',
|
||||
'rounded-[var(--tk-radius-md)] bg-[var(--tk-surface-sunken)] px-4 py-3.5 font-mono text-14px leading-relaxed break-all whitespace-pre-wrap',
|
||||
// 通用面板(自定义区块的轻量容器)
|
||||
// 自带内边距,直接写 class="panel tool-section" 即可,不必每处再补 padding。
|
||||
// 本版容器重新「有形」:白底 + 实描边,与页面底(--tk-bg)拉开层次。
|
||||
// 注意:页面 scoped 样式(如 .block[data-v-*])权重高于本类,需要单独覆盖时照常写即可。
|
||||
'panel':
|
||||
'rounded-[var(--tk-radius-xl)] border border-[var(--tk-border)] bg-[var(--tk-surface)] p-5 md:p-6',
|
||||
'rounded-[var(--tk-radius-xl)] border border-[var(--tk-border)] bg-[var(--tk-surface)] p-5 md:p-7',
|
||||
// 说明性小字
|
||||
'hint-text': 'text-12px text-[var(--tk-text-placeholder)]',
|
||||
'hint-text': 'text-13px text-[var(--tk-text-placeholder)]',
|
||||
},
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user