Icon

Renders a themeable SVG icon from the central registry. Consumers pass a name; Icon owns every ?react import and maps the name to its component.

import { Icon } from '@soroush.tech/design-system/Icon'
;<Icon name="hub" color="primary" size="2rem" />

How color works

The styled svg sets fill: currentColor and maps the color prop to theme.text. Because a CSS fill rule overrides an SVG's baked fill="#hex" presentation attribute, filled icons recolour through the theme without editing the asset files. Stroke-based icons rely on their paths keeping an explicit fill="none" - otherwise they'd inherit currentColor and render as filled blobs (see the generation contract below).

Adding an icon

Icons live as generated TSX components in icons/ - the package has no bundler SVG pipeline, so consumers need no svgr/loader setup.

  1. Generate the component from the .svg - --no-svgo is mandatory so stroke icons keep their per-path fill="none":

    npx @svgr/cli --typescript --jsx-runtime automatic --no-svgo --no-index --out-dir src/Icon/icons path/to/new_icon.svg
    
  2. Import it in icons.ts (import NewIconIcon from './icons/NewIcon') and add it to the icons map.

The name prop is typed from the registry keys (IconName), so new icons get autocomplete and type-checking automatically. Icon.test.tsx renders every registry entry and asserts the fill="none" contract, so a bad generation fails the suite instead of shipping.

Examples

Default

<Icon name="hub" color="primary" size="3rem" />

Color Variants

<div style={{ display: 'flex', gap: '24px' }}>
  <Icon name="hub" color="primary" size="3rem" />
  <Icon name="hub" color="secondary" size="3rem" />
  <Icon name="hub" color="initial" size="3rem" />
</div>

Gallery

<div style={{ display: 'flex', flexWrap: 'wrap', gap: '24px', maxWidth: '480px' }}>
  {iconNames.map((name) => (
    <Icon key={name} name={name as keyof typeof icons} color="primary" size="2rem" />
  ))}
</div>

Icon API reference