Skip to content

Icons

The icon system is part of the broader visual identity established in Visual Identity Direction. Icons serve as functional wayfinders — they orient users within the interface, label content types, and communicate state. They should feel like they belong to the same warm, calm visual family as the typography, illustration, and color system, without drawing attention to themselves. The system builds on Lucide, an open-source icon library that provides consistent geometry and a broad symbol vocabulary. Rather than adopting Lucide’s defaults wholesale, Naluma curates a subset of 35 icons and adjusts the stroke weight to match the brand’s visual character.


The defining adaptation is stroke weight. Lucide defaults to 2px strokes; Naluma uses 1.75px. The reduction is subtle but deliberate — it softens the icons without losing legibility, aligning them with the warm, approachable tone that the Brand Personality demands. At the 80% Warm position on the personality spectrum, icons should feel gentle rather than assertive.

All icons use rounded linecaps and linejoins (stroke-linecap="round", stroke-linejoin="round"), which eliminates sharp endpoints that could read as clinical or harsh. Icons are monochrome by default — they inherit currentColor from their parent element, which means they automatically adapt to light and dark mode without separate assets. Accent color states (hover, active) are applied through semantic color tokens rather than baked into the SVG.

The full set of SVG attributes for every Naluma icon:

<svg
width="24" height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
>
<!-- icon paths -->
</svg>

Icon dimensions are driven by spacing tokens to maintain consistent rhythm with surrounding UI elements. Four sizes cover the range of use cases.

SizeDimensionsToken basisUse case
sm16 x 16--primitive-spacing-4Inline with body text, badges, metadata labels
md24 x 24--primitive-spacing-6Default. Buttons, nav items, list markers
lg32 x 32--primitive-spacing-8Section headers, feature highlights, empty states
xl48 x 48--primitive-spacing-12Hero callouts, onboarding illustrations, landing pages

The 24px medium size is the canonical design size — the viewBox and path geometry are optimized for this dimension. Scaling down to 16px remains legible because of the 1.75px stroke weight (which scales proportionally to ~1.17px, still above the 1px rendering threshold). Scaling up to 48px works because Lucide’s geometry is clean enough to hold at 2x without visible aliasing.


Icons inherit their color from the text context by default, but interactive and status contexts require explicit color assignments. All colors reference semantic tokens so they adapt automatically between light and dark mode.

StateTokenContext
Default--semantic-color-text-primaryBody content, navigation labels
Secondary--semantic-color-text-secondaryMetadata, supporting information
Hover--semantic-color-interactive-hoverClickable icons on mouseover
Disabled--semantic-color-text-disabledInactive controls
Inverse--semantic-color-text-inverseIcons on dark or primary-colored backgrounds

For status icons (info, alert-triangle, check-circle, x-circle), the color maps to the corresponding alert semantic tokens — --semantic-color-status-info, --semantic-color-status-warning, --semantic-color-status-success, and --semantic-color-status-error respectively. These status colors carry meaning independent of the icon shape, satisfying the accessibility principle that color is never the sole indicator (the shape provides redundant encoding).


The curated set covers six functional categories. Every icon listed here has been reviewed for clarity at all four sizes and tested in both light and dark mode.

Navigation

search
menu
x
chevron-down
chevron-right
chevron-left
arrow-left
arrow-right
external-link

Content

book-open
file-text
clock
calendar
tag
bookmark
share-2

Actions

check
plus
minus
edit-3
trash-2
download
upload
copy

Communication

mail
message-circle
globe
link

Media

volume-2
headphones
music

Status

info
alert-triangle
check-circle
x-circle
loader

The WordPress theme uses an SVG sprite sheet for optimal performance. Each icon is defined once in a hidden <svg> block and referenced via <use>:

<!-- In header or footer (rendered once) -->
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="icon-search" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="1.75"
stroke-linecap="round" stroke-linejoin="round">
<path d="m21 21-4.34-4.34" />
<circle cx="11" cy="11" r="8" />
</symbol>
<!-- ... additional symbols -->
</svg>
<!-- Usage anywhere in templates -->
<svg class="naluma-icon naluma-icon--md" aria-hidden="true">
<use href="#icon-search" />
</svg>

CSS classes control sizing: .naluma-icon--sm (16px), .naluma-icon--md (24px), .naluma-icon--lg (32px), .naluma-icon--xl (48px). The aria-hidden="true" attribute is essential for decorative icons; icons that carry meaning without adjacent text need role="img" and an aria-label instead.

The Flutter app uses the lucide_icons package, which provides the same Lucide icon set as Flutter IconData. The stroke weight adjustment happens at the widget level:

Icon(
LucideIcons.search,
size: 24,
color: NalumaColors.textPrimary,
)

Note that the lucide_icons package renders icons at Lucide’s default 2px stroke. To achieve the 1.75px Naluma weight in Flutter, use custom SVG rendering via flutter_svg with the raw SVG strings, or accept the minor visual difference since Flutter’s icon rendering at 24px makes the 0.25px delta negligible on device pixels.

In Astro components and MDX content, use inline SVGs directly with the Naluma attributes. The IconShowcase component demonstrates this approach — each icon is an inline <svg> element with stroke-width="1.75" set explicitly.


The curated set is intentionally small. Adding an icon follows a four-step process.

First, confirm the icon exists in Lucide’s catalog. Naluma does not create custom icon geometry — consistency with the Lucide library ensures visual coherence and avoids maintenance burden.

Second, verify the icon is legible at 16px (the smallest size). Open the Lucide SVG at node_modules/lucide-static/icons/{name}.svg, change the stroke-width to 1.75, and render it at 16 x 16 in a browser. If fine details collapse or strokes merge, the icon is not suitable.

Third, assign it to one of the six categories (Navigation, Content, Actions, Communication, Media, Status). If it does not fit any category, that is a signal to reconsider whether the icon is needed or whether an existing icon can serve the purpose.

Fourth, add the icon to the IconShowcase.astro component in the appropriate category array, including the SVG inner markup extracted from the Lucide source file. Run npm run test:validate to confirm showcase compliance, then commit both the component update and this documentation page.