UI Components Easy
Rainbow Button
A vibrant button with an animated rainbow gradient border that continuously cycles through the color spectrum.
Open in Lab
MCP
css javascript vue svelte
Targets: TS JS HTML React Vue Svelte
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
background: #0a0a0a;
color: #f1f5f9;
min-height: 100vh;
}
.demo {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2rem;
padding: 2rem;
}
.demo-title {
font-size: 1.5rem;
font-weight: 700;
color: #e2e8f0;
}
.hint {
color: #525252;
font-size: 0.875rem;
margin-bottom: 1rem;
}
.button-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 2.5rem;
}
/* --- Rainbow button --- */
.rainbow-btn {
--rainbow-speed: 3s;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 2px;
border-radius: 12px;
border: none;
outline: none;
cursor: pointer;
background: conic-gradient(
from 0deg,
#ff0000,
#ff8800,
#ffff00,
#00ff00,
#0088ff,
#8800ff,
#ff00ff,
#ff0000
);
background-size: 100% 100%;
animation: rainbow-spin var(--rainbow-speed) linear infinite;
transition: transform 0.2s ease, filter 0.2s ease;
}
.rainbow-btn:hover {
transform: scale(1.04);
filter: brightness(1.2);
}
.rainbow-btn:active {
transform: scale(0.97);
}
.rainbow-btn__label {
display: block;
padding: 0.875rem 2rem;
border-radius: 10px;
background: #0a0a0a;
color: #f1f5f9;
font-size: 0.9375rem;
font-weight: 600;
letter-spacing: 0.01em;
transition: background 0.3s ease;
}
.rainbow-btn:hover .rainbow-btn__label {
background: #141414;
}
/* Filled variant — rainbow background with white text */
.rainbow-btn--filled .rainbow-btn__label {
background: transparent;
color: #fff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}
/* Sizes */
.rainbow-btn--sm .rainbow-btn__label {
padding: 0.625rem 1.5rem;
font-size: 0.8125rem;
}
.rainbow-btn--lg .rainbow-btn__label {
padding: 1.125rem 2.75rem;
font-size: 1.0625rem;
}
/* Rounded pill */
.rainbow-btn--rounded {
border-radius: 999px;
}
.rainbow-btn--rounded .rainbow-btn__label {
border-radius: 999px;
}
/* Rainbow animation */
@keyframes rainbow-spin {
0% {
filter: hue-rotate(0deg);
}
100% {
filter: hue-rotate(360deg);
}
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
.rainbow-btn {
animation: none;
}
}// Rainbow Button — minimal JS (CSS handles the animation)
(function () {
"use strict";
const buttons = document.querySelectorAll(".rainbow-btn");
buttons.forEach((btn) => {
btn.addEventListener("click", () => {
btn.style.animation = "none";
btn.offsetHeight; // trigger reflow
btn.style.animation = "";
});
});
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Rainbow Button</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<h2 class="demo-title">Rainbow Buttons</h2>
<p class="hint">Animated rainbow gradient borders</p>
<div class="button-row">
<button class="rainbow-btn">
<span class="rainbow-btn__label">Explore Now</span>
</button>
<button class="rainbow-btn rainbow-btn--filled">
<span class="rainbow-btn__label">Get Premium</span>
</button>
</div>
<div class="button-row">
<button class="rainbow-btn rainbow-btn--lg">
<span class="rainbow-btn__label">Start Your Journey</span>
</button>
<button class="rainbow-btn rainbow-btn--sm rainbow-btn--filled">
<span class="rainbow-btn__label">Try Free</span>
</button>
</div>
<div class="button-row">
<button class="rainbow-btn rainbow-btn--rounded">
<span class="rainbow-btn__label">Join Community</span>
</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>import { useRef, useCallback, type CSSProperties, type ReactNode, type MouseEvent } from "react";
interface RainbowButtonProps {
children: ReactNode;
onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
filled?: boolean;
size?: "sm" | "md" | "lg";
rounded?: boolean;
speed?: string;
className?: string;
}
const sizeStyles: Record<string, CSSProperties> = {
sm: { padding: "0.625rem 1.5rem", fontSize: "0.8125rem" },
md: { padding: "0.875rem 2rem", fontSize: "0.9375rem" },
lg: { padding: "1.125rem 2.75rem", fontSize: "1.0625rem" },
};
export function RainbowButton({
children,
onClick,
filled = false,
size = "md",
rounded = false,
speed = "3s",
className = "",
}: RainbowButtonProps) {
const btnRef = useRef<HTMLButtonElement>(null);
const handleClick = useCallback(
(e: MouseEvent<HTMLButtonElement>) => {
const btn = btnRef.current;
if (btn) {
btn.style.animation = "none";
btn.offsetHeight;
btn.style.animation = "";
}
onClick?.(e);
},
[onClick]
);
const radius = rounded ? "999px" : "12px";
const innerRadius = rounded ? "999px" : "10px";
const outerStyle: CSSProperties = {
position: "relative",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
padding: "2px",
borderRadius: radius,
border: "none",
outline: "none",
cursor: "pointer",
background:
"conic-gradient(from 0deg, #ff0000, #ff8800, #ffff00, #00ff00, #0088ff, #8800ff, #ff00ff, #ff0000)",
animation: `rainbow-spin ${speed} linear infinite`,
transition: "transform 0.2s ease, filter 0.2s ease",
};
const labelStyle: CSSProperties = {
display: "block",
...sizeStyles[size],
borderRadius: innerRadius,
background: filled ? "transparent" : "#0a0a0a",
color: filled ? "#fff" : "#f1f5f9",
fontWeight: 600,
letterSpacing: "0.01em",
textShadow: filled ? "0 1px 2px rgba(0,0,0,0.4)" : "none",
transition: "background 0.3s ease",
};
return (
<>
<style>{`
@keyframes rainbow-spin {
0% { filter: hue-rotate(0deg); }
100% { filter: hue-rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
.rainbow-btn-react { animation: none !important; }
}
`}</style>
<button
ref={btnRef}
onClick={handleClick}
className={`rainbow-btn-react ${className}`}
style={outerStyle}
onMouseEnter={(e) => {
e.currentTarget.style.transform = "scale(1.04)";
e.currentTarget.style.filter = "brightness(1.2) hue-rotate(0deg)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = "scale(1)";
e.currentTarget.style.filter = "";
}}
>
<span style={labelStyle}>{children}</span>
</button>
</>
);
}
export default function RainbowButtonDemo() {
return (
<div
style={{
minHeight: "100vh",
background: "#0a0a0a",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: "2rem",
padding: "2rem",
fontFamily: "system-ui, -apple-system, sans-serif",
}}
>
<h2 style={{ fontSize: "1.5rem", fontWeight: 700, color: "#e2e8f0" }}>Rainbow Buttons</h2>
<p style={{ color: "#525252", fontSize: "0.875rem" }}>Animated rainbow gradient borders</p>
<div style={{ display: "flex", flexWrap: "wrap", gap: "2.5rem", justifyContent: "center" }}>
<RainbowButton>Explore Now</RainbowButton>
<RainbowButton filled>Get Premium</RainbowButton>
</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: "2.5rem", justifyContent: "center" }}>
<RainbowButton size="lg">Start Your Journey</RainbowButton>
<RainbowButton size="sm" filled>
Try Free
</RainbowButton>
</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: "2.5rem", justifyContent: "center" }}>
<RainbowButton rounded>Join Community</RainbowButton>
</div>
</div>
);
}<script setup>
const sizeMap = {
sm: { padding: "0.625rem 1.5rem", fontSize: "0.8125rem" },
md: { padding: "0.875rem 2rem", fontSize: "0.9375rem" },
lg: { padding: "1.125rem 2.75rem", fontSize: "1.0625rem" },
};
function outerStyle(rounded, speed) {
return {
position: "relative",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
padding: "2px",
borderRadius: rounded ? "999px" : "12px",
border: "none",
outline: "none",
cursor: "pointer",
background:
"conic-gradient(from 0deg, #ff0000, #ff8800, #ffff00, #00ff00, #0088ff, #8800ff, #ff00ff, #ff0000)",
animation: `rainbow-spin ${speed} linear infinite`,
transition: "transform 0.2s ease, filter 0.2s ease",
};
}
function labelStyle(filled, size, rounded) {
return {
display: "block",
...sizeMap[size],
borderRadius: rounded ? "999px" : "10px",
background: filled ? "transparent" : "#0a0a0a",
color: filled ? "#fff" : "#f1f5f9",
fontWeight: 600,
letterSpacing: "0.01em",
textShadow: filled ? "0 1px 2px rgba(0,0,0,0.4)" : "none",
transition: "background 0.3s ease",
};
}
const row1 = [
{ label: "Explore Now", filled: false, size: "md", rounded: false, speed: "3s" },
{ label: "Get Premium", filled: true, size: "md", rounded: false, speed: "3s" },
];
const row2 = [
{ label: "Start Your Journey", filled: false, size: "lg", rounded: false, speed: "3s" },
{ label: "Try Free", filled: true, size: "sm", rounded: false, speed: "3s" },
];
const row3 = [
{ label: "Join Community", filled: false, size: "md", rounded: true, speed: "3s" },
];
</script>
<template>
<div style="min-height:100vh;background:#0a0a0a;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2rem;padding:2rem;font-family:system-ui,-apple-system,sans-serif">
<h2 style="font-size:1.5rem;font-weight:700;color:#e2e8f0">Rainbow Buttons</h2>
<p style="color:#525252;font-size:0.875rem">Animated rainbow gradient borders</p>
<div style="display:flex;flex-wrap:wrap;gap:2.5rem;justify-content:center">
<button
v-for="btn in row1" :key="btn.label"
:style="outerStyle(btn.rounded, btn.speed)"
@mouseenter="e => { e.currentTarget.style.transform='scale(1.04)'; e.currentTarget.style.filter='brightness(1.2)' }"
@mouseleave="e => { e.currentTarget.style.transform='scale(1)'; e.currentTarget.style.filter='' }"
>
<span :style="labelStyle(btn.filled, btn.size, btn.rounded)">{{ btn.label }}</span>
</button>
</div>
<div style="display:flex;flex-wrap:wrap;gap:2.5rem;justify-content:center">
<button
v-for="btn in row2" :key="btn.label"
:style="outerStyle(btn.rounded, btn.speed)"
@mouseenter="e => { e.currentTarget.style.transform='scale(1.04)'; e.currentTarget.style.filter='brightness(1.2)' }"
@mouseleave="e => { e.currentTarget.style.transform='scale(1)'; e.currentTarget.style.filter='' }"
>
<span :style="labelStyle(btn.filled, btn.size, btn.rounded)">{{ btn.label }}</span>
</button>
</div>
<div style="display:flex;flex-wrap:wrap;gap:2.5rem;justify-content:center">
<button
v-for="btn in row3" :key="btn.label"
:style="outerStyle(btn.rounded, btn.speed)"
@mouseenter="e => { e.currentTarget.style.transform='scale(1.04)'; e.currentTarget.style.filter='brightness(1.2)' }"
@mouseleave="e => { e.currentTarget.style.transform='scale(1)'; e.currentTarget.style.filter='' }"
>
<span :style="labelStyle(btn.filled, btn.size, btn.rounded)">{{ btn.label }}</span>
</button>
</div>
</div>
</template>
<style>
@keyframes rainbow-spin {
0% { filter: hue-rotate(0deg); }
100% { filter: hue-rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
button { animation: none !important; }
}
</style><script>
const sizeMap = {
sm: "padding: 0.625rem 1.5rem; font-size: 0.8125rem;",
md: "padding: 0.875rem 2rem; font-size: 0.9375rem;",
lg: "padding: 1.125rem 2.75rem; font-size: 1.0625rem;",
};
function wrapperStyle(rounded) {
return [
"position: relative",
"display: inline-flex",
"align-items: center",
"justify-content: center",
"padding: 2px",
`border-radius: ${rounded ? "999px" : "12px"}`,
"border: none",
"outline: none",
"cursor: pointer",
"background: conic-gradient(from 0deg, #ff0000, #ff8800, #ffff00, #00ff00, #0088ff, #8800ff, #ff00ff, #ff0000)",
"transition: transform 0.2s ease, filter 0.2s ease",
].join("; ");
}
function innerStyle(filled, size, rounded) {
return [
"display: block",
sizeMap[size],
`border-radius: ${rounded ? "999px" : "10px"}`,
`background: ${filled ? "transparent" : "#0a0a0a"}`,
`color: ${filled ? "#fff" : "#f1f5f9"}`,
"font-weight: 600",
"letter-spacing: 0.01em",
`text-shadow: ${filled ? "0 1px 2px rgba(0,0,0,0.4)" : "none"}`,
"transition: background 0.3s ease",
].join("; ");
}
const rows = [
[
{ label: "Explore Now", filled: false, size: "md", rounded: false },
{ label: "Get Premium", filled: true, size: "md", rounded: false },
],
[
{ label: "Start Your Journey", filled: false, size: "lg", rounded: false },
{ label: "Try Free", filled: true, size: "sm", rounded: false },
],
[
{ label: "Join Community", filled: false, size: "md", rounded: true },
],
];
function onMouseEnter(e) {
e.currentTarget.style.transform = "scale(1.04)";
e.currentTarget.style.filter = "brightness(1.2)";
}
function onMouseLeave(e) {
e.currentTarget.style.transform = "scale(1)";
e.currentTarget.style.filter = "";
}
</script>
<div style="min-height:100vh;background:#0a0a0a;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2rem;padding:2rem;font-family:system-ui,-apple-system,sans-serif">
<h2 style="font-size:1.5rem;font-weight:700;color:#e2e8f0">Rainbow Buttons</h2>
<p style="color:#525252;font-size:0.875rem">Animated rainbow gradient borders</p>
{#each rows as row}
<div style="display:flex;flex-wrap:wrap;gap:2.5rem;justify-content:center">
{#each row as btn}
<button
class="rainbow-btn"
style={wrapperStyle(btn.rounded)}
on:mouseenter={onMouseEnter}
on:mouseleave={onMouseLeave}
>
<span style={innerStyle(btn.filled, btn.size, btn.rounded)}>{btn.label}</span>
</button>
{/each}
</div>
{/each}
</div>
<style>
.rainbow-btn {
animation: rainbow-spin 3s linear infinite;
}
@keyframes rainbow-spin {
0% { filter: hue-rotate(0deg); }
100% { filter: hue-rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
.rainbow-btn { animation: none !important; }
}
</style>Rainbow Button
A button featuring an animated rainbow gradient border that smoothly cycles through the entire color spectrum, creating a mesmerizing and attention-grabbing effect.
How it works
- A
conic-gradientwith rainbow hues is applied to a pseudo-element behind the button - The
@keyframes rainbow-spinanimation rotates thehue-rotatefilter continuously - The inner button content sits on a dark background, making the border glow pop
Customization
- Border width: Adjust the padding on the outer wrapper or the
inseton::before - Speed: Change
animation-durationfor faster or slower color cycling - Background: Swap the inner background for a semi-transparent version to let color bleed through
When to use it
- Premium or special action buttons
- Creative portfolio CTAs
- Gaming and entertainment interfaces