# 图标系统冻结规格(IconSpec v1) > 本文件是图标相关任务的**唯一真源**。改图标行为先改这里,再改实现。 > 冻结日期:2026-09-20 | 依据:三库实测(见 §一)+ 本仓库既有规范原文。 --- ## 一 · 参考基准(实测,非推测) 用户指定参考 TDesign 与 Element 组件库。以下是 2026-09-20 对三个上游的**实测**数据: | 上游 | 版本 | 许可 | 图标数 | 形态 | 网格 | |---|---|---|---|---|---| | TDesign Icons(线性) | `tdesign-icons-svg@0.4.7` | MIT | 1334 | `stroke="currentColor"` 描边 | 24×24(少数 24×25 / 25×24) | | TDesign Icons(面性) | 同上 | MIT | 1020 | `fill="currentColor"` 填充 | 同上 | | Element Plus Icons | `@element-plus/icons-svg@2.3.2` | MIT | 293 | `fill="currentColor"` 填充 | 1024×1024 | | Element UI Icons | `element-ui@2.15.14` | MIT | 283 名 | 图标字体(无独立 SVG) | — | - **名称并集**:1511 个不同名(归一化后);在 ≥2 个库里都出现的:**217** 个。 - **进入本仓库**:**2576** 个图标(TDesign 线性 1334 + 面性 1020 + Element Plus 293)。 Element UI 的 283 个字体图标名**不单独引入字形**(它是字体不是 SVG), 而是通过**别名层**映射到上述三类里语义相同的图标(见 §三)。 ### 上游参考组件的图标相关功能(用户点名) | 组件 | 上游的图标能力 | 本仓库如何覆盖 | |---|---|---| | TDesign `TabBar` / `TabBarItem` | `icon`(函数或插槽)、`badgeProps`(图标右上角角标)、`subTabBar`(二级菜单) | 图标组件提供 `name` + 角标由 `Badge` 承担(既有设计);`subTabBar` 不在图标职责内 | | Element UI `PageHeader` | 返回箭头(内置 `el-icon-back`)、标题 / 副标题、右侧 `extra` 插槽 | 图标组件提供 `back` / `chevron-left` / `arrow-left` 三个语义名,`PageHeader` 既有实现继续用 | | TDesign `Icon` | `size` 支持**关键字**(`xs`/`small`/`medium`/`large`/`xl`)**与任意 CSS 值**(写成内联 `font-size`)、`onClick`、`class`/`style` 透传 | 本规格 §四:`size` 同名关键字 + 任意 CSS 值;`onClick` 由端上事件绑定承担 | --- ## 二 · 归一化(我们做了哪些修正,不是照抄) | # | 上游问题 | 我们的处理 | 谁卡这条 | |---|---|---|---| | 1 | TDesign 全部 2356 个 SVG 把 `viewBox` 写成 **`view-box`**(无效属性,浏览器忽略 → 图标按默认视口缩放) | 构建期两种拼写都读,产物统一写回 `viewBox` | `tools/verify-icons.mjs` | | 2 | TDesign 描边宽度 `stroke-width="2"`(24 网格) | 归一为 **1.5**,依据 `组件1.txt`「图标规范:线性图标,描边1.5px」 | 同上 | | 3 | `fill="transparent"` 命中区路径 | 丢弃(那是热区不是视觉内容) | 同上 | | 4 | 路径数字精度不一(最多 6 位小数) | 归一到 2 位小数 + 折叠空白(实测省 25% 体积) | 同上 | | 5 | 两库同名不同形 | 优先级 `tdesign-outline > element-plus > tdesign-filled` | 同上 | **为什么优先级是线性优先**:本仓库规范明示「线性图标」,且 PC 既有实现(`Button.html` 的内联 SVG)与移动端 `TabBar` 都是描边风格。面性图标保留 `-filled` 后缀供需要时选用。 --- ## 三 · 命名与别名 ### 3.1 主名(canonical name) - 一律 **kebab-case**,`[a-z0-9-]`。 - 面性图标保留 **`-filled`** 后缀(与上游一致,不发明第二套词)。 - 例:`search` / `star` / `star-filled` / `chevron-right` / `logo-github`。 ### 3.2 别名层(alias) 用途:让 **Element UI 的 283 个历史图标名**、以及常见同义词都能落到某个真实图标上。 别名表 `aliases.json` 是**显式列出**的映射,不做启发式猜测。 - 断言:每个别名必须指向一个真实存在的 canonical name(`verify-icons.mjs` 卡)。 - 别名不产生新图标,只改查找路径。 --- ## 四 · 组件 API(PC 与移动端共用同一套语义) ### 4.1 props | 名 | 类型 | 默认 | PC | 移动端 | 说明 | |---|---|---|---|---|---| | `name` | string | `''` | ✅ | ✅ | 图标名;空则渲染 `default` 插槽 | | `size` | string \| number | `'default'`(PC)/ `'default'`(移动端) | ✅ | ✅ | **关键字**或**任意 CSS 长度**(见 4.2) | | `tone` | string | `'default'` | ✅ | ✅ | `default` / `brand` / `secondary` / `danger` / `success` / `warning` | | `spin` | boolean | `false` | ✅ | ✅ | 持续旋转,仅表示「进行中」 | | `label` | string | `''` | ✅ | ✅ | 无障碍名称;有值 → `role="img"`,无值 → `aria-hidden` | | `color` | string | `''` | ✅ | — | 任意 CSS 颜色,优先于 `tone`(PC 历史字段,保留兼容) | | `icon` | string | `'★'` | ✅ | — | **PC 历史字段**:直接给字形字符而非图标名(保留兼容,见 §六) | ### 4.2 size 取值(覆盖 TDesign 的关键字 + 任意值双模式) | 关键字 | 边长 | 出处 | |---|---|---| | `small` | 16px | PC 规范 16/20/24;移动端四档 | | `default` | 20px | 同上 | | `large` | 24px | 同上 | | `xlarge` | 32px | 仅移动端(空状态大图标) | | 其他字符串 | 原样作为 CSS 长度 | 对齐 TDesign「任意值写内联 fontSize」 | PC 端另有 `--kole-icon-size-*` 令牌层;移动端 `--kole-m-icon-size` 可被业务覆盖。 ### 4.3 无障碍(硬要求) - `label` 有值 → `role="img"` + `aria-label`。 - `label` 为空 → `aria-hidden="true"`,装饰性图标不进读屏序列。 - 图标**不得作为唯一信息载体**:颜色变化必须伴随文字或 `label`。 - 语义色图标与背景对比度 ≥ **3:1**(WCAG 1.4.11 非文本对比)。 - `spin=true` 在 `prefers-reduced-motion: reduce` 下停止旋转。 --- ## 五 · 分层与加载策略(按实测体积决定,不是拍脑袋) | 层 | 数量 | 加载方式 | 实测体积 | |---|---|---|---| | `core` | 151 | **内联**进组件包(`core.js`) | 35KB | | `standard` | 1993 | 按需 `loadAll()` | — | | `extended` | 432 | 按需 `loadAll()` | — | | 全量 `registry.json` | 2576 | 按需 | 670KB | **为什么不全内联**:实测全量 670KB —— 内联等于给每一页都加 670KB, 直接破坏 `ROADMAP` 的 data.js 150KB 预算精神。core 层 151 个 35KB 是可接受的常驻成本。 --- ## 六 · 兼容与迁移 - **PC `icon` prop 保留**:既有 5 端实现用 `icon`(字形字符,默认 `'★'`)。 v1 起新增 `name`(图标名)。两者都在时 **`name` 优先**;`icon` 仍按字形字符直接渲染。 这样既有调用点(`frameworks/Icon.*`)不破,新调用点用 `name`。 - **移动端 `GLYPHS` 9 名字形表保留**:`check`/`close`/`star`/`warn`/`info`/`arrow`/`plus`/`minus`/`more` 是移动端既有语义名,通过别名层映射到新图标集(`warn` → `warning` 等), 既有 4 端实现的行为不破。 --- ## 七 · 产物清单 | 路径 | 内容 | 入库 | |---|---|---| | `.design_library/kole-ui/icons/sources.json` | 上游原始路径数据(归一化后) | ✅ | | `.design_library/kole-ui/icons/registry.json` | 全量渲染数据 2574 | ✅ | | `.design_library/kole-ui/icons/manifest.json` | 清单(名字 / 层 / 组 / viewBox / 模式) | ✅ | | `.design_library/kole-ui/icons/core.js` | 内联运行时(UMD) | ✅ | | `.design_library/kole-ui/icons/viewbox.js` | viewBox 例外表 | ✅ | | `.design_library/kole-ui/icons/aliases.json` | 别名表 | ✅ | | `.design_library/kole-ui/icons/ATTRIBUTION.md` | MIT 归属(分发必带) | ✅ | | `site/icons.json` | 文档站轻量索引 | ✅ | --- ## 八 · 验收门 ```bash npm run build:icons # 离线重建全部产物 npm run verify:icons # 静态断言(见 tools/verify-icons.mjs) ``` `verify:icons` 至少覆盖: 1. 产物齐全且互相一致(manifest ↔ registry 双向)。 2. 无 `view-box` 拼写残留。 3. 描边宽度全部 1.5。 4. core 层图标真的能从 `core.js` 渲染出非空 SVG。 5. 别名全部指向真实图标。 6. 每个端实现的 `name` 查找路径都能落到 registry(不出现"端上有 API 但取不到图标")。 7. 分组覆盖率 ≥ 75%(低于则说明分组规则退化)。