skill
button-motion
Semantic button motion library — one Web Component runtime, pluggable story-driven recipes (delete, search). React/Vue wrappers, agent-skill compatible.
Set up
Skills are installed by placing their folder where your agent loads skills.
The download comes from github.com/youwenjiujiu/button-motion itself, not from our server, so what you get is exactly what its author published.
Install instructions come from the README
README
synced Oct 27, 2026button-motion
Semantic button motion library — every recipe animates the button's own story instead of a generic bounce or fade. Delete sweeps its label into an open trash bin; Search morphs a magnifier into sunglasses that glance around a growing search field.
One zero-dependency Web Component runtime; recipes plug in as pure data (CSS keyframes + SVG). No Figma, no Lottie, no external assets. SSR-safe, Shadow-DOM isolated, respects prefers-reduced-motion.
→ Open the gallery
Recipes
| Recipe | Narrative | Duration | Result state |
|---|---|---|---|
delete | Lid lifts level → broom winds up, sweeps the label away letter-by-letter in one stroke → debris arcs into the bin mouth → returning stroke reveals "Done" → lid drops with a settle bounce. Seam-free loop. | 4.18 s | result-label |
search | Magnifier → search field grows → lens splits into sunglasses (bridge draws in, handle vanishes) → gradient ring spins in → placeholder slides in → idle glance left/right → symmetric collapse. | 6.5 s | — |
Planned: send, like, save, add-to-cart, refresh, upload, copy, submit.
Install
npm i button-motion # or copy core/ + recipes/ into your project
Use
Web Component (any framework)
import "button-motion/recipes/delete.js"; // registers <icon-motion> + the recipe
<button type="button" aria-label="Delete" id="del"
style="position:relative;width:202px;height:62px">
<icon-motion id="m" recipe="delete" accent="#CC0033" label="Delete" result-label="Done"
autoplay="false" loop="false" presentation="inline" aria-hidden="true"
style="position:absolute;inset:0;overflow:visible"></icon-motion>
</button>
<script>
del.addEventListener("click", () => m.restart());
m.addEventListener("motion-complete", () => {/* real delete */});
</script>
React
import { IconMotion } from "button-motion/react";
import "button-motion/recipes/delete.js";
<button aria-label="Delete" onClick={() => ref.current?.restart()}
style={{ position: "relative", width: 202, height: 62 }}>
<IconMotion ref={ref} recipe="delete" accent="#CC0033"
label="Delete" resultLabel="Done" onMotionComplete={onDelete}
aria-hidden="true" style={{ position: "absolute", inset: 0, overflow: "visible" }} />
</button>
Vue 3
<script setup>
import { IconMotion } from "button-motion/vue";
import "button-motion/recipes/search.js";
</script>
<template>
<IconMotion recipe="search" label="搜索商品…" autoplay loop />
</template>
Runtime API
| Kind | Names |
|---|---|
| Attributes | recipe accent light background bar foreground muted shadow label result-label duration autoplay loop paused presentation |
| Methods | play() pause() restart() seek(ms) setProgress(0..1) |
| Events | motion-ready motion-play motion-pause motion-seek motion-loop motion-complete |
- Booleans must be explicit when false:
autoplay="false". durationrescales the whole timeline — beats are percentage-based, rescaling is safe.recipecan be swapped at runtime; the element rebuilds and re-emitsmotion-ready.- Slot aspect: delete 202 × 62, search 250 × 62 (from each recipe's
defaults.hostWidth/Height).
As an Agent Skill
The repo root doubles as a Claude Code skill — clone and go:
git clone https://github.com/youwenjiujiu/button-motion.git ~/.claude/skills/button-motion
Then ask naturally: "give this delete button the delete motion" / "给搜索框加动效". The skill locates the target button, copies the library in, swaps the visual while preserving the real <button> semantics (click handler, focus, aria-label), and wires motion-complete to the real action.
Motion craft rules (why it feels right)
- One-stroke principle: a primary action travel is a single keyframe pair with a single easing curve — segment chains read as multiple pushes.
- Contact-coupled coordination: the label starts erasing exactly when the broom foot reaches its last letter, and the erase edge tracks the foot.
- Anticipation / follow-through / smear live on child elements (the broom arm swings around its foot pivot), never on the travel track.
- Seam-free loops: every property resolves to its 0 % value by 100 %.
transform-box: fill-boxdiscipline: alltransform-originvalues are bounding-box-relative, pre-converted. Do not edit them.
Authoring a new recipe
- Reference material → beat sheet (
motion-planstyle, percentages). - Author CSS keyframes + SVG in a standalone sample page.
- Audit with
toolkit/render-range.mjs(frame strips: contact coupling, velocity continuity, seam). - Extract with
toolkit/extract-recipe.mjs <fused.js> <name> <hostW> <hostH> recipes/<name>.js. - Add to
recipes/index.js, galleryRECIPES, and runnpm test(pixel parity + events).