Koder Icons — curated shared UI icon library + KDS surface
stackRFC003 — Koder Icons: curated shared UI icon library + KDS surface
§1 — Problem
The Koder Stack has formal icon infrastructure *nly for product brand icons*(one SVG per Koder product, generated to PNG variants by kicon). It does *ot*have a curated library of *I icons*shared across products: avatar defaults, empty-state graphics, error illustrations, category symbols, action glyphs branded to match Verge.
Today each product reaches independently into Flutter's built-in Icons.* (Material). Consequences observed in production:
- Inconsistency: Hub uses
Icons.account_circle_outlinedin the login modal, Kruze used a literal?in the same role for over a year (KRUZE170, 20260522). No central reference said "this is what signedout looks like in Koder." - Branding leak: every product looks "generic Material" in its emptyerror states. Verge tokens cover colorspacing/typography but not iconography.
- Discoverability: no place to look up "is there a canonical Koder icon for X?"
- Asset duplication: Hub ships its own
asset://assets/icons/<category>.pngset for app categories (HUB151 family), which other products would reinvent if they needed similar categories.
§2 — Decision
Create a curated icon library shipped as a Koder Stack SDK and surfaced as a browsable section of kds.koder.dev.
*odule path (canonical):*engines/sdk/koder_icons/
*ayout:*
engines/sdk/koder_icons/
├── icons/ # SOURCE OF TRUTH — master SVGs
│ ├── avatar/
│ │ ├── default.svg
│ │ ├── default-circle.svg
│ │ └── default.frontmatter.yaml # tags, aliases, sizes, status, since
│ ├── empty-state/
│ │ ├── search.svg
│ │ ├── messages.svg
│ │ └── ...
│ ├── error-state/
│ │ ├── offline.svg
│ │ ├── server-error.svg
│ │ └── ...
│ ├── category/
│ │ ├── productivity.svg
│ │ ├── media.svg
│ │ └── ...
│ ├── action/ # branded wrappers over Material (only when Material's default doesn't fit Verge)
│ │ ├── share.svg
│ │ └── ...
│ └── brand-koder/ # logos curated in variants
│ ├── wordmark.svg
│ └── mark.svg
├── flutter/ # Flutter package
│ ├── lib/koder_icons.dart # exports KoderIcons.avatarDefault, etc.
│ ├── lib/src/generated.dart # auto-generated from icons/ + frontmatter
│ └── pubspec.yaml
├── web/ # npm + standalone web font subset
│ ├── package.json
│ ├── dist/koder-icons.css # font-face declarations
│ └── dist/koder-icons.woff2 # subset font (only shipped icons)
├── tools/ # CLI helpers
│ └── (extends dev/kicon)
├── README.kmd
└── koder.toml*aming convention:*
- Filesystem:
koder-icons/<group>/<variant>.svg— kebab-case - Dart import:
KoderIcons.<group>_<variant>(snake-case from kebab) — e.g.,KoderIcons.avatar_default,KoderIcons.empty_state_search - HTML:
<koder-icon name="<group>-<variant>" size="24"></koder-icon>web component, OR CSS class<i class="ki ki-avatar-default"></i> - Per-icon
frontmatter.yaml:
name: avatar/default
title: Default User Avatar
aliases: [user, person, profile]
tags: [avatar, user, generic]
sizes: [16, 20, 24, 32, 48]
status: stable
since: v0.1.0§3 — Distribution
| Surface | Package | Versioning |
|---|---|---|
| Flutter (mobile + desktop) | koder_icons pub package |
semver per release |
| Web (JS) | @koder/icons npm + standalone CSS+woff2 |
semver |
| Linux desktop apps | koder-icons .deb on Hub apt |
aligned with semver |
Single source of truth: engines/sdk/koder_icons/icons/. All distribution forms are derived. Buildtime codegen runs on every release CI step.kpkg pack or pre
§4 — Curation policy
*nclusion criteria:*
- Used (or imminently planned) in ≥2 Koder products
- Cannot be satisfied by Material's
Icons.*without sacrificing Verge brand consistency - Has a clear semantic name that a designer in any product would recognize
*xclusion criteria:*
- One-off product brand icons (those stay under
<product>/icons/and usekicon) - Decorative ornaments — those live in product theme assets, not the SDK
- Material-equivalent icons that don't need rebranding (just use
Icons.X)
*rocess:*
- Anyone can propose new icons by opening a ticket in
engines/sdk/koder_icons/backlog/pending/ - Designer review required before merge to
icons/ kicon icons-auditin CI blocks adoption of an icon not present in the SDK if it's used by ≥2 products
§5 — KDS site surface
kds.koder.dev/<locale>/icons/ becomes a new section adjacent to existing palette/, fonts/, verge/.
*tools/design-gen` extension:*
cmd/design-gen/handlers/icons.go— new handler that walksengines/sdk/koder_icons/icons/, parses frontmatter, renders gallery + per-icon detail pagestemplates/icons/index.html— gallery with search + category filter + light/dark toggletemplates/icons/detail.html— per-icon page with:- SVG preview (light/dark side
byside) - Code snippets: Flutter (
KoderIcons.X), HTML webcomponent (`<kodericon>), CSS class (<i class="ki ki-X">`), raw SVG download - Size variants displayed inline
- Aliases + tags for search
- Changelog entry (since version)
- SVG preview (light/dark side
Autoregenerates on every designgen build → no manual sync between SDK and site.
§6 — Migration path
*hase 1 — Bootstrap (~5h):*
- Scaffold
engines/sdk/koder_icons/with empty structure + README - Ship 3 founder icons in Phase 1:
avatar/default.svg(motivated by KRUZE170, used by Kruze sidebar + Hub login + future Talk signin)empty-state/search.svg(used by Hub search + Drive find + Kruze history search)error-state/offline.svg(used by Hub + Kruze + Talk + Drive)
- Flutter package wrapper + first three
KoderIcons.Xexports - design-gen
icons.gohandler renders icons section with these 3 - Migrate KRUZE-170 from
Icons.account_circle→KoderIcons.avatar_default
*hase 2 — Coverage expansion (~10-20h, ongoing):*
- Audit all Koder product backlogs for
TODO: brand-this-iconmarkers, prioritize fill-out - Goal: ≤5% of icon usage in products falls back to raw
Icons.X - Hub categories (
assets/icons/<category>.png) migrate from PNG to SVG SDK icons
*hase 3 — Lock-in (~ongoing):*
kicon icons-auditbecomes pre-commit hook in every product repo (blocking)- New product onboarding includes "use KoderIcons" in CLAUDE.md gatilho table
§7 — Open questions for owner ratification
- *tyle direction*— outlined vs filled vs duotone as default? Material's choice is "Symbols (variable)". Verge equivalent should be decided once. Default suggestion: outlined, stroke 2px, rounded caps (matches Verge button styling).
- *icense*— assets under OFL (matches fonts wave) or under CC0 / MIT? Owner decision.
- *ize canonical set*— 16, 20, 24, 32, 48 sufficient? Add 64/128 for hero variants?
- *ub categories migration*— should `enginessdkkoder_icons/category