
The water button
Buttons, SVG, Physics, Hover
NewFree
The actions menu of a project as a multitool pocket knife: closed, it is the red enamel handle, with its steel liners and the backs of the tools showing edge-on. Pressed, the tools swing out round the pivot one after another, from the middle of the fan outwards, each on its own spring and with the snap of its stop; every tool is an action engraved in the steel, and Delete is the blade. Once a tool is chosen, the others fold away and it makes its own gesture before it snaps shut last.
A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The pocket knife menu</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/saira@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- the shared light of the steel: each tool keeps its own copy of these gradients, turned against its own angle,
so the reflections stay still while the tool turns through them -->
<svg class="defs" width="0" height="0" aria-hidden="true" focusable="false">
<defs>
<linearGradient id="kn-steel" gradientUnits="userSpaceOnUse" x1="0" y1="-190" x2="0" y2="190">
<stop offset="0" stop-color="#c3c9cf"/>
<stop offset="0.13" stop-color="#f2f4f6"/>
<stop offset="0.24" stop-color="#9aa3ab"/>
<stop offset="0.34" stop-color="#dfe3e7"/>
<stop offset="0.43" stop-color="#aab2b9"/>
<stop offset="0.5" stop-color="#f6f8f9"/>
<stop offset="0.57" stop-color="#959ea7"/>
<stop offset="0.67" stop-color="#d5dade"/>
<stop offset="0.79" stop-color="#a2aab2"/>
<stop offset="0.9" stop-color="#ebeef0"/>
<stop offset="1" stop-color="#b8bfc6"/>
</linearGradient>
<!-- the flat of the blade: a satin finish, a little warmer -->
<linearGradient id="kn-satin" gradientUnits="userSpaceOnUse" x1="0" y1="-190" x2="0" y2="190">
<stop offset="0" stop-color="#d6d8d9"/>
<stop offset="0.16" stop-color="#f6f6f4"/>
<stop offset="0.3" stop-color="#c3c7ca"/>
<stop offset="0.45" stop-color="#eeefee"/>
<stop offset="0.55" stop-color="#c9cdd0"/>
<stop offset="0.7" stop-color="#f1f2f1"/>
<stop offset="0.85" stop-color="#bcc1c5"/>
<stop offset="1" stop-color="#e4e6e6"/>
</linearGradient>
<!-- the ground edge of the blade: polished like a mirror, so its reflections are hard -->
<linearGradient id="kn-mirror" gradientUnits="userSpaceOnUse" x1="0" y1="-190" x2="0" y2="190">
<stop offset="0" stop-color="#ffffff"/>
<stop offset="0.1" stop-color="#5e666f"/>
<stop offset="0.19" stop-color="#f4f6f8"/>
<stop offset="0.27" stop-color="#3f464e"/>
<stop offset="0.36" stop-color="#d7dce1"/>
<stop offset="0.45" stop-color="#ffffff"/>
<stop offset="0.52" stop-color="#4a525a"/>
<stop offset="0.61" stop-color="#e8ebee"/>
<stop offset="0.7" stop-color="#6b737c"/>
<stop offset="0.8" stop-color="#fbfcfd"/>
<stop offset="0.9" stop-color="#565e66"/>
<stop offset="1" stop-color="#eef1f3"/>
</linearGradient>
<!-- a reflection crossing the steel: a bright core between two darker bands, so it shows on bright steel too -->
<linearGradient id="kn-glint" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#1c232a" stop-opacity="0"/>
<stop offset="0.2" stop-color="#1c232a" stop-opacity="0.5"/>
<stop offset="0.36" stop-color="#ffffff" stop-opacity="0.85"/>
<stop offset="0.5" stop-color="#ffffff" stop-opacity="1"/>
<stop offset="0.64" stop-color="#ffffff" stop-opacity="0.85"/>
<stop offset="0.8" stop-color="#1c232a" stop-opacity="0.5"/>
<stop offset="1" stop-color="#1c232a" stop-opacity="0"/>
</linearGradient>
<linearGradient id="kn-brass" gradientUnits="userSpaceOnUse" x1="0" y1="-3.2" x2="0" y2="3.2">
<stop offset="0" class="br-0"/>
<stop offset="0.35" class="br-1"/>
<stop offset="0.75" class="br-2"/>
<stop offset="1" class="br-3"/>
</linearGradient>
<radialGradient id="kn-screw" cx="0.38" cy="0.32" r="0.78">
<stop offset="0" stop-color="#ffffff"/>
<stop offset="0.35" stop-color="#d3d8dc"/>
<stop offset="0.8" stop-color="#737a81"/>
<stop offset="1" stop-color="#3d4248"/>
</radialGradient>
</defs>
</svg>
<main class="stage">
<!-- the pocket knife menu: the actions of a project as the tools of a multitool. Closed, it is the handle, red
enamel on steel liners, with the backs of the tools showing along its edges. Pressed, the tools flick out
round the pivot one after another, each on its own back spring, snap into their stops and fan out; each tool
is an action, engraved with its name. The blade is Delete -->
<div class="knife" data-knife>
<div class="knife__body" data-body>
<button class="handle" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="knife-menu" data-handle data-autoclic>
<span class="handle__press">
<svg class="handle__art" viewBox="0 0 204 60" aria-hidden="true">
<defs>
<linearGradient id="kn-liner" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#eef1f3"/>
<stop offset="0.05" stop-color="#8b939a"/>
<stop offset="0.09" stop-color="#4b5258"/>
<stop offset="0.5" stop-color="#2c3035"/>
<stop offset="0.91" stop-color="#4b5258"/>
<stop offset="0.955" stop-color="#a7aeb5"/>
<stop offset="1" stop-color="#5d646b"/>
</linearGradient>
<linearGradient id="kn-enamel" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" class="en-0"/>
<stop offset="0.07" class="en-1"/>
<stop offset="0.42" class="en-2"/>
<stop offset="0.8" class="en-3"/>
<stop offset="0.93" class="en-4"/>
<stop offset="1" class="en-5"/>
</linearGradient>
<radialGradient id="kn-depth" cx="0.5" cy="0.62" r="0.62">
<stop offset="0" class="en-glow" stop-opacity="0.4"/>
<stop offset="1" class="en-glow" stop-opacity="0"/>
</radialGradient>
<linearGradient id="kn-gloss" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ffffff" stop-opacity="0.66"/>
<stop offset="0.5" stop-color="#ffffff" stop-opacity="0.16"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
</linearGradient>
<linearGradient id="kn-sweep" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#ffffff" stop-opacity="0"/>
<stop offset="0.5" stop-color="#ffffff" stop-opacity="0.55"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
</linearGradient>
<radialGradient id="kn-rivet" cx="0.36" cy="0.3" r="0.8">
<stop offset="0" stop-color="#ffffff"/>
<stop offset="0.3" stop-color="#dfe3e6"/>
<stop offset="0.75" stop-color="#7d848b"/>
<stop offset="1" stop-color="#454b51"/>
</radialGradient>
<clipPath id="kn-scale-clip"><rect x="2" y="5" width="200" height="50" rx="25"/></clipPath>
<clipPath id="kn-rim-clip"><rect width="204" height="60" rx="30"/></clipPath>
</defs>
<!-- the steel liners, and between them the backs of the tools seen edge-on, each with its nail nick -->
<rect width="204" height="60" rx="30" fill="url(#kn-liner)"/>
<g class="handle__backs" clip-path="url(#kn-rim-clip)">
<rect data-back="share" x="40" y="1" width="96" height="1.2" rx="0.6"/>
<rect data-back="duplicate" x="36" y="2.3" width="116" height="1.1" rx="0.55"/>
<rect data-back="rename" x="38" y="3.5" width="112" height="1" rx="0.5"/>
<rect data-back="export" x="36" y="55.5" width="128" height="1.1" rx="0.55"/>
<rect data-back="delete" x="34" y="56.7" width="134" height="1.3" rx="0.65"/>
</g>
<rect x="1.4" y="4.4" width="201.2" height="51.2" rx="25.6" fill="#0b0c0e"/>
<rect x="2" y="5" width="200" height="50" rx="25" fill="url(#kn-enamel)"/>
<g clip-path="url(#kn-scale-clip)">
<ellipse cx="102" cy="38" rx="92" ry="22" fill="url(#kn-depth)"/>
<rect x="13" y="7.2" width="178" height="15.5" rx="7.75" fill="url(#kn-gloss)"/>
<rect class="handle__sweep" x="-70" y="0" width="46" height="60" fill="url(#kn-sweep)" data-sweep/>
</g>
<rect x="2.5" y="5.5" width="199" height="49" rx="24.5" fill="none" stroke="#ffffff" stroke-opacity="0.16"/>
<!-- the pivot and the rivet at the far end, set into the enamel -->
<circle class="en-well" cx="30" cy="30" r="7"/>
<circle cx="30" cy="30" r="5.8" fill="url(#kn-rivet)"/>
<circle class="en-well" cx="174" cy="30" r="5.8"/>
<circle cx="174" cy="30" r="4.7" fill="url(#kn-rivet)"/>
</svg>
<!-- the steel plate inlaid in the enamel: it says what the knife is, and what it has just done -->
<span class="plate" aria-hidden="true" data-plate>
<span class="plate__face" data-face>
<svg class="plate__icon" viewBox="0 0 24 24"><rect class="solid" x="9.2" y="15" width="13.6" height="5" rx="2.5"/><path class="solid fan" d="M9.9 15.6 3.3 3.9q-.2-.8.6-.4 6.9 4.6 8.6 11.1z"/></svg>
<span class="plate__word">Actions</span>
</span>
</span>
</span>
<span class="sr" data-name>Actions</span>
</button>
<!-- the tools: each one turns round the pivot. The menu reads from the top tool to the bottom one -->
<div class="tools" id="knife-menu" role="menu" aria-label="Project actions" tabindex="-1" data-menu inert>
<div class="tool-slot" data-slot>
<button class="tool" type="button" role="menuitem" tabindex="-1" data-tool="share">
<!-- the awl, with its sewing eye -->
<svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
<defs>
<linearGradient id="kn-steel-share" href="#kn-steel" data-light/>
<clipPath id="kn-clip-share"><path d="M0 -12H106C124 -12 141 -7 160 -1.3Q162 0 160 1.3C141 7 124 12 106 12H0A12 12 0 0 1 0 -12Z"/></clipPath>
</defs>
<path class="tool__ring" d="M0 -12H106C124 -12 141 -7 160 -1.3Q162 0 160 1.3C141 7 124 12 106 12H0A12 12 0 0 1 0 -12Z"/>
<path class="tool__body" fill="url(#kn-steel-share)" d="M0 -12H106C124 -12 141 -7 160 -1.3Q162 0 160 1.3C141 7 124 12 106 12H0A12 12 0 0 1 0 -12Z"/>
<g clip-path="url(#kn-clip-share)">
<path class="tool__brush" d="M0 -9.5H170M0 -6H170M0 -2.5H170M0 1.5H170M0 5H170M0 8.5H170"/>
<path class="tool__facet" d="M106 -12C124 -12 141 -7 160 -1.3L157 -0.4C141 -4.4 124 -6.8 108 -7.2Z"/>
<rect class="tool__hole" x="126" y="-1.7" width="13" height="3.4" rx="1.7"/>
<path class="tool__lip" d="M127.5 2.2H137.5"/>
<path class="tool__nick" d="M40 -8.4Q46.5 -5.4 53 -8.4"/>
<path class="tool__lip" d="M40.5 -7.1Q46.5 -4.3 52.5 -7.1"/>
<rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
</g>
<path class="tool__lit" data-lit="top" d="M0 -11.5H106C124 -11.5 141 -6.6 159.5 -1"/>
<path class="tool__lit" data-lit="bottom" d="M0 11.5H106C124 11.5 141 6.6 159.5 1"/>
<path class="tool__hit" d="M0 -12H106C124 -12 141 -7 160 -1.3Q162 0 160 1.3C141 7 124 12 106 12H0A12 12 0 0 1 0 -12Z"/>
</svg>
<span class="tool__etch" style="--x0: 56; --x1: 124; --cy: 0">
<svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.6 9.8H7.2a1.9 1.9 0 0 0-1.9 1.9v6.4A1.9 1.9 0 0 0 7.2 20h9.6a1.9 1.9 0 0 0 1.9-1.9v-6.4a1.9 1.9 0 0 0-1.9-1.9h-1.4"/><path d="M12 14.6V3.8M8.6 7.2 12 3.8l3.4 3.4"/></svg>
<span class="tool__label">Share</span>
</span>
</button>
</div>
<div class="tool-slot" data-slot>
<button class="tool" type="button" role="menuitem" tabindex="-1" data-tool="duplicate" data-autoclic>
<!-- the scissors: the upper blade opens on its screw -->
<svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
<defs>
<linearGradient id="kn-steel-duplicate" href="#kn-steel" data-light/>
<clipPath id="kn-clip-duplicate"><path d="M0 -12H128V12H0A12 12 0 0 1 0 -12Z"/><path d="M124 0.6L164 2.2Q166.4 3.2 164 4.4C153 8 140 12 124 12Z"/></clipPath>
</defs>
<path class="tool__ring" d="M0 -12H124C141 -12 154 -6.4 165 0.6Q167 2.4 164 4.4C153 8 140 12 124 12H0A12 12 0 0 1 0 -12Z"/>
<path class="tool__body" fill="url(#kn-steel-duplicate)" d="M0 -12H128V12H0A12 12 0 0 1 0 -12Z"/>
<path class="tool__body" fill="url(#kn-steel-duplicate)" d="M124 0.6L164 2.2Q166.4 3.2 164 4.4C153 8 140 12 124 12Z"/>
<g clip-path="url(#kn-clip-duplicate)">
<path class="tool__brush" d="M0 -9.5H170M0 -6H170M0 -2.5H170M0 1.5H170M0 5H170M0 8.5H170"/>
<path class="tool__nick" d="M44 -8.4Q50.5 -5.4 57 -8.4"/>
<path class="tool__lip" d="M44.5 -7.1Q50.5 -4.3 56.5 -7.1"/>
<rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
</g>
<path class="tool__seam" d="M127.4 -11.4V11.4"/>
<g data-blade>
<path class="tool__body" fill="url(#kn-steel-duplicate)" d="M124 -12C141 -12 154 -6.4 165 0.6Q166.6 1.6 164.4 2.2L124 0.6Z"/>
<path class="tool__seam" d="M128 0.7L164 2.2"/>
<path class="tool__lit" data-lit="top" d="M124 -11.5C141 -11.5 154 -6 164.6 0.8"/>
</g>
<circle class="tool__screw" cx="136" cy="0" r="3.4"/>
<path class="tool__slot" d="M134.3 -1 137.7 1"/>
<path class="tool__lit" data-lit="top" d="M0 -11.5H124"/>
<path class="tool__lit" data-lit="bottom" d="M0 11.5H124C140 11.5 153 7.6 163.6 4"/>
<path class="tool__hit" d="M0 -12H124C141 -12 154 -6.4 165 0.6Q167 2.4 164 4.4C153 8 140 12 124 12H0A12 12 0 0 1 0 -12Z"/>
</svg>
<span class="tool__etch" style="--x0: 52; --x1: 124; --cy: 0">
<svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="8.6" y="8.6" width="11.4" height="11.4" rx="2.4"/><path d="M15.4 8.6V6.4A2.4 2.4 0 0 0 13 4H6.4A2.4 2.4 0 0 0 4 6.4V13a2.4 2.4 0 0 0 2.4 2.4h2.2"/></svg>
<span class="tool__label">Duplicate</span>
</span>
</button>
</div>
<div class="tool-slot" data-slot>
<button class="tool" type="button" role="menuitem" tabindex="-1" data-tool="rename">
<!-- the pen: a knurled barrel and a brass point -->
<svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
<defs>
<linearGradient id="kn-steel-rename" href="#kn-steel" data-light/>
<clipPath id="kn-clip-rename"><path d="M0 -11H119Q123 -11 124 -8H129L153 -3L160.6 -1.3Q163.4 0 160.6 1.3L153 3L129 8H124Q123 11 119 11H0A11 11 0 0 1 0 -11Z"/></clipPath>
</defs>
<path class="tool__ring" d="M0 -11H119Q123 -11 124 -8H129L153 -3L160.6 -1.3Q163.4 0 160.6 1.3L153 3L129 8H124Q123 11 119 11H0A11 11 0 0 1 0 -11Z"/>
<path class="tool__body" fill="url(#kn-steel-rename)" d="M0 -11H119Q123 -11 124 -8H129L153 -3V3L129 8H124Q123 11 119 11H0A11 11 0 0 1 0 -11Z"/>
<path class="tool__nib" d="M153 -3L160.6 -1.3Q163.4 0 160.6 1.3L153 3Z"/>
<g clip-path="url(#kn-clip-rename)">
<path class="tool__brush" d="M0 -8.5H170M0 -5H170M0 -1.5H170M0 2H170M0 5.5H170M0 9H170"/>
<rect class="tool__collar" x="124" y="-9" width="5" height="18"/>
<path class="tool__knurl" d="M132.5 -7.2V7.2M136 -6.5V6.5M139.5 -5.8V5.8M143 -5.1V5.1M146.5 -4.4V4.4"/>
<path class="tool__nick" d="M40 -7.6Q46.5 -4.8 53 -7.6"/>
<path class="tool__lip" d="M40.5 -6.3Q46.5 -3.7 52.5 -6.3"/>
<rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
</g>
<path class="tool__lit" data-lit="top" d="M0 -10.5H119Q122.6 -10.5 123.6 -8M129 -7.5L153 -2.6"/>
<path class="tool__lit" data-lit="bottom" d="M0 10.5H119Q122.6 10.5 123.6 8M129 7.5L153 2.6"/>
<path class="tool__hit" d="M0 -11H119Q123 -11 124 -8H129L153 -3L160.6 -1.3Q163.4 0 160.6 1.3L153 3L129 8H124Q123 11 119 11H0A11 11 0 0 1 0 -11Z"/>
</svg>
<span class="tool__etch" style="--x0: 54; --x1: 120; --cy: 0">
<svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4.5 19.5h4.2L19.4 8.8a2.1 2.1 0 0 0 0-3l-1.2-1.2a2.1 2.1 0 0 0-3 0L4.5 15.3z"/><path d="M13.6 6.2l4.2 4.2"/></svg>
<span class="tool__label">Rename</span>
</span>
</button>
</div>
<div class="tool-slot" data-slot>
<button class="tool" type="button" role="menuitem" tabindex="-1" data-tool="export">
<!-- the cap lifter, with its screwdriver end -->
<svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
<defs>
<linearGradient id="kn-steel-export" href="#kn-steel" data-light/>
<clipPath id="kn-clip-export"><path d="M0 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L148 -1.4Q141.6 -2.2 140.4 -6.2L141.4 -9.4Q141.8 -12 138.6 -12H0A12 12 0 0 0 0 12Z"/></clipPath>
</defs>
<path class="tool__ring" d="M0 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L148 -1.4Q141.6 -2.2 140.4 -6.2L141.4 -9.4Q141.8 -12 138.6 -12H0A12 12 0 0 0 0 12Z"/>
<path class="tool__body" fill="url(#kn-steel-export)" d="M0 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L148 -1.4Q141.6 -2.2 140.4 -6.2L141.4 -9.4Q141.8 -12 138.6 -12H0A12 12 0 0 0 0 12Z"/>
<g clip-path="url(#kn-clip-export)">
<path class="tool__brush" d="M0 -9.5H170M0 -6H170M0 -2.5H170M0 1.5H170M0 5H170M0 8.5H170"/>
<path class="tool__facet" d="M155 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L155 -0.6Z"/>
<path class="tool__nick" d="M44 8.4Q50.5 5.4 57 8.4"/>
<path class="tool__lip" d="M44.5 9.6Q50.5 6.8 56.5 9.6"/>
<rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
</g>
<path class="tool__lip" d="M147.6 -0.8Q142 -1.6 141 -5.6"/>
<path class="tool__lit" data-lit="top" d="M0 -11.5H138.6Q141.2 -11.5 140.9 -9.4"/>
<path class="tool__lit" data-lit="bottom" d="M0 11.5H160Q163.6 11.5 164 8.5"/>
<path class="tool__hit" d="M0 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L148 -1.4Q141.6 -2.2 140.4 -6.2L141.4 -9.4Q141.8 -12 138.6 -12H0A12 12 0 0 0 0 12Z"/>
</svg>
<span class="tool__etch" style="--x0: 56; --x1: 132; --cy: 0">
<svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v10.6M7.8 10.6 12 14.8l4.2-4.2"/><path d="M4.4 14.6v3.2A2.2 2.2 0 0 0 6.6 20h10.8a2.2 2.2 0 0 0 2.2-2.2v-3.2"/></svg>
<span class="tool__label">Export</span>
</span>
</button>
</div>
<div class="tool-slot" data-slot>
<button class="tool tool--blade" type="button" role="menuitem" tabindex="-1" data-tool="delete">
<!-- the blade: a satin flat, a mirror ground edge and the name etched in red -->
<svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
<defs>
<linearGradient id="kn-satin-delete" href="#kn-satin" data-light/>
<linearGradient id="kn-mirror-delete" href="#kn-mirror" data-light/>
<clipPath id="kn-clip-delete"><path d="M0 14H122C144 14 160 11 170 4.6Q171.2 3.6 169.4 2.6C156 -6.4 138 -14 114 -14H22Q19 -14 18 -12H0A13 13 0 0 0 0 14Z"/></clipPath>
</defs>
<path class="tool__ring" d="M0 14H122C144 14 160 11 170 4.6Q171.2 3.6 169.4 2.6C156 -6.4 138 -14 114 -14H22Q19 -14 18 -12H0A13 13 0 0 0 0 14Z"/>
<path class="tool__body" fill="url(#kn-satin-delete)" d="M0 14H122C144 14 160 11 170 4.6Q171.2 3.6 169.4 2.6C156 -6.4 138 -14 114 -14H22Q19 -14 18 -12H0A13 13 0 0 0 0 14Z"/>
<g clip-path="url(#kn-clip-delete)">
<path class="tool__brush" d="M0 -2H172M0 2H172M0 6H172M0 10H172"/>
<path class="tool__grind" fill="url(#kn-mirror-delete)" d="M22 -14H114C138 -14 156 -6.4 169.4 2.6L167 3.4C154 -1.2 136 -5.6 113 -5.6H25Q23 -5.6 22 -8Z"/>
<path class="tool__shoulder" d="M25 -5.6H113C136 -5.6 154 -1.2 167 3.4"/>
<path class="tool__nick" d="M40 9.6Q47 6.6 54 9.6"/>
<path class="tool__lip" d="M40.5 10.8Q47 8 53.5 10.8"/>
<rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
</g>
<path class="tool__edge" d="M22 -13.6H114C138 -13.6 156 -6 169 2.8"/>
<path class="tool__run" pathLength="100" d="M22 -13.6H114C138 -13.6 156 -6 169 2.8" data-run/>
<path class="tool__lit" data-lit="top" d="M22 -13.5H114C138 -13.5 156 -6 169 2.4"/>
<path class="tool__lit" data-lit="bottom" d="M0 13.5H122C144 13.5 160 10.6 169.5 4.4"/>
<path class="tool__hit" d="M0 14H122C144 14 160 11 170 4.6Q171.2 3.6 169.4 2.6C156 -6.4 138 -14 114 -14H22Q19 -14 18 -12H0A13 13 0 0 0 0 14Z"/>
</svg>
<span class="tool__etch" style="--x0: 52; --x1: 124; --cy: 4.2">
<svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4.4 6.9h15.2M9.6 6.9V5.2A1.2 1.2 0 0 1 10.8 4h2.4a1.2 1.2 0 0 1 1.2 1.2v1.7"/><path d="M6.4 6.9 7.2 18.3A1.9 1.9 0 0 0 9.1 20h5.8a1.9 1.9 0 0 0 1.9-1.7l.8-11.4M10.2 10.6v5.6M13.8 10.6v5.6"/></svg>
<span class="tool__label">Delete</span>
</span>
</button>
</div>
</div>
</div>
</div>
<p class="sr" aria-live="polite" data-say></p>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.
26 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “The pocket knife menu” from Animorio and fit it into my siteEach tool is a button drawn in SVG with the pivot at its origin, and every frame the script gives it rotate() for its angle and translateX() for how far it slides out. The angles where they stop come from SPREAD: a fan pointing away from the handle, with the blade set further apart. The ones above the middle open swinging over the handle, the ones below swinging under it; they open from the middle outwards and close the other way round, so none of them ever crosses a tool already at rest.
Each tool has an angle and a speed. It starts with the thumb’s flick (FLICK), the friction of the joint slows it down, a steady force pulls it on, and in the last 34 degrees the spring snaps (SNAP, growing with the square of how close it is). Against the stop it bounces back with BOUNCE of its speed and the spring brings it down again until it settles. It runs by hand on GSAP’s ticker in steps of 1/240 of a second, with the frame’s step capped at 1/30.
At every hit against the stop the handle gets a push on a stiff spring, a turn of a few tenths of a degree round the pivot, and a reflection sets off on the tool: a slanted rectangle with a white core between two dark bands, clipped to the outline, running from the pivot to the point. The smaller knocks of the bounce do not start it again. The knife slides sideways by how open the fan is, to stay in the middle.
The steel is a linearGradient in userSpaceOnUse that turns against the tool every frame (gradientTransform), so the bands of the reflections stay still in the room and the tool turns through them. The two long edges have a light line that brightens with the dot product of their normal and the direction of the lamp. The shadow sits on a still layer round each tool, so that it always falls the same way.
Once a tool is chosen, the others fold back and it plays its gesture with a GSAP timeline on its own numbers (turn, slide, the opening of the scissors); when the gesture is over it waits until all the others are home and then it shuts. The plate changes its words by rolling: first the old ones leave, then the new ones come in. A word too long for it narrows on the wdth axis of the Saira typeface until it fits.
The handle is a button with aria-haspopup, aria-expanded and aria-controls; the tools are menuitems in a role menu, inert while the knife is closed. Up and down arrows, Home, End, the initial, Enter and Space, Escape and Tab which close and give the focus back to the handle. The focus is an amber line following the tool’s outline. Only the outline of the steel, a little widened, takes the pointer, and the result is read out by an aria-live area.
| Name | File | Default | What it changes |
|---|---|---|---|
FLICK | script.js | 560 | The speed of the thumb’s flick that sets a tool going, in degrees per second. |
SNAP | script.js | 5600 | How hard the spring pulls the tool against its stop in the last stretch, in degrees per second squared. |
BOUNCE | script.js | 0.3 | How much speed a tool keeps as it bounces off its stop (0 none, 0.6 lively). |
STAGGER | script.js | 0.095 | The seconds between one tool setting off and the next. |
SPREAD | script.js | 22 | The degrees between one tool and the next in the open fan; the blade keeps a wider gap. |
LIFT | script.js | 6 | How far a tool slides out under the pointer, in pixels. |
HOLD | script.js | 1.6 | The seconds the plate says what was done. |
OPEN_WORD | script.js | 'Close' | What the plate says while the knife is open. |
DONE_WORDS | script.js | 5 | What the plate says after each tool, from the top one down. |
For the actions menu of a file, a project or an item in a list, in a site or an app with an object-like character: a tool for creatives, a file manager, a portfolio, a tool shop. From four to six short actions; with more the fan gets crowded and the words cover each other.
The knife is 360 units wide and at 390 pixels it fits at almost full scale (about 0.99): the open fan stays inside the screen and the closed handle is as high as on a computer. A tap opens and closes it like a click; a finger pressing a tool makes it slide out and light up like the pointer, and letting go chooses it.
No turning, no bouncing and no knocks: the knife fades out and comes back open or closed, the tool under the pointer brightens without sliding out, and choosing an action the tool lights up, then the knife fades back closed with the plate saying what was done. The plate’s words fade instead of rolling.
GSAP 3.15.0, Saira (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 15 KB; the libraries: GSAP 28 KB.
Tested on 6 October 2026
Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.