Changelog
May 1, 2026 · View on GitHub
All notable changes to @respeak/lucide-motion-vue are documented here.
This file follows Keep a Changelog. Versions follow Semantic Versioning.
Unreleased
Nothing yet.
[0.6.2] - 2026-05-01
Performance
- Lazy-load
MorphPathso flubber (~21 kB gzip) is only fetched by icons that actually use apathsmorph chain. Previously every multi-variant SFC (Sun,AudioLines,Cast, …) pulled flubber via<MultiVariantElement>'s static import — even though none of the current merged icons morph. TheMultiVariantIconchunk drops from 92 kB → 4.8 kB raw (22.6 kB → 1.4 kB gzip).
[0.6.1] - 2026-05-01
Added
<MultiVariantIcon>supports nested element trees. Wrapper tags (tag: 'g'withchildren) render via a self-recursive<MultiVariantElement>, so animate-ui icons that drive a single variant transform on a whole sub-tree (Send's plane-takeoff group is the canonical example) can fold both silhouettes into one SFC.- v-for expansion in the Forge multi-variant merger. pqoqubbw
upstream renders staggered animations as
[paths].map(...); the merger now expands the collapsed<motion.path v-for=...>into N concrete elements with per-iteration variant keys and IIFE-baked custom values.SunandSunMedium'saltvariants now render the full element graph instead of one empty path. - Per-icon variant audit scripts:
scripts/audit-multi-variant-merges.mjsre-renders every merged pair from git history and byte-compares to disk;scripts/audit-lucide-animated.mjsflags un-fanned dynamic bodies, unbaked function variants,translateX/Yleftovers, and all-empty variants.scripts/check-new-icons.mjslists upstream additions from animate-ui and pqoqubbw not yet ported.
Fixed
- Forge augment bakes function-form variants per hand path.
pqoqubbw's
[paths].map((d, index) => <motion.path custom={index+1} variants={SHARED} />)collapsed to one upstream path with a function variant; old augment assigned that body topath1only and leftpath2..pathNempty, and evenpath1's function ran withi = undefinedbecause the hand template has no v-for to forward:custom. New behaviour fans the body across all hand paths and pre-invokes the arrow function with each path's index via IIFE. Affected:MessageCircleDashed,MessageSquareDashed,SunDim,GalleryHorizontalEnd,GalleryVerticalEndlucide-animated variants now animate. - Forge augment pairs by d-prefix (with progressive relaxation).
Hand and upstream path orders / counts can drift when pqoqubbw
renders the "background" element as a static
<path>(cloud-rain- wind: 4 hand paths vs 3 upstream motion.paths). Previous positional pairing applied the rain animation to the cloud and dropped one rain line. New behaviour matches by 12/5/3/2-char d-prefix and falls back to skip-first positional when upstream has fewer paths. Recovered:CloudRainWind,CloudRain,CloudLightning,Blocks,ChartLine,Expand,MessageSquarePlus,MessageSquareX,Users. - Forge rewrites
translateX/translateYtox/yin variants. motion-v Vue treatstranslateX/Yon a<motion.rect>by leaking the rect's SVGx/yattributes into the transform translation —Copy's blocks rendered withtransform: translate(8, 8)at rest, doubling the offset. Thex/ymotion props resolve to a clean transform-translate without leakage. - Forge multi-variant merger preserves the root
:variantsbinding from<motion.svg>(via a synthetic<g>wrapper) soBotMessageSquare's head wobble propagates to the alt variant. - Forge multi-variant merger preserves geometry attrs (
width,height,stroke-widthoverrides) on merged child elements;Bot's body rectangle was the visible regression. <AnimateIcon>suppresses UA focus outlines on the icon SVG and inner paths so click/tap doesn't render a stray dotted ring.
Changed
- 14 numbered sibling pairs collapsed into multi-variant SFCs:
Cast/Cast2,MessageCircle/MessageCircle2,MessageSquare/MessageSquare2,Sun/Sun2,SunMedium/SunMedium2,Terminal/Terminal2,Send/Send2,Bot/Bot2,BotMessageSquare/BotMessageSquare2,Compass/Compass2,MessageCircleMore/MessageCircleMore2,MessageSquareMore/MessageSquareMore2,Moon/Moon2,Plus/Plus2,X/X2,AudioLines/AudioLines2. Public API unchanged:<Sun animation="alt" />works the same way. The numbered sibling exports (<Sun2>etc.) are removed.
Docs
- Playground: in-context preview, denser icon grid,
ColorPickerrelocated into the search toolbar, sticky offsets tightened.
[0.6.0] - 2026-05-01
Added
<MultiVariantIcon>core component (src/core/MultiVariantIcon.vue). Lets one icon component ship variants whose element graphs diverge — not just per-variant timings/keyframes on a shared silhouette. Previously the only way to ship two animations whose path splits differed was a numbered sibling (<Bell2 />); now a single<Bell animation="alt" />can drive both, with<MultiVariantIcon>swapping the active animation's element list at runtime. The consumer-facing API is unchanged (<Bell animation="alt" />); whether the SFC uses the standard hand-templated layout or delegates to<MultiVariantIcon>is an implementation detail.- New public types:
SvgElement,SvgTag,AnimationDef,MultiVariantAnimations(src/core/element-types.ts). These describe the data structure consumed by<MultiVariantIcon>and are intended for SFC writers (Forge, custom porters); typical app code never touches them. - New icons via the Forge maintainer tool:
HeartPulse,Siren,Umbrella,WandSparkles. Each has adefaulthand-writtenvariant.
Fixed
- Forge SFC writer escapes non-identifier element keys. Aux-element keys
like
aux:drop1previously producedvariants."aux:drop1"(template syntax error) instead ofvariants['aux:drop1']. Existing icons with the bug have been patched in this release.
[0.5.0] - 2026-04-26
Fixed
- Self-wrap no longer emits an inline-flex
<span>wrapper. When an icon had any trigger (animate,animateOnHover,animateOnTap,animateOnView),AnimateIconwrapped the svg in a<span style="display: inline-flex; line-height: 0">. In a block parent that span sat in the inline formatting context and opened an anonymous line box — so an icon withposition: absolute(the standard lucide-vue-next "icon inside an input" overlay) still pushed its block sibling down by ~1em. Events, theuseInViewref,clip, and consumer fallthrough attrs now forward onto the slot's first vnode (the<motion.svg>) viacloneVNode+mergePropsinstead. Matches the bare-svg shape oflucide-vue-next. Thanks @FAbrahamDev (#5).
Changed
<AnimateIcon>'sasprop renamed'span'→'default'to reflect the new wrapperless behaviour;'span'is still accepted as a silent alias so existing markup keeps compiling.- Multi-child slot contract:
<AnimateIcon>now forwards events onto the slot's first vnode rather than capturing them on its own DOM box. To drive multiple icons from one trigger, wrap them in a single element so the trigger area covers them all (e.g.<span style="display: inline-flex"> <Heart/><Trash2/></span>). Listing siblings directly under<AnimateIcon>would only fire on the first one.
Documented
transformmust be applied via inlinestyle, not via class. motion-v writes an inlinetransformon the rendered svg to drive its animations, and inline style beats any class-definedtransform. So idioms liketop: 50%; transform: translateY(-50%)for centering an absolute-positioned icon need to live on the icon'sstyleattribute rather than its class —mergePropsflows the inline style through alongside motion-v's transform. Affects onlytransform; every other property (position,top,width,color, …) reaches the svg fine via class. Flex/grid centering (e.g. Vuetify<v-text-field #prepend-inner>) needs no CSS at all.
Internal
- New e2e regression test asserts a self-wrapped absolute-positioned icon doesn't push its block sibling down (locks in #5 against re-introducing any wrapper element).
- Unit + smoke tests updated for the wrapperless DOM shape (events now
dispatch on the svg, no
<span>to query).
[0.4.2] - 2026-04-26
Fixed
Facebookicon now scales correctly at sizes other than ~24px. Upstream pqoqubbw shipped the icon's<svg>without aviewBox, so the SVG usedwidth/heightas its implicit user-coordinate space. The path coords (0–24) only filled the canvas whensizehappened to land near 24; at:size="48"the icon rendered at half-size anchored top-left, at 128 it shrank to a corner glyph. AddedviewBox="0 0 24 24"and patchedport-pqoqubbw-icons.mjsto inject aviewBoxdefensively when an upstream icon ships without one, so a future regen can't silently revert it.
Internal
- Added a layered test suite — Vitest unit (
AnimateIcontriggers, context helpers, generator structural shape, an all-icons smoke render) and Playwright e2e (consumer interaction idioms) — wired into a GitHub Actions CI workflow with parallel typecheck / build / unit / e2e jobs. No public API change; package surface published to npm is unchanged.
[0.4.1] - 2026-04-23
Fixed
classandstylenow reach the inner<motion.svg>when an icon self-wraps. Previously, setting any trigger (animate,animateOnHover,animateOnTap,animateOnView) made the icon render as<AnimateIcon><Icon /></AnimateIcon>, and Vue's default attribute fallthrough sent the consumer'sclass/styletoAnimateIcon's<span>wrapper instead of the SVG. CSS-based sizing (<Heart class="w-6 h-6" animateOnHover />, the idiomlucide-vue-nextusers carry over) silently broke.<AnimateIcon>is nowinheritAttrs: falseand forwards fallthrough attrs (class, style, events, aria, data-*) onto the slot's first vnode viacloneVNode. No icon-SFC changes; the static path (<Heart class="w-6 h-6" />without triggers) was already correct and is unaffected. Minor behavior change:id,aria-*, and event handlers on self-wrapped icons now land on the<svg>instead of the<span>wrapper — the old placement was a side effect of the same bug. Thanks @FAbrahamDev (#4).
Changed
- Docs section
Colorrenamed toStylingand gained a "Sizing via CSS" subsection. Deep-links to#colorbecome#styling.
0.4.0 - 2026-04-22
Added
- +86 icons gain a
lucide-animatedvariant, +19 new numbered-sibling icons imported from pqoqubbw/icons. Existing icons whose upstream counterpart shares the same shape get a new variant you can pick viaanimation="lucide-animated"; icons whose upstream shape genuinely differs (e.g.send's swoosh vs. the original paper-plane) ship as siblings —Send2,Moon2,X2,AlarmClock2, etc. — so both the existing animation and the pqoqubbw design stay available. Every new variant carries itssource: 'lucide-animated'tag iniconsMeta. triggerTargetprop on every icon and on<AnimateIcon>— binds hover/tap listeners to an ancestor element instead of the icon's own span wrapper. Accepts'self'(default),'parent', or`closest:${selector}`. Lets you drop animation into existing<button><Icon /></button>markup without restructuring toas="template"(<Heart animateOnHover triggerTarget="parent" />).as="template"is still the right tool when one trigger should drive several icons. Exported as typeTriggerTargetfor wrapper authors. New "Migrating existing buttons" section in the README and the docsButtonspage.- Nuxt module at
@respeak/lucide-motion-vue/nuxt— auto-registers<AnimateIcon>and every icon with anAnimatedsuffix by default (<HeartAnimated>,<Link2Animated>, …), so the library coexists withlucide-vue-next's static<Heart>without collisions. Naming is configurable via thelucideMotionkey innuxt.config. Per-icon tree-shaking is preserved. clipprop on every icon and on<AnimateIcon>. Opt-in boolean that hides overflow at the wrapper's box — use it for animations that deliberately move parts off-screen (<SendAnimated animateOnHover clip />, rocket'slaunchvariant, etc.). Off by default so icons that render outside their box on purpose (Link2's burst particles, somelucide-animatedvariants) keep working.- Docs playground now has a
cliptoggle and emits the prop in the generated snippet. New "Clipping overflow" subsection on the Props page with a side-by-sideSenddemo.
Fixed
- SSR safety:
<AnimateIcon>no longer throwsrequestAnimationFrame is not definedwhen rendered server-side (Nuxt, vite-ssg) with a truthyanimateprop. Client hydration still gets the deferred animation frame. - Docs site: the
#clip-demolink in the Props table no longer bounces you back to the browse view (the hash router treated the anchor as an unknown route). It now scrolls directly to the subsection without rewriting the URL hash.
0.3.2 - 2026-04-21
Fixed
calendar-daysnow renders its dot overlay correctly (unusedAnimatePresenceimport was breaking the template).<Link2>burst particles pivot from the correct origin under CSStransform-box: view-box— previous behavior drifted when the icon was scaled via a parent transform.- Drawer preview enforces a 190px minimum height so the icon doesn't collapse on narrow viewports.
Changed
- Hero GIF on the README is cropped tighter + captures more motion frames; added a variants-preview GIF for the docs site.
0.3.0 - 2026-04-17
Added
- Interactive Playground tab in the docs site — live icon preview, searchable picker, prop knobs (trigger, variant, size, stroke, color, persist), and a copy-paste snippet that mirrors current state.
- "In context" drawer row — each icon's detail drawer now shows the icon at 16 / 24 / 32 px plus two shadcn-style button samples (primary, outline) so you can see how it reads at real UI scales.
rocketicon gains alucide-animatedvariant via the new--augment=<kebab>flow on the pqoqubbw port script — lets hand-written icons grow upstream variants without losing their originals.
0.2.0 - 2026-04-09
Added
- +253 icons from
lucide-animated(pqoqubbw/icons) in three passes — standard, inline-variants, and stray-JSX shapes. Every variant now carries asourcetag (animate-ui/lucide-animated/hand-written) for per-variant attribution (seeiconsMeta). - Favicons wired into the docs site.
Fixed
- Default SVG
overflowis nowvisibleso animations can draw past the 24×24 viewBox without being clipped. (This is the pre-clip-prop behavior; the newclipopt-in in the upcoming release reintroduces clipping for specific exit animations.)
0.1.1 - 2026-03-28
Added
- First public release. 259 animated icons ported from animate-ui to Vue 3 on Motion for Vue.
<AnimateIcon>wrapper withanimateOnHover/animateOnTap/animateOnViewtriggers and a composable template mode.- Tree-shakable barrel + per-icon subpath imports (
@respeak/lucide-motion-vue/icons/<kebab>). - Docs site with icon gallery, search, and keyboard navigation.