Elastic toggles
Two toggles, three temperaments. A switch you click anywhere to flip, and a two-sided toggle where you click the side you want, each built balanced, firm, and cartoonishly elastic. The knob stretches into a capsule as it leaves, springs back as it lands, and leans into your press before you let go. Every moving part is a transform, and a whole flight is handed to the browser in the click, so the motion stays smooth while the page behind it works. Stretch, squash, bounce, duration, and the press are all controls.
Build prompt
InteractionBuild a switch: a button with role="switch" that flips when you click anywhere on it. A 38 x 22 pill track and a 14px round knob that sits 4px in from every edge and travels 16px. Off: field-coloured track, muted knob. On: the track takes a quiet fill (the ink colour mixed 26% into the field colour, not a bright block) and the knob turns to the ink colour, both as layers faded in over 180ms. As it travels the knob stretches into a capsule and springs back. Rules that make it feel right, do not skip any: 1. Transform and opacity only. Nothing that animates may touch layout or paint: no width, left, background-color or border-radius changes. Colour changes are a second layer faded in by opacity. 2. The moving shape must stay a true shape while it stretches. Build it from three pieces: two copies of its rest shape and a bar between them. One copy stays, the other slides out by the stretch distance (translateX), and the bar scales (scaleX, origin left) to fill the gap. Never scaleX the shape itself, that turns round ends into ellipses. 3. A flight is one continuous motion, never a stretch followed by a settle (that stops at the seam and reads as two animations). The trailing edge of the shape makes the trip on a spring curve, and the stretch follows the speed: stretch = (stretch x shape width, capped at 80% of the trip) x current speed / top speed. So the shape is longest when it is fastest and round again once it has slowed down. For calm feels (bounce under 0.5) the shape must land round: also limit the stretch to 60% of the distance still to go, with a smooth minimum so there is no corner, so the front edge never reaches the wall first and waits for the back to squeeze in behind it. Fade that limit out as bounce rises to 0.5. 4. The curve is a damped spring response from 0 to 1, tuned to be at rest at the end of the duration, driven by one "bounce" number: 0 is critically damped with no overshoot, towards 1 it overshoots and wobbles. Nothing may ever leave the track: before each flight, lower the bounce until the widest swing of an edge fits in the gap around the shape (the gap less the 1px ring). Squash: the shape flattens in proportion to its length, then its height springs back past full with the unreduced bounce, since there is space above and below. Also clip the track as a safety net. 5. Hand the whole flight to the browser in the click: sample it into about 40 keyframes and start one Web Animations API animation per moving element (linear between keyframes). Never start part of a flight from script when an earlier part ends; on a busy page that freezes the shape mid-stretch. 6. Interruptible: a new flight starts from the pose on screen right now (read the computed transforms), so a second click mid-flight reverses without a jump. 7. State flips in the click (aria and the colour fade start at once). Offer onChange (in the click) and onSettle (when the shape comes to rest); heavy work belongs in onSettle. 8. Press feedback, on by default: on pointer down the shape leans toward where it is about to go and holds (150ms; a wide plate leans half as far as a knob would, relative to its width); releasing commits from that pose, dragging off springs it back. Expose pressFeedback (boolean) and pressAmount (0 to 1). 9. Feel is four numbers: stretch (in shape widths), squash (how much it flattens while stretched), bounce, duration. Ship three presets: Balanced (0.45, 0, 0, 520ms: it stretches but lands clean, with no overshoot), Firm (0, 0, 0, 220ms: a plain slide), Elastic (1, 0.22, 0.8, 1100ms: exaggerated, cartoon). 10. On and off are exactly the same size. Draw the edge as a 1px ring on top of everything (an inset box-shadow on a pseudo-element), present in both states, never as a border that the fill covers. Centre the shape by geometry so the gap is equal on every side at any scale. 11. prefers-reduced-motion: jump straight to the end state and still call onSettle. Grayscale, theme tokens only.
Append your stack, content, and brand constraints before handing it to your agent.