Skip to main content

The contract

Every icon is a forwardRef component with the same shape:
Colour comes from currentColor, so the parent’s text colour drives it — no color prop to keep in sync. Install one at a time:
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:

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.
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.

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:

Colouring by state

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

The full set

47 icons. The registry item is @9am/icon-<file>; the import path is @/components/ui/<file>.
refresh-cw exports RefreshCWIcon, not RefreshCwIcon. It is the one name that does not follow plain PascalCase.

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.