---
title: "Icon Gallery — zenit Zig UI 文档"
description: "搜索、预览并复制 zenit 公开图标，理解静态常量、动态 registry 与语义图标层的分工。"
url: https://zenit.z.express/zh/docs/guide/icons
language: zh-CN
alternate_en: https://zenit.z.express/docs/guide/icons.md
alternate_es: https://zenit.z.express/es/docs/guide/icons.md
alternate_ja: https://zenit.z.express/ja/docs/guide/icons.md
alternate_ko: https://zenit.z.express/ko/docs/guide/icons.md
alternate_fr: https://zenit.z.express/fr/docs/guide/icons.md
alternate_de: https://zenit.z.express/de/docs/guide/icons.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# Icon Gallery

搜索、预览并复制 zenit 公开图标，理解静态常量、动态 registry 与语义图标层的分工。

## 两个图标入口

公开构建内置 Lucide（`lucide-static` v1.31.0）。完整 provider 目录位于 `ui.icons`，共 **2,026** 个名称：上游 2,025 个 SVG，加上兼容别名 `hand-pointing`（与上游 `pointer.svg` 字节相同）。`ui.system_icons` 则提供 45 个与 provider 无关的语义角色，zenit 自己的组件只依赖后者，避免把供应商命名写进公共组件契约。

| 入口 | 用途 |
| --- | --- |
| `ui.system_icons` | 45 个稳定语义角色（close、search、alert、pointer…）；可复用的框架 / 包代码用它，换 provider 也能编译 |
| `ui.icons` | 2,026 个 Lucide 名称；产品界面与动态配置用它 |
| `ui.assets.common` | 17 个历史兼容图标；新代码不再优先使用 |

ICON PATH

语义角色 → provider 映射 → Asset → icon 渲染路径。换 provider 只替换映射模块。

[Video](https://zenit.z.express/media/stories/icons.mp4?v=6dffffbd52)

真实 zenit Storybook 的语义图标 Story：虚拟鼠标扫过语义图标、尺寸和 tint；完整 provider 目录见下面的网页清单。[/components/icons](https://zenit.z.express/zh/components/icons)

## 完整 2,026 项清单

输入名称即时过滤，每次追加 180 个，避免一次创建两千多个 DOM 节点。点击任意图标会给出一行可直接粘贴的 `ui.iconTint` 调用；需要转义的名称（例如 `ui.icons.@"type"`）已按生成器规则处理。

180 / 2,026

## 渲染与着色

图标在构建期由 `gen-icons` 解析为扁平几何（每个 Asset 带 `icon_id` 和按尺寸划分的 `reps`），运行时 `ui.iconTint` 按逻辑尺寸选取最接近的 rep 写入节点，不在窗口里跑 SVG 引擎；没有预展平几何的 Asset 才回退到 `svgTint` 栅格化。宽高不是固定像素时，使用 Asset 的默认尺寸（Lucide 为 24）。

`ui.iconTint` 最适合跟随主题色；同一个 Asset 可以用不同尺寸和颜色反复使用。`ui.icon` 不带 tint 参数，默认以白色绘制，所以放在浅色背景上时请改用 `iconTint`。需要透明度等 BoxStyle 字段时用 `ui.iconTintStyled`。已挂载的图标要换色（hover、选中态）时调用 `node.setTint(color)`：它同时覆盖 icon 表存储与 `svgTint` 回退的 image 存储，节点两者都没有时返回 false。

`icons.zig`

```zig
// Follow the theme: tint with a token color.
const settings = try ui.iconTint(
    cx,
    ui.icons.settings,
    cx.tokens.color.fg_secondary,
    .{ .width = .fixed(20), .height = .fixed(20) },
);

// Dynamic lookup by kebab-case source name (config files, user data).
const configured = ui.icons.get("circle-alert") orelse
    return error.UnknownIcon;
const warning = try ui.iconTint(cx, configured, cx.tokens.color.warning, .{
    .width = .fixed(24),
    .height = .fixed(24),
});

// Enumerate the whole registry.
for (ui.icons.all) |entry| {
    std.log.debug("{s}", .{entry.name});
}
```

## 语义图标契约

组件代码优先写 `ui.system_icons.close`，而不是绑定某个供应商的具体名字。当前 Lucide profile 在 `src/ui/system_icons_lucide.zig` 里把 45 个角色映射到具体 Asset；用 `-Dicon-set` 切换 provider 时，组件 API 不变。

activity

alert

audio

check

close

search

heart

star

home

settings

notification

calendar

user

mail

trash

download

upload

edit

copy

lock

chevron\_down

chevron\_up

chevron\_left

chevron\_right

minus

plus

more\_horizontal

cursor\_default

cursor\_click

pointer

move

not\_allowed

grab

resize\_horizontal

resize\_vertical

resize\_diagonal

wait

progress

help

warning

info

chevrons\_up

pause

sun

moon

角色名与 Lucide 名不同的映射：

| ui.system\_icons | Lucide 源名称 |
| --- | --- |
| `alert` | `circle-alert` |
| `audio` | `headphones` |
| `close` | `x` |
| `home` | `house` |
| `notification` | `bell` |
| `trash` | `trash-2` |
| `edit` | `pencil` |
| `more_horizontal` | `ellipsis` |
| `cursor_default` | `mouse-pointer-2` |
| `cursor_click` | `mouse-pointer-click` |
| `pointer` | `hand-pointing` |
| `not_allowed` | `ban` |
| `grab` | `hand` |
| `resize_horizontal` | `chevrons-left-right` |
| `resize_vertical` | `chevrons-up-down` |
| `resize_diagonal` | `expand` |
| `wait` | `hourglass` |
| `progress` | `loader-circle` |
| `help` | `circle-help` |
| `warning` | `triangle-alert` |

`semantic.zig`

```zig
// Reusable code: depend on the role, not the provider's name.
const close = try ui.iconTint(cx, ui.system_icons.close, cx.tokens.color.fg_secondary, .{
    .width = .fixed(16),
    .height = .fixed(16),
});

// Icon-only button: no visible label, so set the accessible name explicitly.
const remove = try ui.widgets.Button(.{
    .icon_only = true,
    .icon_asset = ui.system_icons.trash,
    .variant = .ghost,
}).mount(scope, cx);
remove.behavior.interaction.a11y.label = "Delete";
```

## 命名、更新与许可

-   **两种写法。**Registry 查询使用 Lucide 的 kebab-case 源名称（`ui.icons.get("circle-alert")`）；Zig 常量把连字符改为下划线（`ui.icons.circle_alert`）。
    
-   **自动转义。**与 Zig 关键字、基础类型名（包括 `iN` / `uN`）同名或以数字开头的常量由生成器转义，例如 `ui.icons.@"type"`；动态 `get` 不受影响。
    
-   **可枚举。**`ui.icons.all` 是按名称排序的 `{ name, asset }` 数组，`get` 在其上做二分查找。
    
-   **与上游一致。**SVG 原文件与上游字节相同，便于更新和许可审计；更新时先删除旧文件再复制，避免残留已改名的图标。
    
-   **单独许可。**Lucide 使用 ISC License（`src/ui/icons_oss/LICENSE`），不在 zenit 根目录 GPL-3.0-only 授权范围内。
    

```sh
# Verify the Lucide profile (test-headless + storybook build); the worktree is not modified
bash scripts/switch_icon_set.sh lucide

# Regenerate the committed provider module after the SVG set changes
zig build gen-icons-lucide
zig build test-ui
```

> TIP
> 
> **纯图标按钮仍需要文字语义。** 图标名称不是用户可读的 label。Button 的 `.label` 既是可见文字也是无障碍名称；不显示文字的 icon-only Button 要在 mount 后设置 `node.behavior.interaction.a11y.label`，让视觉、命中区域和辅助功能动作指向同一操作。
