No.79 · Buttons· New
Yarn Wrap
Point at the button and a ball of yarn rolls round it from where you came in, unwinding as it goes and leaving the thread wrapped round the pill in loose laps that cross each other; the ball shrinks as it pays out. Leave and it winds itself back up. Press to make the ball hop.
The prompt
Written for the variant you picked above: Wool
Build a pill button that a ball of yarn wraps itself round. Props: children, yarn (one of three yarns), onClick, type and motion. Geometry (pure functions): the pill outline walked by distance with its tangent and outward normal, the distance nearest a point, and a coil — 2.3 laps round the outline from the entry point in the direction the pointer was moving, sampled every 3px, each sample pushed out from the edge by 7 + 4·sin(2π·3.3t/L) + 2·sin(2π·7.1t/L + 1) px so the laps drift in and out and cross each other. Plus running lengths along a polyline and the point (with direction) at any distance along it. An SVG overlay 34px round the button, pointer-events none, draws the coil twice through a mask: a 2.6px round-capped stroke in the yarn colour with a faint shadow, and a lighter 1.1px dashed strand (1.6 2.4) on top for the twist of the plies. The mask is the same path as an 8px white stroke whose dash offset reveals it. A ball rides the head of the revealed thread: a circle with five curved strand lines, rolling — rotated by distance travelled over its radius — and shrinking from 11px to 6px as it pays out. Progress runs on requestAnimationFrame: towards 1 on hover at about 0.6/s, slowing as it fills, and back to 0 at 1.5/s on leave, so the yarn winds itself up quicker than it came out; the ball fades when it's home. Pressing makes the ball hop 10px on a sine. Keyboard focus wraps from the top centre; touch wraps on press for 2.4s. Reduced motion jumps straight to wrapped or unwrapped. The button: a 54px pill in a deep colour with a light label and a 1px lift on hover. Selected variant — Wool (wool): Tomato-red wool #c8432f with a peach twist #f19a7f round a dark brown #2c2420 button, on oatmeal #f2e8dc. This selection takes precedence over references to other themes or options in the general brief. Implement this selected appearance; do not add a variant picker or alternative appearances unless requested. Implementation requirements: Use React and TypeScript. Keep the component self-contained, with uniquely scoped CSS and no animation libraries. Include required imports, styles and font setup in the result. Interaction: Point at the button to wrap it in yarn; leave to wind it back; press to make the ball hop. Keyboard focus wraps it too. Motion: The thread pays out over about two seconds and winds back in under one; the ball rolls and shrinks with it. Reduced motion skips the movement. Accessibility: A real button with its text label; the yarn is decorative SVG, hidden and pointer-transparent. Responsive behavior: Leave about 34px of room round the button. Touch: A press wraps the button for a couple of seconds. Preserve keyboard access, visible focus and reduced-motion behavior. Keep fictional demo data and simulated service calls in a separate usage example; expose callbacks for real actions and recover from rejected requests. Keep usage-example links inside the demo: use local buttons or prevent navigation. Demonstration links must not navigate to Vitrine routes or open other pages; preserve real URL support in the reusable component itself. Verify the result at narrow and wide viewport sizes, with keyboard input and reduced motion enabled.
Notes
- Interaction
- Point at the button to wrap it in yarn; leave to wind it back; press to make the ball hop. Keyboard focus wraps it too.
- Motion
- The thread pays out over about two seconds and winds back in under one; the ball rolls and shrinks with it. Reduced motion skips the movement.
- Accessibility
- A real button with its text label; the yarn is decorative SVG, hidden and pointer-transparent.
- Responsive
- Leave about 34px of room round the button.
- On touch
- A press wraps the button for a couple of seconds.