Icons
Three sanctioned sources — lucide (functional), thesvg (brand), lucide-animated (motion).
Last updated
Icons come from exactly three sanctioned sources, each with a wrapper convention in @vegastack/design/icons:
Icon— functional UI icons fromlucide-react(every icon). Size tokensxs/sm/md/lg(14/16/20/24px), fixed stroke width,currentColor,aria-hiddenunless labeled.BrandIcon— brand/logo icons fromthesvg(every brand).variant=color(brand colors),auto(uses distinct light/dark artwork when available),mono(inheritscurrentColor), orlight/dark/wordmark.AnimatedIcon— motion icons fromlucide-animated, mirrored into the VegaStack registry (467 icons). Add any withshadcn add @vegastack/icon-<name>, then wrap withAnimatedIconfor the same runtime icon-role tokens + a11y; the generated mirror animates from pointer, touch, or keyboard focus where its upstream interaction supports replay, or via its React 19startAnimation()/stopAnimation()ref handle.motion@^13.2.0is pulled in only when you add an icon — never in the base install.
import { Icon } from "@vegastack/design/icons";
import { Check } from "lucide-react";
<Icon as={Check} size="sm" aria-label="Done" />;// Animated — after `shadcn add @vegastack/icon-activity`
"use client";
import { AnimatedIcon } from "@vegastack/design/icons";
import { ActivityIcon } from "@/components/ui/icons/activity";
<AnimatedIcon as={ActivityIcon} size="md" aria-label="Activity" />;The factory owns the controller; icons are data. A mirrored icon is a createAnimatedIcon({ … })
call describing its geometry, its Motion variants, and — where upstream choreography is not a plain
play/rest pair — its start/stop steps. Everything shared lives once in
@vegastack/design/create-animated-icon: the animation controls, the reduced-motion gate, the
imperative handle, and the trigger rules (hover plays on a fine pointer, a tap plays on touch, focus
plays and blur rests, and all of them stand down once you attach a ref, so your handler becomes the
only driver). The host is an inline-flex <span>, so an icon sits on the text baseline instead of
breaking the line.
Reduced motion is intrinsic: playback becomes a no-op and the icon returns immediately to its static
resting state. The preference is subscribed to, not sampled once — turning it on settles every
icon already on screen, not just the ones mounted afterwards. An explicit
<MotionConfig reducedMotion="always"> adds reduction on top of it, so an application that already
coordinates its motion centrally gets icons for free — and no MotionConfig is required for
VegaStack icon safety.
The override is one-way on purpose. Motion's own default is reducedMotion: "never", and
MotionConfig merges over its parent, so an explicit reducedMotion="never" and no MotionConfig
at all produce identical context values — nothing can tell them apart. Honouring "never" as an
opt-out would therefore switch reduced motion off for every consumer who configured nothing, so the
OS preference always wins unless you ask for more reduction, never less.
Attaching a ref makes your handler the only driver, which includes the tap driver: on a touch device
there is no hover and no focus, so a ref-controlled icon needs a pointerdown handler of its own or
it will never play.
Browse and replay every animated icon. The gallery has its own route so its generated icon modules load only when you ask for the full catalog, not on every docs page.