# @moreyears/icons

Official icon library for More Years. 1,289 icons across 6 weights, available as
tree-shakeable React components and as a Web Component.

Upgrading from v1? See [MIGRATION.md](./MIGRATION.md).

## Installation

```bash
npm install @moreyears/icons
```

## React Usage

Import icons from the weight you want. Your bundler drops everything you do not
import, so a screen using 30 icons ships about 19 KB gzipped, not the whole set.

```jsx
import { Mailbox, ChatRoundDots } from '@moreyears/icons/bold';

<Mailbox />
<Mailbox size={32} color="#333" />
<ChatRoundDots size={24} strokeWidth={1.5} />
```

Deep imports work identically and are a safe fallback for bundlers that handle
barrel files poorly:

```jsx
import Mailbox from '@moreyears/icons/bold/mailbox';
```

Icon names are PascalCase versions of the slug, so `chat-round-dots` becomes
`ChatRoundDots`.

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `size` | `number \| string` | `24` | Icon size in pixels |
| `color` | `string` | `"currentColor"` | Icon color |
| `strokeWidth` | `number \| string` | — | Stroke width (for linear/outline weights) |
| `className` | `string` | — | Additional CSS class |

Any other prop is forwarded to the wrapper element, so you can override
`aria-label` or attach handlers.

### Weights

`broken`, `line-duotone`, `linear`, `outline`, `bold`, `bold-duotone`

Each is its own subpath: `@moreyears/icons/linear`, `@moreyears/icons/bold`, and
so on. 48 icons do not exist in every weight; importing a missing combination is
a build error rather than a silent blank.

### Choosing an icon at runtime

Pass the icon itself rather than its name. This keeps dynamic selection possible
while still letting unused icons fall out of the bundle.

```jsx
import { Icon } from '@moreyears/icons';
import CheckCircle from '@moreyears/icons/bold/check-circle';
import DangerCircle from '@moreyears/icons/bold/danger-circle';

const TONE_ICONS = { success: CheckCircle, error: DangerCircle };

function Toast({ tone }) {
  return <Icon glyph={TONE_ICONS[tone]} size={16} />;
}
```

### Raw SVG

Every icon component carries its own source:

```js
import Mailbox from '@moreyears/icons/bold/mailbox';

Mailbox.svg;         // "<svg viewBox=\"0 0 24 24\" ...>"
Mailbox.iconName;    // "mailbox"
Mailbox.iconWeight;  // "bold"
```

Or import the file directly:

```js
import mailboxUrl from '@moreyears/icons/svgs/bold/messages-conversation/mailbox.svg';
```

### Names that appear in two categories

Seventeen slugs exist in more than one category. Each has an explicit
category-qualified export, and one also holds the bare name:

```js
import { Star } from '@moreyears/icons/bold';            // like/star
import { StarAstronomy } from '@moreyears/icons/bold';   // astronomy/star
```

`money/bill` and `list/bill` are spelled `MoneyBill` and `ListBill`, because
`BillList` is the real icon `money/bill-list`. `4k` exports as `Icon4k`.

## Metadata

Icon metadata lives on its own subpath and carries no artwork, so an icon
browser can enumerate the catalogue cheaply.

```js
import { getAllIcons, getIconInfo, getIconsByCategory, getCategories }
  from '@moreyears/icons/manifest';

getIconInfo('mailbox');
// { name: "Mailbox", slug: "mailbox", category: "messages-conversation", weights: [...] }

getIconsByCategory('messages-conversation');
getCategories();
```

The manifest itself is both the default export and available as raw JSON:

```js
import manifest from '@moreyears/icons/manifest';
import manifest from '@moreyears/icons/manifest.json';
```

## CDN / Web Component

For non-React projects, use the Web Component via CDN:

```html
<script src="https://cdn.jsdelivr.net/npm/@moreyears/icons/dist/web-component.js"></script>

<moreyears-icon name="mailbox" weight="bold" size="24" color="#333"></moreyears-icon>
<moreyears-icon name="mailbox" weight="linear" stroke-width="1.5"></moreyears-icon>
```

| Attribute | Default | Description |
|-----------|---------|-------------|
| `name` | — | Icon name (slug, display name, or full id) |
| `weight` | `"linear"` | Icon weight |
| `size` | `"24"` | Size in pixels |
| `color` | `"currentColor"` | Icon color |
| `stroke-width` | — | Stroke width |

The web component resolves names at runtime by design, so it contains the whole
library. That is appropriate for a script tag but is why the React entry points
work differently.

## Building from Source

```bash
npm install
npm run build
npm test
```

The build emits:

- `dist/{weight}/{icon}.{mjs,js,d.ts}` — per-icon entry points
- `dist/{weight}/index.{mjs,js,d.ts}` — per-weight barrels
- `dist/index.{mjs,js}` — root entry (`Icon`, render helpers)
- `dist/manifest.{mjs,js,json}` — metadata
- `dist/runtime.{mjs,js}` — shared render helper
- `dist/web-component.{js,mjs}` — Web Component (IIFE for CDN, plus ESM)

### Tests

```bash
npm test
```

- **render-parity** asserts 1,680 render cases against golden output captured
  from a real v1.1.0 build, so packaging changes cannot alter artwork.
- **bundle-size** builds fixtures with webpack and fails if tree-shaking
  regresses.
- **exports** checks every subpath resolves under ESM and CJS, and that the
  naming rules produce no shadowed icons.
