Most icons in a project share the same size, stroke width, or base class. Instead of repeating those attributes on every call, set them once and let the five-layer merge handle the rest.
The five-layer merge
When manager.Get() resolves an icon, the IconRenderer merges attributes from five sources. Higher layers override lower ones. The class attribute is concatenated rather than replaced at every layer.
Layer 1 (lowest) Icon's own attributes (from SVG file parse or Iconify JSON) e.g. viewBox="0 0 24 24", width="24", height="24"Layer 2 Global DefaultAttributes e.g. stroke="currentColor", stroke-width="2", fill="none"Layer 3 PrefixAttributes for the resolved prefix e.g. tabler → stroke-width="1.5"Layer 4 PrefixSuffix for the resolved prefix + icon name suffix e.g. heroicons + "-solid" → fill="currentColor"Layer 5 (highest) Caller-provided attributes e.g. manager.Get("home", map{"class": "icon", "width": "16"})Configuring each layer
Layer 2 (global defaults):
import swarmicons "github.com/frostybee/go-swarm-icons"manager, _ := swarmicons.NewConfig(). AddProvider("lucide", lucide.Provider()). DefaultAttributes(map[string]string{ "fill": "none", "stroke": "currentColor", "stroke-width": "2", }). Build()Calling DefaultAttributes() multiple times is additive. Later calls merge into earlier ones.
Layer 3 (per-prefix):
import swarmicons "github.com/frostybee/go-swarm-icons"manager, _ := swarmicons.NewConfig(). AddProvider("tabler", tablerProvider). PrefixAttributes("tabler", map[string]string{ "stroke-width": "1.5", }). Build()These attributes apply only to icons resolved from the "tabler" prefix.
Layer 4 (suffix matching):
import swarmicons "github.com/frostybee/go-swarm-icons"manager, _ := swarmicons.NewConfig(). AddProvider("heroicons", heroiconsProvider). PrefixSuffix("heroicons", "solid", map[string]string{ "fill": "currentColor", }). PrefixSuffix("heroicons", "outline", map[string]string{ "fill": "none", "stroke": "currentColor", }). PrefixSuffix("heroicons", "", map[string]string{ "fill": "currentColor", }). Build()The renderer checks if the icon name ends with -{suffix}. When multiple suffixes match, the longest suffix wins. An empty suffix ("") acts as a catch-all for icons that match no other suffix.
Layer 5 (caller attributes):
icon, _ := manager.Get("home", map[string]string{ "class": "nav-icon", "id": "home-btn",})Merge rules
class: concatenated from all layers, space-separated. No deduplication.- All other attributes: last layer wins. A value set at layer 4 overrides the same key from layer 3.
- Empty-string values: skipped entirely. Passing
"fill": ""at layer 5 does not clear a fill set at layer 2.
Full example
import ( "fmt" swarmicons "github.com/frostybee/go-swarm-icons" "github.com/frostybee/go-swarm-icons/lucide")func main() { manager, _ := swarmicons.NewConfig(). AddProvider("lucide", lucide.Provider()). DefaultPrefix("lucide"). DefaultAttributes(map[string]string{ "fill": "none", "stroke": "currentColor", "class": "icon", }). Build() icon, _ := manager.Get("home", map[string]string{ "class": "nav-icon", "id": "home-link", }) fmt.Println(icon.ToHTML()) // class includes both "icon" (from layer 2) and "nav-icon" (from layer 5) // fill="none" and stroke="currentColor" from layer 2 // id="home-link" from layer 5}ARIA injection
After the five-layer merge, the renderer injects ARIA attributes for accessibility. The behavior depends on which attributes are already present:
Decorative icons (default)
If no aria-label, aria-labelledby, or role attribute is present:
aria-hidden="true"is set (unless already present).focusable="false"is set (unless already present).
This marks the icon as decorative and hidden from screen readers.
Labeled icons
If aria-label, aria-labelledby, or role is present:
role="img"is set (unlessroleis already set to another value).aria-hiddenandfocusableare not injected.
Existing values are preserved
The renderer never overwrites an attribute that is already set. For example:
- An icon with
aria-hidden="false"keeps that value. - An icon with
role="button"keepsrole="button"even whenaria-labelis present. - An icon with
focusable="true"keepsfocusable="true".
Interaction with Title()
The Title() fluent method prepends a <title> element to the SVG content but does not set aria-label automatically. To make a titled icon accessible, pass aria-label or aria-labelledby as a caller attribute:
icon.Title("Home").Attr(map[string]string{"aria-label": "Home"})// Result: role="img" is injected, aria-hidden is NOT set.See also
- Renderer & SVG Utilities API reference: full
IconRendererAPI, attribute merging implementation, and ARIA injection details - Icon Manipulation: the fluent API for per-icon transforms (size, rotation, flip, opacity)