RippleButton
ripple-buttonEmits pointer-positioned ripples when the button is activated.
Usage
Basic usage
After clicking, Material Feng Shui ripples will spread in a circle from the landing point, and will be automatically removed when the animation ends.
tsx
<RippleButton>Click here to see the ripples</RippleButton>Ripple speed
duration Controls the duration of a single ripple diffusion.
tsx
<RippleButton duration="400ms">Fast</RippleButton>
<RippleButton duration="900ms">Slow</RippleButton>Customized ripple color
rippleColor specifies the ripple color, the default is primary-foreground.
tsx
<RippleButton rippleColor="rgba(255,255,255,0.7)">High contrast ripple</RippleButton>When to use
Use RippleButton when an action needs Material-style feedback that expands from the pointer position. Use Button for ordinary actions, or ShimmerButton, RainbowButton, or PulsatingButton for continuous visual emphasis rather than click feedback.
Import
ts
import { RippleButton } from "@hulianui/ui"Props
Accepts all native <button> props, including disabled and type.
| Name | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "md" | Size step, on the same 32/40/48px scale as Button. |
| variant | "solid" | "outline" | "ghost" | "soft" | "solid" | Appearance step, colored exactly like the Button step of the same name. There is no `link`: the ripple needs a box, see the usage notes. |
| tone | "brand" | "neutral" | "success" | "warning" | "danger" | "brand" | Semantic tone, colored exactly like the Button step of the same name. There is no `current`: the default ripple color is derived from the tone, and an inherited color derives nothing. |
| rippleColor | string | Derived from variant × tone | Ripple color. Solid steps default to the foreground color of the tone (a light ripple on a dark surface); every other step defaults to the tone itself. Passing a value overrides the derivation. |
| duration | string | "600ms" | Duration of one ripple animation. |
Events
| Event | Type | Description |
|---|---|---|
| onClick | (e: MouseEvent<HTMLButtonElement>) => void | Transparently transmit native click callback (click triggers ripple diffusion at the same time) |
Slots
| Slot | Type | Description |
|---|---|---|
| children | ReactNode | Button content (copy/icon) |
| render | ReactElement | Custom rendered element, such as <a> or Next.js <Link>; button styles and the ripple layer are merged into it. |
Example
tsx
<RippleButton>Show ripple</RippleButton>tsx
<RippleButton duration="900ms">Show slow ripple</RippleButton>tsx
{/* Appearance is picked with variant x tone, exactly like Button - no buttonVariants() injection */}
<RippleButton variant="outline">Cancel</RippleButton>
<RippleButton variant="ghost">Later</RippleButton>
<RippleButton tone="danger">Delete</RippleButton>
<RippleButton variant="outline" tone="danger">Delete</RippleButton>tsx
{/* Looks like a solid button but has to be a real link: middle-click, copy link address, crawlable */}
<RippleButton render={<Link href="/docs" />}>Read the docs</RippleButton>Usage guidelines
- Shares its base and its color steps with [Button](/components/button). Layout, the three
sizesteps (32/40/48px tall), the focus ring, the disabled treatment, andforwardRefall come from the sameEFFECT_BUTTON_BASE_CLASSandBUTTON_SIZE_CLASS, and everyvariantxtonecell carries the same color as the Button cell of the same name (#233). Corner radius, shadow and color hover are deliberately not shared: the radius belongs to the effect layer, none of the four effect buttons carryshadow-sm, and color hover conflicts with this base (next note). Before 0.27.0 each effect button rolled its own markup, lacked all of the above, and sized itself withpx-6 py-3, so a toolbar row came out uneven (#126). - There is no color hover; the ripple is the feedback. The effect base deliberately omits
transition-colors(these components animate a background, not a color), so ahover:bg-*would land as an untransitioned jump. Avariant="ghost"ripple button therefore rests as plain text and does not tint under the pointer. When a secondary action has to react on hover, useButton variant="ghost"; this component answers on press. - `variant` has no `link` and `tone` has no `current`. The ripple needs a box:
linkremoves the height and the horizontal padding, so the ripple on anh-auto px-0label is clipped to a sliver or smears across the text. And the default ripple color is derived from the tone, which is exactly whatcurrentrefuses to provide. PassrippleColor="currentColor"explicitly if the ripple should follow the container. - `variant` is about appearance, `render` is about semantics. The note above ("use
Button variant="link"") is about how the control looks, that is, whether it has a button box. If you need something that looks exactly like this solid button but has to be an<a>(middle-click to open in a new tab, copy link address, visible to crawlers), that isrender={<a href="…" />}; the ripple and the colors are unchanged (#256). - The derived ripple color depends on the step: solid steps use the foreground color of the tone (a light ripple on a dark surface), while outline, ghost and soft use the tone itself. The reverse (a foreground color on a light surface) is an almost invisible ring, so keep the surface in mind when overriding
rippleColor. - The ripple animation is automatically suppressed under
prefers-reduced-motion: reduce; consumers do not need to add a separate motion check.
Related
Button · ShimmerButton · RainbowButton · PulsatingButton · ButtonGroup · SocialButton
Playground
<RippleButton duration="600ms">Click here to see the ripples</RippleButton>