> ## Documentation Index
> Fetch the complete documentation index at: https://docs.9am.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Icons

> 47 hand-built animated icons with an imperative ref handle, so a parent element can drive the animation.

## The contract

Every icon is a `forwardRef` component with the same shape:

```tsx theme={null}
interface IconHandle {
  startAnimation: () => void;
  stopAnimation: () => void;
}

interface IconProps extends HTMLAttributes<HTMLDivElement> {
  size?: number;   // default 28
}
```

Colour comes from `currentColor`, so the parent's text colour drives it — no `color` prop to keep in sync.

Install one at a time:

```bash theme={null}
bunx shadcn@latest add @9am/icon-box @9am/icon-truck
```

They land in `src/components/ui/` alongside the primitives, so the import path is the same as any other component.

## Basic use

Hover the icon itself and it animates on its own:

```jsx theme={null}
import { BoxIcon } from "@/components/ui/box";

<BoxIcon size={24} className="text-primary" />
```

## Driving it from a parent

This is the pattern that makes the kit's navigation feel alive: hovering a **tab** animates the icon inside it, not just the icon's own hitbox.

The moment you attach a ref, the icon stops handling its own hover and hands control to you.

```jsx theme={null}
import { useRef } from "react";
import { BoxIcon } from "@/components/ui/box";

type IconHandle = { startAnimation: () => void; stopAnimation: () => void };

function StockTab() {
  const iconRef = useRef<IconHandle>(null);

  return (
    <TabsTrigger
      value="Stock"
      onMouseEnter={() => iconRef.current?.startAnimation()}
      onMouseLeave={() => iconRef.current?.stopAnimation()}
    >
      <BoxIcon ref={iconRef} size={24} />
      Stock
    </TabsTrigger>
  );
}
```

<Note>
  Attaching a ref means the icon no longer animates on its own hover — it forwards `onMouseEnter` and `onMouseLeave` to you instead. If you take the ref, you own both events.
</Note>

## Theme toggle

`ThemeToggleIcon` is the one icon with an extra prop. It crossfades between a sun and a moon and needs to know which to show:

```jsx theme={null}
import { ThemeToggleIcon } from "@/components/ui/theme-toggle";
import { useTheme } from "@/hooks/useTheme";

const { theme, toggleTheme } = useTheme();

<Button variant="default2" className="h-9 px-3" onClick={toggleTheme}>
  <ThemeToggleIcon theme={theme} size={16} />
</Button>
```

## Colouring by state

Because icons inherit `currentColor`, the usual approach is to compute a colour from the parent's state:

```jsx theme={null}
const iconColor = (tab) => {
  if (currentTab === tab) return theme === "dark" ? "#F6E371" : "var(--color-primary)";
  if (hoveredTab === tab) return theme === "dark" ? "#ffffff" : "#18181b";
  return "#6b7280";
};

<BoxIcon size={24} style={{ color: iconColor("Stock") }} />
```

## The full set

47 icons. The registry item is `@9am/icon-<file>`; the import path is `@/components/ui/<file>`.

| File                      | Export                      |   | File              | Export              |
| ------------------------- | --------------------------- | - | ----------------- | ------------------- |
| `arrow-left`              | `ArrowLeftIcon`             |   | `layout-grid`     | `LayoutGridIcon`    |
| `arrow-right`             | `ArrowRightIcon`            |   | `loader-circle`   | `LoaderCircleIcon`  |
| `badge-percent`           | `BadgePercentIcon`          |   | `map-pin`         | `MapPinIcon`        |
| `box`                     | `BoxIcon`                   |   | `menu`            | `MenuIcon`          |
| `boxes`                   | `BoxesIcon`                 |   | `plus`            | `PlusIcon`          |
| `calendar-days`           | `CalendarDaysIcon`          |   | `receipt`         | `ReceiptIcon`       |
| `cart`                    | `CartIcon`                  |   | `refresh-cw`      | `RefreshCWIcon`     |
| `chart-column-increasing` | `ChartColumnIncreasingIcon` |   | `route`           | `RouteIcon`         |
| `check`                   | `CheckIcon`                 |   | `search`          | `SearchIcon`        |
| `chevron-down`            | `ChevronDownIcon`           |   | `send`            | `SendIcon`          |
| `chevron-left`            | `ChevronLeftIcon`           |   | `settings`        | `SettingsIcon`      |
| `chevron-right`           | `ChevronRightIcon`          |   | `square-pen`      | `SquarePenIcon`     |
| `chevron-up`              | `ChevronUpIcon`             |   | `theme-toggle`    | `ThemeToggleIcon`   |
| `circle-check`            | `CircleCheckIcon`           |   | `trending-down`   | `TrendingDownIcon`  |
| `circle-dollar-sign`      | `CircleDollarSignIcon`      |   | `trending-up`     | `TrendingUpIcon`    |
| `clock`                   | `ClockIcon`                 |   | `truck`           | `TruckIcon`         |
| `credit-card`             | `CreditCardIcon`            |   | `user`            | `UserIcon`          |
| `delete`                  | `DeleteIcon`                |   | `user-round-plus` | `UserRoundPlusIcon` |
| `dollar-sign`             | `DollarSignIcon`            |   | `user-round-x`    | `UserRoundXIcon`    |
| `droplet`                 | `DropletIcon`               |   | `users`           | `UsersIcon`         |
| `folder-open`             | `FolderOpenIcon`            |   | `wallet`          | `WalletIcon`        |
| `grip`                    | `GripIcon`                  |   | `x`               | `XIcon`             |
| `hand-coins`              | `HandCoinsIcon`             |   |                   |                     |
| `hand-helping`            | `HandHelpingIcon`           |   |                   |                     |
| `hard-drive-download`     | `HardDriveDownloadIcon`     |   |                   |                     |

<Warning>
  `refresh-cw` exports `RefreshCWIcon`, not `RefreshCwIcon`. It is the one name that does not follow plain PascalCase.
</Warning>

## Adding a new icon

Copy an existing icon in `registry/icons/` and swap the SVG paths — the ref handle, hover forwarding, and `size` prop are all boilerplate you want to keep identical. Anything in that directory exposing `startAnimation` is picked up automatically by the registry generator and the preview gallery.

The kit has **no icon-library dependency at all**. Do not add one — reach for a hand-built icon so the animation contract stays consistent.
