跳转到内容

图标集

@xihan-ui/icons 是一套结构化数据,不是 SVG 字符串,也不是字体。每枚图标是一条 IconRecord:名字、viewBox、若干节点,节点带标签与属性。

ts
import { ArrowDownIcon } from "@xihan-ui/icons";

// { name: 'arrow-down', viewBox: '0 0 24 24', nodes: [{ tag: 'path', attrs: { d: '…' } }] }

渲染端逐节点创建元素,运行期不经任何解析,因此不存在把外来字符串写入 innerHTML 的路径。这份数据框架无关,Vue、React、Web Components 适配器与不使用框架的场景消费同一份数据。

首方集覆盖中后台界面的常用语义,逐枚手绘、统一 24 网格与 2 粗描边:方向与布局、文件与文档、文本编辑、媒体与设备、通信、状态与安全、数据与图表、系统与账户、商业场景。

它不打算成为穷尽的图标库。图标审美与授权属于使用者:使用 Lucide、Tabler、Bootstrap Icons 或自绘的图标集,把 SVG 目录交给下文的转换器即可。集合大小不影响产物:打包器逐枚判定未被引用并摇掉,只引用一枚就只承担一枚的体积。

组件自带的兜底字形

作者未向部件写入内容时,皮肤会绘制一个默认字形:多选框的勾、下拉框的箭头、清空按钮的叉、表头的排序方向、树的展开把手、数字框的加减号。这些图形不是字符,而是图标包中对应的 SVG:令牌 --xh-glyph-mark-* 的取值是 url("data:image/svg+xml,…"),皮肤把它用作 mask-image、以 currentColor 着色,因此随语气、悬停、禁用自动变色,与使用 <XhIcon> 绘制的结果一致。

令牌取自用途
--xh-glyph-mark-checkcheck各列表族的条目勾、多选框、树、穿梭框、表格勾选把手、步骤条已完成
--xh-glyph-mark-minusminus半选横杠;number-field 的减号
--xh-glyph-mark-plusplusnumber-field 的加号、悬浮按钮
--xh-glyph-mark-closex清空钮、关闭钮、标签与文件条目的删除钮
--xh-glyph-mark-chevron-down · -up · -left · -rightchevron-*展开箭头、树与侧栏的分支把手、轮播与穿梭框的翻页、回到顶部
--xh-glyph-mark-sort · -sort-asc · -sort-descarrow-up-down / arrow-up / arrow-downtable 的排序方向
--xh-glyph-mark-info · -warninginfo / triangle-alert命令式 dialog / notification 的类型徽记,以及 toast 的状态字形
--xh-glyph-mark-zoom-in · -zoom-out · -rotate-left · -rotate-right · -flip-horizontal · -flip-vertical同名图标image-viewer 的工具条
--xh-glyph-mark-required'*'field · fieldset 的必填星号(这是文字,不是图标)

替换方式

通道一:修改令牌。全局修改写在 :root 上,只改一块区域就写在该区域的容器上:它是普通的自定义属性,随 DOM 继承。取值是任意一张 SVG 的 url(),着色同样使用 currentColor

css
:root {
  --xh-glyph-mark-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E…%3C/svg%3E");
}

/* 只有这张表里的排序箭头换成实心三角 */
.report-table {
  --xh-glyph-mark-sort-asc: url("/icons/caret-up.svg");
  --xh-glyph-mark-sort-desc: url("/icons/caret-down.svg");
}

遮罩只读取图形的不透明度,颜色由部件的 color 决定,因此 SVG 中的 fill / stroke 取值不影响结果。

通道二:自行放置节点。向该部件写入内容后,皮肤规则不再命中:它带有 :empty 守卫:

vue
<XhSelectItemIndicator>
  <XhIcon :icon="CheckIcon" />
</XhSelectItemIndicator>
html
<span data-xh-part="item-indicator"><svg data-xh-part="root">…</svg></span>

:empty 对空白文本同样敏感:HTML 中要写成 <span data-xh-part="item-indicator"></span>,标签之间不留换行。

有几处部件本身已有其他内容,:empty 恒不命中,只能使用通道一:checkbox-group 与 transfer 的全选格、field 与 fieldset 的必填星号。

json-viewer 键名后的冒号不在这一族中:它是 JSON 数据格式的语法字符,不是视觉标记。Kbd 的 键名由 JS 渲染为文本,使用它自己的 translations

把任意 SVG 目录转成图标集

包里带一个 xihan-icons 命令:

bash
npx xihan-icons ./node_modules/lucide-static/icons --out src/icons.mjs --dts

产出一份运行期模块,每枚图标一个顶层 export const,可摇树;--dts 一并产出类型声明。之后照常使用:

ts
import { ArrowDownIcon } from "./icons.mjs";

命令行参数:

参数说明
<svg 目录>必填。扫描该目录下的 *.svg,不递归
--out <文件>产物路径,默认 icons.mjs
--dts一并产出同名 .d.mts
--quiet跳过的图标只列前 5 条

需要接入自己的构建脚本时使用 @xihan-ui/icons/codegen

js
import { ingestIconDir, renderModule } from "@xihan-ui/icons/codegen";

const { icons, skipped } = await ingestIconDir("./svg");
await writeFile("icons.mjs", renderModule(icons));

ingestIconDir 不中断:无法转换的图标收进 skipped(带文件名与原因)后继续,由调用方决定报告还是视为失败。

转换时丢弃的内容

图标名由文件名归一而来:小写、连字符分段。数字开头的(Bootstrap Icons 的 0-circle)会派生出不合法的导出标识符,前缀一个 n 归为 n0-circleN0CircleIcon。重名的后来者被跳过而不是覆盖。

非 24 网格的源会就地归一到 0 0 24 24:坐标、描边宽度、半径一并按比例换算,不是只修改 viewBox

外部图标集普遍带有与图标本身无关的内容,这些会被丢弃并逐条记入 notes

  • 根上的 width / height / xmlns / version / xml:space
  • 任何节点上的 class(样式归皮肤)、styleonclick 等内联事件属性
  • 白名单外的属性、取值不合规的属性
  • <title> / <desc> / 许可注释

内联事件属性走同一条丢弃路径,源中的任何脚本都不会进入产物。

丢弃它们正是预期行为:尺寸由使用处决定,颜色使用 currentColor,无障碍名字由 XhIconlabel 提供。

无法转换时明确报错

属性层可以宽松,标签层不可以。宽松模式下以下几类仍然报错:

  • <use>:外观依赖记录无法表达的外部引用
  • <text> / <tspan> / 任何文本内容:记录没有文本变体,字体缺席时无法绘制
  • <style> / <image> / <script>:使图标外观依赖记录之外的内容

接收这些会产出一枚绘制错误的图标,而绘制错误比缺少一枚更难发现,因此报错。CLI 逐条打印跳过的图标:静默缺少图标时使用者只会以为名字写错。

另一类会报错:源不是 24 网格且节点带 transform。正确归一需要先把变换乘入坐标,管线不做这件事,直接报告。

按三套真实图标集实测:

图标集转成跳过
Lucide 1.31.02025 / 20250
Tabler 3.46.0(outline)5130 / 51300
Bootstrap Icons 1.13.12077 / 20781(16 网格 + transform

首方集使用严格模式

本仓库 src/svg 下的图标由 buildIconSet 构建,任何属性不合规都直接失败:首方集的每一枚都由库自行绘制,不设宽松接收的余地。这条路径与上文的摄取管线共用同一套白名单与变换,只是不开启宽松开关。

Released under The MIT License