Switch
An on/off toggle. Two sizes; the track fills with the selected green when checked. Underneath it runs three clocks at once: the knob's travel is a 350ms keyframe bounce on an overshooting bezier rather than a spring, the press is the 170ms press spring, and a third animation squeezes the knob while it is in flight. They coexist because each owns a different transform property — translate for the journey, transform for the press, scale for the squish — and the browser composes all three into one matrix, so the two squeezes multiply during an overlap instead of one clobbering the other.
import { Switch, Label } from "@pestoui/core"Sizes & states
Double-bounce travel
The knob crosses on keyframes, not a spring: 350ms on cubic-bezier(0.34, 1.35, 0.64, 1), with two stops written past the destination. Sampled frame by frame off the live thumb, it covers 94% of its 14.67px in the first 65ms, sails 1.61px beyond its stop at about 130ms, walks that back, and touches down a second time roughly 0.29px over at around 275ms before settling. The second, much smaller bounce is the affordance — one overshoot reads as a spring, two read as something with mass that was thrown. The track colour is not animated at all: its transition-duration measures 0s, so the fill answers on the same frame as the click while the knob is still crossing, which is what separates “the state changed” from “the knob is moving”. Nothing squishes on load either — the travel animation is armed by the first change, so a settings list of eight rows arrives still.
Press feedback
Press and hold either knob without releasing. It compresses to 90% and leans four pixels toward where it is about to go — right when the switch is off, left when it is on — so the control answers your finger before it answers the click. That lean is on the 170ms press spring, fast enough to read as resistance rather than lag. Release, and it travels. A second squeeze runs during the journey itself, on keyframes rather than a transition, because a there-and-back on one property cannot be written as a transition between two values. It troughs at 90% a fifth of the way through the 350ms travel clock — measured, the knob has already covered 94% of its distance by then and crosses its stop moments later — so the compression reads as the knob absorbing its own arrival rather than as a decoration on top of the movement. It releases on a 1.2% rebound at 55%, while the bounce is still walking back its overshoot. Because it is tied to the travel and not to the pointer, a keyboard toggle shows a knob under load too.
One clock at both sizes
Both sizes run the identical 350ms and the identical curve — the small knob simply covers less ground in that time, 12px against the default's 14.67px. Keeping the clock fixed and letting the distance vary is what makes the two read as one object at two scales; scaling duration with distance instead would make the small toggle feel twitchy next to the large one. The two bounce stops are written in absolute pixels rather than as a share of the distance — +1px, then +0.3px — at both sizes, so the small knob's overshoot is a slightly larger fraction of its own journey. The disabled switch changes opacity only, so nothing shifts position when it becomes available again.
Props
Pesto UI · 117 documented components