Search
Search
Lucide and Simple Icons with hugolify-theme-icons module
Icons are a module of their own, hugolify-theme-icons. It renders Lucide for UI and content icons, and Simple Icons for brands.
The module is opt-in: a project that does not import it renders no icons. It ships no framework CSS and completes hugolify-theme-design-system, which bundles no icon of its own.
.icon class: importing the two together makes them collide.Two libraries are imported by the module, each with its own role.
The UI and content set, and the only one for that role: over 1700 outline icons, drawn on a 24x24 grid and stroked (see Stroke weight). Mounted on assets/icons/ui/, resolved by its Lucide name, as-is:
{{ partial "icon" "map-pin" }}
The brand set: over 3400 logos, solid fills with no stroke, rendered monochrome. Mounted on assets/icons/brands/, resolved by its Simple Icons slug prefixed with brand::
{{ partial "icon" "brand:github" }}
Use the slug shown on the site, not the marketing name (x, not twitter).
The prefix also disambiguates a name held by both sets: apple gives the Lucide fruit, brand:apple the Apple logo.
/config/_default/module.yaml
imports:
- path: github.com/hugolify/hugolify-theme/v2
- path: github.com/hugolify/hugolify-theme-design-system # styling module
- path: github.com/hugolify/hugolify-theme-icons # icons
The module imports Lucide and Simple Icons as Hugo modules and mounts them on role-based paths (assets/icons/ui/, assets/icons/brands/), so swapping an icon set is a one-line change and no icon file is committed to your project.
Requires Hugo >= 0.128.0 (templates.Defer).
{{ partial "icon" "map-pin" }} {{/* UI icon — Lucide name, as-is */}}
{{ partial "icon" "brand:github" }} {{/* brand icon — Simple Icons */}}
The name is the filename: there is no mapping table and no alias layer. Browse the Lucide icon list to find a name.
The rendered DOM is a single element, no inline SVG:
<i class="icon icon-map-pin" aria-hidden="true"></i>
Icons are rendered via CSS mask-image from inline SVG data-URIs. No icon font, no SVG sprite, no request per icon.
.icon {
background-color: var(--icon-color, currentColor);
display: inline-block;
height: var(--icon-size, 1em);
mask: var(--icon-glyph) center / contain no-repeat;
vertical-align: -0.125em;
width: var(--icon-size, 1em);
}
Each icon only differs by its --icon-glyph. The SVG is used as an alpha mask, so its own colors are irrelevant: the icon inherits the current text color via currentColor and scales with the font size.
Size and color are overridden with the two custom properties:
.main-menu .icon {
--icon-color: var(--color-brand);
--icon-size: var(--size-24);
}
Brand icons render monochrome — the norm for social rows. A colored logo needs an inline SVG instead of a mask.
Lucide ships every icon at stroke-width="2", which reads heavy at UI sizes. The module treats stroke weight as a design token and defaults it to 1. Change it with a single param:
/config/_default/params.yaml
icons:
strokeWidth: 1.5 # default 1, set 2 to restore Lucide's own weight
The unit is viewBox units, not pixels. All Lucide icons are 24x24, so the rendered thickness is strokeWidth / 24 * --icon-size: at the default --icon-size: 1em (16px), 1 gives 0.67px, 1.5 gives 1px and 2 gives 1.33px. A weight of 1 is genuinely hairline at 16px and depends on the browser antialiasing, so 1.5 is the safer pick for icon-dense interfaces.
Two limits come from the mask rendering described above:
stroke-width attribute inside the SVG: the value is substituted into the source before encoding, at build time.The param only applies to UI icons. Simple Icons brands are solid fills and carry no stroke.
There is no project-side build step and no Node script. The subset ships natively:
partial "icon" registers each name used during the render.templates.Defer resolves the union of those names..icon rule plus one --icon-glyph per icon actually used.Nothing to run, and nothing to commit: there is no yarn build:icons step as with the Bootstrap Icons font, and no generated font file in the project.
An unknown name logs a build warning and renders nothing — the default glyph is an empty SVG, so a missing icon is invisible rather than a solid square.
Some brands are dropped by both Simple Icons and Lucide. The module ships curated SVGs in its own assets/icons/brands/, and local assets take precedence over the imported modules — so you can add a missing brand in your project at the same path:
assets/
└── icons/
└── brands/
└── linkedin.svg