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.
ui.system_icons45 stable semantic roles (close, search, alert, pointer…); for reusable framework / package code that must compile with any providerui.icons2,026 Lucide names; for product UI and dynamic configurationui.assets.common17 legacy compatibility icons; not preferred for new codeAll 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.
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.
// 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.
Roles whose Lucide name differs:
alertcircle-alertaudioheadphonesclosexhomehousenotificationbelltrashtrash-2editpencilmore_horizontalellipsiscursor_defaultmouse-pointer-2cursor_clickmouse-pointer-clickpointerhand-pointingnot_allowedbangrabhandresize_horizontalchevrons-left-rightresize_verticalchevrons-up-downresize_diagonalexpandwaithourglassprogressloader-circlehelpcircle-helpwarningtriangle-alert// 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 exampleui.icons.@"type"; dynamicgetis unaffected. - ✓
Enumerable.
ui.icons.allis a name-sorted array of{ name, asset }entries;getbinary-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.
# 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