Ripple
Expands a circle from the point that was pressed. The only decorator of the kit that renders a node, and the only one that holds state.
API
Props
| prop | type | default | |
|---|---|---|---|
children? | ReactElement | — | the element to ripple; cloned, its children extended |
rippleClassName? | string | "bg-primary-darken" | classes of the circle, usually its colour |
Examples
Usage
<Ripple>
<button className="rounded-lg border-2 border-border px-3 py-2">press me</button>
</Ripple>Details
Where it departs from the others
The other decorators are pure CSS. A ripple cannot be: it needs one element per press, positioned where the pointer landed, animated once and then discarded. So Ripple keeps the current ripple in state and appends a span to the children of its child — it still clones rather than wraps, but it does add a node.
It also means the child must accept children of its own, on top of the usual className and onPointerDown. The clone sets relative overflow-hidden on it, which is what clips the circle to the shape of the element, rounded corners included.
The geometry
The circle is placed at the press point and sized to twice the diagonal of the element, which guarantees it covers the surface from any corner — the farthest point of a rectangle from any position inside it is at most its diagonal.
size = Math.hypot(bounds.width, bounds.height) * 2;
x = event.clientX - bounds.left;
y = event.clientY - bounds.top;From there a 500ms animation scales it from zero to full while fading it from half opacity to nothing, and the ripple removes itself on animationend. Each press gets a fresh key, so pressing again restarts the animation instead of resuming it.
Colour and contrast
rippleClassName is a class list rather than a colour prop, so it takes any utility — an opacity, a gradient, a blend mode. The default is the darker primary, which reads on the kit's surfaces; over a coloured or inverted background, pick something with the contrast you need.
<Ripple rippleClassName="bg-on-main/30">
<Card as="button">…</Card>
</Ripple>Button already wraps itself in a Ripple, so applying one over a Button would give you two circles. Reach for it on surfaces that are pressable but are not Buttons — a card acting as a control, a custom row.
Pointer, not keyboard
The ripple starts on pointerdown, so it is feedback for a press and does not fire when a control is activated with the keyboard. That is deliberate — it is an animation about where you touched — but it means a ripple is never the only feedback a control should have.