A button with a multicolour border and soft glow that slowly travels around it. The label sits on the normal button surface, so its contrast is the plain Button's. 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 { RainbowButton } from "@rdloom/react";
export default function RainbowButtonBasicExample() {
return (
<div className="flex w-full justify-center">
<RainbowButton>Upgrade plan</RainbowButton>
</div>
);
}Installation
npx rdloom add rainbow-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/rainbow-button.jsonWorks, but without upgrade tracking, and you import the motion CSS yourself.
Usage
import { RainbowButton } from "@rdloom/react";
<div className="flex w-full justify-center">
<RainbowButton>Upgrade plan</RainbowButton>
</div>Variants
import { RainbowButton } from "@rdloom/react";
export default function RainbowButtonVariantsExample() {
return (
<div className="flex w-full flex-wrap items-center justify-center gap-4">
<RainbowButton size="sm">Small</RainbowButton>
<RainbowButton variant="secondary">Secondary</RainbowButton>
<RainbowButton variant="ghost" size="lg">Large ghost</RainbowButton>
</div>
);
}Paused
import { useState } from "react";
import { Button, RainbowButton } from "@rdloom/react";
// Anything that keeps moving should be stoppable: isPaused freezes the effect.
export default function RainbowButtonPausedExample() {
const [paused, setPaused] = useState(false);
return (
<div className="flex w-full items-center justify-center gap-3">
<RainbowButton isPaused={paused}>Get started</RainbowButton>
<Button variant="ghost" size="sm" onPress={() => setPaused((p) => !p)} aria-pressed={paused}>
{paused ? "Resume motion" : "Pause motion"}
</Button>
</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" |
durationSeconds for one full turn of the colours. | number | 4 |
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 can also be shown still with the rdm-still class or isPaused
- Does not flash: nothing changes more than three times in a second (WCAG 2.3.1)
- Forced colors: the effect is dropped and the Button's own border and text remain
- Optional: it is a separate component, so projects that do not add it carry none of its code
- The label keeps the Button's contrast: the colours live only in the border and glow, never behind the text
Guidelines
Use it when
- A single standout action, like Upgrade plan
Avoid it when
- More than one per screen: it stops standing out
- Dense toolbars and forms
Don't
- Using it for every button
- A very short duration, which reads as flicker
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