# @brightlocal/icons

BrightLocal Design System Icons - Lucide defaults and custom icons

## Overview

This package provides a unified icon system for the BrightLocal Design System, combining:

- All 5000+ Lucide React icons (tree-shakeable)
- Custom BrightLocal icons
- Dynamic icon loading for database-driven UI
- Full TypeScript support

## Installation

```bash
pnpm add @brightlocal/icons
```

## Usage

### 1. Direct Import (Recommended)

Best for tree-shaking - only imports the icons you use.

```tsx
import { Check, Heart, Star } from "@brightlocal/icons";

<Check className="w-4 h-4" />
<Heart size={20} color="red" fill="red" />
<Star size={16} strokeWidth={2} />
```

### 2. DynamicIcon Component

For database-driven icon rendering with code-splitting. Icons are loaded dynamically only when used.

```tsx
import { DynamicIcon } from "@brightlocal/icons";

// From database or CMS
const iconName = user.settings.preferredIcon; // "heart"
<DynamicIcon name={iconName} size={20} />

// Works with custom icons too
<DynamicIcon name="bright-local-logo" size={32} />

// With Suspense fallback (optional)
<React.Suspense fallback={<div className="w-6 h-6 animate-pulse bg-gray-200 rounded" />}>
  <DynamicIcon name={dbIconName} size={16} />
</React.Suspense>
```

### 3. Custom BrightLocal Icons

```tsx
import { BrightLocalLogo } from "@brightlocal/icons";

<BrightLocalLogo className="h-8 w-8 text-blue-600" />;
```

## Component Props

### DynamicIcon Component

- `name`: Icon name (kebab-case, e.g., "camera", "chevron-down", "bright-local-logo")
- `size`: Icon size in pixels (default: 16)
- `color`: Icon color (uses currentColor by default)
- `strokeWidth`: Stroke width (default: 1.33)
- All other SVG props

### Flag & Social Media Icons

- `size`: Icon size in pixels (default: 16)
- `className`: CSS classes for styling
- All standard SVG props

## Benefits

- **Tree-shakeable**: Only imports icons you use (with direct imports)
- **Type-safe**: Full TypeScript support with autocomplete
- **Flexible**: Multiple import patterns for different use cases
- **Lazy loading**: DynamicIcon splits icons into separate chunks
- **Consistent**: Unified API across all icons
- **Optimized**: Minimal bundle impact

## Adding Custom Icons

### Method 1: Using createLucideIcon (Recommended)

Compatible with DynamicIcon and follows Lucide's guidelines (16×16 canvas, 2px strokes).

```tsx
// src/custom-icons/my-icon.tsx
import { createLucideIcon } from "@brightlocal/icons";

export const MyIcon = createLucideIcon("MyIcon", [
  ["path", { d: "M12 2v20M2 12h20", key: "cross" }],
  ["circle", { cx: "12", cy: "12", r: "3", key: "center" }],
]);

export default MyIcon;
```

Then add to dynamic imports:

```ts
// src/icons/dynamic-icon-imports.ts
const customIconImports = {
  "my-icon": () =>
    import("../custom-icons/my-icon.js").then((m) => ({
      default: m.MyIcon,
    })),
};
```

### Method 2: Plain SVG Component

For icons that don't need dynamic loading.

```tsx
// src/custom-icons/my-icon.tsx
import * as React from "react";

export const MyIcon = (props: React.SVGProps<SVGSVGElement>) => (
  <svg viewBox="0 0 16 16" fill="none" {...props}>
    <path d="M12 2v20M2 12h20" stroke="currentColor" strokeWidth={2} />
  </svg>
);
```

## Examples

### Icon Gallery

```tsx
import { Check, X, Plus, Minus, Heart, Star } from "@brightlocal/icons";

const icons = [Check, X, Plus, Minus, Heart, Star];

<div className="grid grid-cols-6 gap-4">
  {icons.map((Icon, i) => (
    <Icon key={i} size={16} />
  ))}
</div>;
```

### Database-Driven Icons

```tsx
import { DynamicIcon } from "@brightlocal/icons";

// Icons stored in database
const features = [
  { id: 1, name: "Fast", icon: "zap" },
  { id: 2, name: "Secure", icon: "shield" },
  { id: 3, name: "Scalable", icon: "trending-up" },
];

<div className="grid grid-cols-3 gap-4">
  {features.map((feature) => (
    <div key={feature.id}>
      <DynamicIcon name={feature.icon} size={32} />
      <h3>{feature.name}</h3>
    </div>
  ))}
</div>;
```

### Loading States

```tsx
import { Loader2 } from "@brightlocal/icons";

<button disabled>
  <Loader2 className="mr-2 animate-spin" size={16} />
  Loading...
</button>;
```

## Icon Naming Convention

When using `DynamicIcon`, icon names follow kebab-case format:

- Lucide icons: Convert PascalCase to kebab-case
  - `ChevronDown` → `"chevron-down"`
  - `AlertCircle` → `"alert-circle"`
  - `UserPlus` → `"user-plus"`
- Custom icons: Use kebab-case consistently
  - `BrightLocalLogo` → `"bright-local-logo"`

## Performance Considerations

### Direct Imports vs DynamicIcon

**Use Direct Imports when:**

- Icons are known at build time
- Maximum tree-shaking is desired
- You want the smallest possible bundle

```tsx
import { Camera, Heart } from "@brightlocal/icons";
```

**Use DynamicIcon when:**

- Icon names come from database/API
- You need code-splitting for large icon sets
- Building configurable UI components

```tsx
<DynamicIcon name={iconFromDatabase} />
```

### Bundle Size Impact

- Direct import: ~1-2KB per icon (tree-shaken)
- DynamicIcon: ~4KB + icon loaded on demand

## Default Icon Properties

All Lucide icons from `@brightlocal/icons` are wrapped with design system defaults:

| Property | Default | Description |
|----------|---------|-------------|
| `size` | 16 | Icon dimensions in pixels |
| `strokeWidth` | 1.33 | Matches Figma design specs |
| `absoluteStrokeWidth` | true | Keeps stroke consistent regardless of icon size |

### Default Sizes by Icon Type

| Icon Type | Default Size | Use Case |
|-----------|--------------|----------|
| Lucide icons | 16px | UI elements, buttons, inputs |
| DynamicIcon | 16px | Database-driven icons |
| Flag icons | 16px | Country/region indicators |
| Social Media icons | 16px | Brand logos, social links |

```tsx
// Lucide icons default to 16px with 1.33 strokeWidth
<Check />

// Explicit size — stroke stays consistent (absoluteStrokeWidth: true)
<Check size={20} />
<Check size={12} />
```

## TypeScript Support

Full TypeScript support with type inference:

```tsx
import { DynamicIcon } from "@brightlocal/icons";
import type { DynamicIconProps } from "@brightlocal/icons";

// Props are fully typed
const props: DynamicIconProps = {
  name: "heart",
  size: 16,
  color: "#ff0000",
};
```

## Accessibility

Icons should include proper accessibility attributes:

```tsx
// Decorative icons (no semantic meaning)
<Check aria-hidden="true" />

// Meaningful icons (provide label)
<Heart aria-label="Like this post" role="img" />

// Interactive icons (use with button/link)
<button aria-label="Close dialog">
  <X size={20} />
</button>
```

## Troubleshooting

### Icon not displaying

1. Check the icon name is correct (case-sensitive for direct imports)
2. Verify the icon exists in Lucide library
3. For DynamicIcon, ensure the icon is registered in `dynamic-icon-imports.ts`

### TypeScript errors

Make sure you have the latest types installed:

```bash
pnpm add -D @types/react
```

### Bundle size too large

Use direct imports instead of importing everything:

```tsx
// ❌ Don't do this
import * as Icons from "@brightlocal/icons";

// ✅ Do this
import { Camera, Heart, Star } from "@brightlocal/icons";
```

## Migration from Other Icon Libraries

### From React Icons

```tsx
// Before (react-icons)
import { FaHeart } from "react-icons/fa";
<FaHeart size={16} />;

// After (brightlocal/icons)
import { Heart } from "@brightlocal/icons";
<Heart size={16} />;
```

### From Heroicons

```tsx
// Before (heroicons)
import { HeartIcon } from "@heroicons/react/16/outline";
<HeartIcon className="h-6 w-6" />;

// After (brightlocal/icons)
import { Heart } from "@brightlocal/icons";
<Heart className="h-6 w-6" />;
```

## Resources

- [Lucide Icon Gallery](https://lucide.dev/icons) - Browse all 5000+ available icons
- [Lucide Documentation](https://lucide.dev/guide) - Learn more about Lucide icons
- [Storybook Documentation](../../../apps/docs/src/stories/Lucide.mdx) - Interactive icon gallery
- [Package Source](https://github.com/BrightLocal/design-system/tree/main/packages/icons) - View source code

## Contributing

To add new custom icons:

1. Create icon file in `src/custom-icons/`
2. Export from `src/index.ts`
3. Add to dynamic imports in `src/icons/dynamic-icon-imports.ts`
4. Update `customIconsData` in Storybook documentation
5. Run build: `pnpm build`

## License

Part of the BrightLocal Design System. See main repository for license details.
