REFERENCE
- Gallery page: https://magpui.com/en/i/animated-outline-icon-set-c4a22b
- First frame (image): https://dnioievkvxejjbxgwvko.supabase.co/storage/v1/object/public/media/animated-outline-icon-set-c4a22b/shot.jpg
- Clip (mp4): https://dnioievkvxejjbxgwvko.supabase.co/storage/v1/object/public/media/animated-outline-icon-set-c4a22b/clip.mp4
- Original post: https://x.com/jamesm/status/2113224126991388873 by @jamesm
Before building, download the first frame and look at it. If ffmpeg is available, download the clip and extract frames (ffmpeg -i clip.mp4 -vf fps=2 frame-%02d.png), then study the motion frame by frame. Match what you see; the text below describes it, the frames are the truth.
BUILD: An animated outline icon set: 28 line icons (24px grid, 2px stroke, round caps and joins) where each icon plays its own short state animation on hover or when its parent action becomes active, then settles back to the static glyph. The feel is quiet and precise: the icon keeps its silhouette, only one detail moves (an arrow spins, a dot pops, a dashed outline marches, a hand taps, a chart line draws itself).
DESIGN SYSTEM: Icons drawn on a 24x24 viewBox, stroke 2px, stroke-linecap round, stroke-linejoin round, no fills except tiny 2px accent dots. Stroke color #3A3A3C on a #EEEEF0 background; ghost or trailing parts at 35% opacity. Demo grid: 7 columns x 4 rows, 72px cell pitch, icons rendered at 48px. Inner corner radius ~4px on square glyphs.
MOTION: Triggered on :hover / :focus-visible of the icon button, or by an `active` prop; each runs once (no infinite loops in product UI). Examples:
- Arrow trail (send/upgrade): ghost copies of the chevron fade in behind it, opacity 0 -> 0.35, translate 3px, 320ms, stagger 60ms, cubic-bezier(0.22, 1, 0.36, 1).
- Refresh: the two arcs rotate 180deg, 600ms, cubic-bezier(0.65, 0, 0.35, 1).
- Dashed square / selection: stroke-dashoffset 0 -> -16 with stroke-dasharray 4 4, 500ms linear, then the outline turns solid over 200ms.
- Sparkle / AI: the small star scales 0.6 -> 1.15 -> 1 with 20deg rotation, 420ms, cubic-bezier(0.34, 1.56, 0.64, 1).
- Chat bubble: three dots bounce translateY 0 -> -2px -> 0, 360ms each, stagger 80ms.
- Curve / path: line draws with pathLength 0 -> 1, 500ms, cubic-bezier(0.22, 1, 0.36, 1); end dot pops scale 0 -> 1, 180ms, delay 420ms.
- Hand tap: finger translateY 0 -> 2px -> 0, 240ms, plus a small arc pulsing outward (scale 0.8 -> 1.2, opacity 1 -> 0) 300ms.
- Globe: meridian ellipse scaleX 1 -> -1 (spin illusion), 700ms ease-in-out.
- Grid: one of four squares swaps to dashed, 250ms; squares shift 2px and return, stagger 40ms.
- Power: the vertical bar dips (scaleY 1 -> 0.4 -> 1) while the arc opens 20deg, 400ms.
Return to rest: 200ms, cubic-bezier(0.4, 0, 0.2, 1).
SECTIONS: Each icon component has states: idle (static glyph), hover/focus (plays its animation once), active (holds the end state when meaningful, e.g. solid selection, checked badge), disabled (40% opacity, no motion). The demo grid shows all 28 icons in 7x4.
IMPLEMENTATION: Inline SVG React components, one per icon, animated with Motion (framer-motion) variants (initial "idle", whileHover / whileFocus "active") driving transform, opacity, pathLength and strokeDashoffset on individual <path> elements. Set transform-box: fill-box and transform-origin: center on animated parts. The parent button forwards hover/focus so the icon animates when the whole button is hovered. prefers-reduced-motion: skip the motion and show the end state with a 120ms opacity crossfade. Icons are decorative inside labelled buttons (aria-hidden="true"); buttons get a visible 2px focus ring. Animate only transform, opacity and stroke properties.
ADAPT TO THE PROJECT: Reuse the project's existing icon size tokens, color tokens and button components; map #3A3A3C and #EEEEF0 to the closest foreground and surface tokens. Redraw glyphs for the project's own actions; never copy logos or brand marks from the original, use generic placeholders.
SUCCESS CHECK:
- Hovering or tab-focusing each icon button plays one distinct animation under 700ms and returns to the identical static glyph.
- All icons share the same 24px grid and 2px round stroke and read as one family when static.
- With prefers-reduced-motion enabled, no transform animations run.