4.4 KiB
Adding New SVG Icons (Sufi Icons)
Sufi Icons (si) are the only icon set used in the platform. All icons live in a single registry and are rendered by SbIcon. This guide explains how to add a new SVG icon.
Where icons are defined
| File | Purpose |
|---|---|
SufiChain.SufiBlazor/Icons/SiIconCategory.cs |
Enum of categories (Navigation, Actions, Media, Files, etc.) |
SufiChain.SufiBlazor/Icons/SiIconMetadata.cs |
Record: Name, Category, Description, Svg |
SufiChain.SufiBlazor/Icons/SufiIcons.cs |
Static registry: dictionary of all icons, GetIcon, GetSvg, GetByCategory, Search |
SufiChain.SufiBlazor/Components/Common/SbIcon.razor |
Renders icon by name via SufiIcons.GetSvg(Name) — do not add icons here |
Icons are not defined in SbIcon.razor. They are defined only in SufiIcons.cs inside CreateIconRegistry().
SVG contract (required for consistency)
All new outline-style icons must follow this contract so they scale and theme correctly:
- ViewBox:
viewBox="0 0 24 24" - Fill:
fill="none"for outline icons - Stroke:
stroke="currentColor"andstroke-width="2" - Caps/joins:
stroke-linecap="round"andstroke-linejoin="round" - No scripts, external refs, or editor metadata
Example outline SVG:
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M6 9l6 6 6-6"/>
</svg>
For solid/filled icons (e.g. dots, filled shapes), use fill="currentColor" and no stroke. The registry defines constants SvgOutlineAttrs and SvgSolidAttrs in SufiIcons.cs — use them when building the SVG string in code.
Step-by-step: add a new icon
1. Choose a name and category
- Name: kebab-case (e.g.
file-pdf,user-plus). Must be unique. - Category: Pick one of the values in
SiIconCategory(e.g.SiIconCategory.Files,SiIconCategory.Actions). If you need a new category, add it to the enum inSiIconCategory.csfirst.
2. Add the icon to the registry
Open sufi-platform/src/modules/sufi-blazor/src/SufiChain.SufiBlazor/Icons/SufiIcons.cs and find the method CreateIconRegistry(). Add a new entry to the dictionary:
["your-icon-name"] = new("your-icon-name", SiIconCategory.YourCategory, "Short description for search/accessibility",
$"<svg {SvgOutlineAttrs}><path d=\"M...\"/></svg>"),
- Use
SvgOutlineAttrsfor stroke-based icons orSvgSolidAttrsfor fill-only icons. - Escape double quotes inside the SVG string (e.g.
\"for attributes). - Place the entry in the appropriate comment block (e.g.
// ========== File Icons ==========) so the file stays organized.
3. Verify in the demo
Run the SufiBlazorDemo app and open the Icon page (e.g. /components/icon). Your icon should appear in the grid and in search. You can filter by category to confirm it’s in the right group.
4. Use the icon in the app
Use the same name (with or without the si- prefix) in SbIcon:
<SbIcon Name="your-icon-name" />
For menu items and other string-based icon fields, use the same name:
icon: "your-icon-name"
Naming conventions
- Use kebab-case:
chevron-down,folder-open,shield-check. - Prefer short, clear names that match common icon sets (e.g. Lucide/Feather) for easier recognition.
- No
si-prefix in the registry key; the component accepts both"home"and"si-home"and normalizes internally.
Optional: AI-generated icons
If you generate SVG with an AI or tooling:
- Enforce the same contract (24×24 viewBox, stroke/fill, round caps/joins,
currentColor). - Strip scripts, external references, and metadata.
- Prefer optimizing with SVGO (or similar) before pasting into the registry, and avoid aggressive rounding of path data.
The file .cursor/plans/icons-list.csv in the repo contains icon names, categories, and prompts used for the initial pack; you can use it as a reference for naming and categories.
Summary checklist
- Name is kebab-case and unique.
- Category exists in
SiIconCategory(add enum value if needed). - New entry added in
SufiIcons.CreateIconRegistry()with correct SVG. - SVG follows the contract (viewBox, stroke/fill, round caps/joins,
currentColor). - Icon appears and is searchable on the demo Icon page.
- Usage in app is via
<SbIcon Name="your-icon-name" />oricon: "your-icon-name"only.