---
title: "Icon Gallery | zenit Zig UI Docs"
description: "Search, preview and copy zenit’s public icons, and see how static constants, the dynamic registry and the semantic layer divide the work."
url: https://ui.surf/zenit/docs/guide/icons
language: en
alternate_zh: https://ui.surf/zenit/zh/docs/guide/icons.md
alternate_es: https://ui.surf/zenit/es/docs/guide/icons.md
alternate_ja: https://ui.surf/zenit/ja/docs/guide/icons.md
alternate_ko: https://ui.surf/zenit/ko/docs/guide/icons.md
alternate_fr: https://ui.surf/zenit/fr/docs/guide/icons.md
alternate_de: https://ui.surf/zenit/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

Search, preview and copy zenit’s public icons, and see how static constants, the dynamic registry and the semantic layer divide the work.

## Two entry points

Public builds ship Lucide (`lucide-static` v1.31.0). The full provider catalog is `ui.icons` with **2,026** names: 2,025 upstream SVGs plus the compatibility alias `hand-pointing` (byte-identical to upstream `pointer.svg`). `ui.system_icons` offers 45 provider-neutral semantic roles; zenit’s own components depend only on those, so no vendor names leak into the public component contract.

| Entry point | Use it for |
| --- | --- |
| `ui.system_icons` | 45 stable semantic roles (close, search, alert, pointer…); for reusable framework / package code that must compile with any provider |
| `ui.icons` | 2,026 Lucide names; for product UI and dynamic configuration |
| `ui.assets.common` | 17 legacy compatibility icons; not preferred for new code |

ICON PATH

Semantic role -> provider mapping -> Asset -> icon render path. Swapping providers replaces only the mapping module.

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

The semantic-icon story in the real zenit Storybook: a virtual cursor sweeps roles, sizes and tints; the full provider catalog is in the web list below. [/components/icons](https://ui.surf/zenit/components/icons)

## All 2,026 icons

Type to filter instantly; icons load 180 at a time so the page never creates two thousand DOM nodes at once. Click any icon for a ready-to-paste `ui.iconTint` call; names that need escaping (such as `ui.icons.@"type"`) follow the generator’s rules.

180 / 2,026

## Rendering and tint

At build time `gen-icons` turns each icon into flattened geometry (every Asset carries an `icon_id` and size-specific `reps`). At runtime `ui.iconTint` picks the rep closest to the logical size and writes it to the node. No SVG engine runs in the window; only an Asset without pre-flattened geometry falls back to `svgTint` rasterization. When width / height aren’t fixed pixels, the Asset’s default size applies (24 for Lucide).

`ui.iconTint` is the best fit for theme-following colors; one Asset can be reused at any size and color. `ui.icon` takes no tint and draws white, so reach for `iconTint` on light backgrounds. Use `ui.iconTintStyled` when you need BoxStyle fields such as opacity. To recolor a mounted icon (hover, selected state), call `node.setTint(color)`: it handles both icon-table storage and the image storage of the `svgTint` fallback, and returns false when the node has neither.

`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});
}
```

## The semantic icon contract

Component code should say `ui.system_icons.close` rather than bind to a vendor’s name. The Lucide profile maps all 45 roles to concrete Assets in `src/ui/system_icons_lucide.zig`; switching providers with `-Dicon-set` leaves the component API unchanged.

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

Roles whose Lucide name differs:

| ui.system\_icons | Lucide source name |
| --- | --- |
| `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";
```

## Naming, updates and license

-   **Two spellings.** Registry lookups use Lucide’s kebab-case source name (`ui.icons.get("circle-alert")`); Zig constants swap hyphens for underscores (`ui.icons.circle_alert`).
    
-   **Automatic escaping.** Constants that collide with Zig keywords or primitive type names (including `iN` / `uN`) or start with a digit are escaped by the generator, for example `ui.icons.@"type"`; dynamic `get` is unaffected.
    
-   **Enumerable.** `ui.icons.all` is a name-sorted array of `{ name, asset }` entries; `get` binary-searches it.
    
-   **Identical to upstream.** SVG files are byte-identical to upstream for clean updates and license audits; delete the old files before copying so renamed icons don’t linger.
    
-   **Separately licensed.** Lucide is ISC-licensed (`src/ui/icons_oss/LICENSE`) and not covered by zenit’s root GPL-3.0-only license.
    

```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
> 
> **Icon-only buttons still need words.** An icon name is not a human-readable label. A Button’s `.label` is both its visible text and its accessible name; for an icon-only Button with no text, set `node.behavior.interaction.a11y.label` after mounting so the visual, the hit area and the assistive action all point at the same operation.
