A button with a soft ring that keeps radiating out, to draw the eye to the one action that matters now. Builds on Button.
Optional, and kept apart.
Nothing here is included in a project unless you add it, and the plain component it builds on is unchanged. This effect is decoration: it is hidden from screen readers, stands still for visitors who prefer reduced motion, and can be paused. Use Still on a preview to see that version.
import { PulseButton } from "@rdloom/react";
export default function PulseButtonBasicExample() {
return (
<PulseButton>Claim offer</PulseButton>
);
}Installation
npx rdloom add pulse-buttonCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.
It also adds rdloom-motion.css next to your tokens and imports it from the tokens file, once: no setup, and no inline styles. The plain components you already use are untouched.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/pulse-button.jsonWorks, but without upgrade tracking, and you import the motion CSS yourself.
Usage
import { PulseButton } from "@rdloom/react";
<PulseButton>Claim offer</PulseButton>Custom color
import { PulseButton } from "@rdloom/react";
export default function PulseButtonCustomColorExample() {
return (
<div className="flex flex-wrap items-center gap-6">
<PulseButton color="var(--rd-color-feedback-success)" variant="secondary">
Go live
</PulseButton>
<PulseButton color="var(--rd-color-feedback-danger)" variant="danger" duration={1.6}>
Stop recording
</PulseButton>
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
childrenrequiredButton label. Icon-only buttons must pass aria-label instead. | node | none |
variantVisual emphasis, as on Button. | "primary" | "secondary" | "ghost" | "danger" | "primary" |
sizeHeight, padding and font size, as on Button. | "sm" | "md" | "lg" | "md" |
colorColour of the ring, any CSS colour. Defaults to the action colour. | string | none |
durationSeconds for one pulse. | number | 2 |
isPausedStops the animation. Moving content that lasts more than five seconds should be stoppable. | boolean | false |
isDisabledPrevents interaction and dims the button. | boolean | false |
isLoadingShows a spinner and blocks presses; the pending state is announced. | boolean | false |
onPressCalled when the button is pressed by mouse, touch or keyboard. | () => void | none |
Accessibility
Role button, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab focuses it
- Enter or Space presses it
Screen readers announce
- Announced exactly as a Button: its label, as a button; the effect adds nothing to what is read
What your code must do
- Decoration only: the animated layers are hidden from assistive technology and never take focus
- Stands still for visitors who ask for less motion (prefers-reduced-motion), and the effect can also be shown still with the rdm-still class
- The pulse is a box shadow outside the button, so it never changes the label's contrast
- A disabled button stops pulsing
- Does not flash: nothing changes more than three times in a second (WCAG 2.3.1)
- Optional: it is a separate component, so projects that do not add it carry none of its code
Guidelines
Use it when
- A time-limited offer or a step that is waiting on the user
Avoid it when
- Anything that stays on screen for a long time: constant motion becomes noise
Don't
- Pulsing several buttons at once
- Using it where people are reading
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-action-primary--rd-color-action-on-primary--rd-color-action-danger--rd-color-surface-default--rd-color-text-default--rd-color-border-default--rd-color-focus-ring--rd-radius-control