---
title: Iconography
tags: [design-system, icons, iconify]
created: 2026-05-23
---

# Iconography

Icons come from **Iconify** — a unified SVG icon library aggregating 100k+ icons from dozens of icon sets, loaded on-demand.

> [!important]
> Per cursor rules (`backend/.cursor/rules/ui-development-standards.mdc`), Iconify is the **only** icon system. Do NOT import from `@mui/icons-material`, `react-icons`, or inline SVG files unless you're building a wholly custom illustration.

---

## 1. Library

Installed as `@iconify/react`. Wrapped by `frontend/src/components/iconify/`:

```tsx
import { Iconify } from 'src/components/iconify';

<Iconify icon="solar:user-bold" />
<Iconify icon="eva:close-fill" width={20} />
<Iconify icon="mingcute:check-fill" sx={{ color: 'success.main' }} />
```

Props: `icon` (required), `width` (number or string, default 20), `sx` (any MUI sx), plus standard HTML attributes.

---

## 2. Approved icon sets

Per the cursor rules, **prefer** these prefixes:

| Prefix | Set | Style | Best for |
|---|---|---|---|
| `solar:` | Solar Icons | Bold / Line / Linear / Outline | Default everywhere — most consistent |
| `eva:` | Eva Icons | Fill / Outline | Compact UI icons (close, more, plus) |
| `mingcute:` | MingCute | Fill / Line | Detailed actions |
| `mdi:` | Material Design Icons | Many variants | Use only when above sets lack the metaphor |
| `simple-icons:` | Brand logos | Monochrome | Social platforms, third-party brands |
| `flagpack:` | Country flags | Filled | Language switcher, address country |

> [!tip]
> Pick ONE style per set across the app for visual consistency. The default convention is `solar:*-bold` for filled and `solar:*-line-duotone` for accents.

---

## 3. Sizing

| Size | Use |
|---|---|
| 16 px | Inline with text (caption, body2) |
| 20 px | Inline with body1, navigation items, buttons (default) |
| 24 px | Standalone action buttons, primary nav |
| 32 px | Empty-state illustrations, large CTAs |
| 48 px+ | Hero illustrations (consider Lottie or `react-icons` set built for hero use) |

Set via `width` (icon is always square):

```tsx
<Iconify icon="solar:bell-bold" width={24} />
```

---

## 4. Color

Color comes from the parent's `color` CSS property (Iconify renders with `fill: currentColor`). Use the `sx` prop:

```tsx
<Iconify icon="solar:check-circle-bold" sx={{ color: 'success.main' }} />
<Iconify icon="solar:warning-bold" sx={{ color: 'warning.dark' }} />
```

Never hard-code hex — go through palette tokens.

---

## 5. Common icon mappings

A non-exhaustive convention table:

| Action / Concept | Recommended icon |
|---|---|
| Search | `solar:magnifer-bold` |
| Close | `eva:close-fill` |
| Add / Create | `solar:add-circle-bold` or `mingcute:add-fill` |
| Edit | `solar:pen-bold` |
| Delete | `solar:trash-bin-trash-bold` |
| Settings | `solar:settings-bold` |
| Notifications | `solar:bell-bold` (badge for unread) |
| User / Profile | `solar:user-bold` |
| Avatar group | `solar:users-group-rounded-bold` |
| Shopping cart | `solar:cart-bold` |
| Filter | `solar:filter-bold` |
| Sort | `solar:sort-bold` |
| More menu | `eva:more-vertical-fill` |
| Back | `solar:alt-arrow-left-bold` (auto-flips RTL) |
| Forward | `solar:alt-arrow-right-bold` |
| Up / Down | `solar:alt-arrow-up-bold` / `solar:alt-arrow-down-bold` |
| Success | `solar:check-circle-bold` |
| Warning | `solar:danger-bold` |
| Error | `solar:close-circle-bold` |
| Info | `solar:info-circle-bold` |
| Upload | `solar:upload-bold` |
| Download | `solar:download-bold` |
| Copy | `solar:copy-bold` |
| Eye (show/hide password) | `solar:eye-bold` / `solar:eye-closed-bold` |
| Wallet | `solar:wallet-money-bold` |
| Coin / Token | `solar:dollar-bold`, `mingcute:coin-fill` |
| QR code | `solar:qr-code-bold` |
| Chat | `solar:chat-round-bold` |
| Send (chat) | `solar:plain-bold` |
| Attachment | `eva:attach-2-fill` |
| Calendar | `solar:calendar-bold` |
| Clock | `solar:clock-circle-bold` |
| Location | `solar:map-point-bold` |
| Phone | `solar:phone-bold` |
| Email | `solar:letter-bold` |
| Lock | `solar:lock-password-bold` |
| Unlock | `solar:lock-unlock-bold` |
| Star (rating) | `solar:star-bold` (filled) / `solar:star-linear` (empty) |
| Hamburger | `solar:hamburger-menu-bold` |
| Refresh | `solar:refresh-bold` |
| Logout | `solar:logout-3-bold` |

---

## 6. RTL & direction

Direction-implying icons (chevrons, arrows, swipe) are flipped automatically by `stylis-plugin-rtl` for arrow-class icons. Semantic icons (play, share, send) should NOT flip — wrap them in:

```tsx
<Box sx={{ direction: 'ltr' }}>
  <Iconify icon="solar:play-bold" />
</Box>
```

Or use `transform: 'scaleX(-1)'` on RTL if you specifically need to mirror.

---

## 7. Custom SVG (when Iconify is not enough)

Place SVGs in `public/assets/icons/` or `frontend/src/assets/icons/` and use:

```tsx
import { SvgColor } from 'src/components/svg-color';

<SvgColor src="/assets/icons/custom-shape.svg" sx={{ color: 'primary.main' }} />
```

`svg-color` uses the mask-image technique so a single-color SVG inherits the parent color, matching Iconify behavior.

For multi-color illustrations, ship them as separate files in `public/illustrations/` and reference with Next `Image`.

---

## 8. Accessibility

- Icon-only buttons MUST have `aria-label`:
  ```tsx
  <IconButton aria-label="close"><Iconify icon="eva:close-fill" /></IconButton>
  ```
- Icons next to a label are decorative — no aria-label needed.
- Live status icons (loading, success) inside a `aria-live="polite"` region announce changes.

---

## 9. Performance

- Iconify loads icons on demand from its CDN (or self-hosted if configured).
- Bundle impact is minimal — only the icon component, not the icon data.
- For the most-used set (e.g., `solar:*`), consider pre-loading via `@iconify-icons/solar` to remove the CDN dependency.

---

## 10. Related

- [[Design System Overview]] · [[Components]] · [[Colors]]
- [[Coding Standards]] — Iconify rule cited
