liten

Rainbow Button

A button whose top-lit depth border is walked by a band of rainbow light that lifts into the label on hover.

Installation

Complete the shared Setup first, then copy the component into components/ui/rainbow-button.tsx.

components/ui/rainbow-button.tsx
'use client';import * as React from 'react';import { cn } from '@/lib/cn';const FOCUS =  'focus-visible:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:[outline-color:var(--rainbow-focus)]';export type RainbowButtonProps = {  borderWidth?: number;  speed?: number;  accent?: string;} & React.ButtonHTMLAttributes<HTMLButtonElement>;export function RainbowButton({  borderWidth = 2,  speed = 4,  accent = '#22d3ee',  children = 'Get Unlimited Access',  className,  style,  ...props}: RainbowButtonProps) {  const vars = {    '--rainbow-focus': accent,    '--rainbow-bw': `${borderWidth}px`,    '--rainbow-speed': `${speed}s`,  } as React.CSSProperties;  return (    <button      type="button"      style={{ ...vars, ...style }}      className={cn(        'bloom-edge rainbow-btn group relative isolate cursor-pointer select-none rounded-[14px]',        'shadow-[0_1px_2px_0_rgba(0,0,0,0.05),0_5px_14px_-10px_rgba(0,0,0,0.1)]',        'dark:shadow-[0_1px_2px_0_rgba(0,0,0,0.4),0_6px_16px_-10px_rgba(0,0,0,0.45)]',        'transition-transform duration-100 ease-out active:scale-[0.98] motion-reduce:active:scale-100',        FOCUS,        className,      )}      {...props}    >      <span        aria-hidden        className={cn(          'rainbow-ring pointer-events-none absolute inset-0 rounded-[inherit] [padding:var(--rainbow-bw)]',          '[-webkit-mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]',          '[-webkit-mask-composite:xor] [mask-composite:exclude]',        )}      />      <span        className={cn(          'relative flex items-center justify-center px-6 py-3',          'text-[15px] font-medium tracking-[-0.01em]',          'bg-gradient-to-b from-white to-[#f4f4f5] text-neutral-900',          'dark:from-[#1d1d1d] dark:to-[#141414] dark:text-white',          'shadow-[inset_0_1px_0_0_rgba(255,255,255,0.06)]',          '[margin:var(--rainbow-bw)] [border-radius:calc(14px-var(--rainbow-bw))]',        )}      >        <span className="rainbow-label">{children}</span>      </span>    </button>  );}

Add the animations to your global.css.

global.css
@property --rb-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}
@keyframes rainbow-walk {
  to {
    --rb-angle: 360deg;
  }
}
@keyframes rainbow-hue {
  to {
    filter: hue-rotate(360deg);
  }
}
@keyframes rainbow-text-walk {
  to {
    background-position: 200% 50%;
  }
}
.rainbow-ring {
  background: conic-gradient(
    from var(--rb-angle),
    transparent 0deg,
    transparent 210deg,
    #ff3b5c 250deg,
    #ff8a3d 270deg,
    #ffe14d 290deg,
    #4ade80 310deg,
    #22d3ee 330deg,
    #b06bff 350deg,
    transparent 360deg
  );
  animation:
    rainbow-walk var(--rainbow-speed, 4s) linear infinite,
    rainbow-hue calc(var(--rainbow-speed, 4s) * 2.5) linear infinite;
  transition:
    opacity 160ms cubic-bezier(0.23, 1, 0.32, 1),
    transform 160ms cubic-bezier(0.23, 1, 0.32, 1);
}
@media (hover: hover) and (pointer: fine) {
  .rainbow-btn:hover .rainbow-ring {
    opacity: 0;
    transform: scale(1.035);
    transition-duration: 280ms;
  }
  .rainbow-btn:hover .rainbow-label {
    background-image: linear-gradient(
      90deg,
      #ff3b5c,
      #ff8a3d,
      #ffe14d,
      #4ade80,
      #22d3ee,
      #5b8cff,
      #b06bff,
      #ff3b5c
    );
    background-size: 200% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    animation: rainbow-text-walk var(--rainbow-speed, 4s) linear infinite;
  }
}
@media (prefers-reduced-motion: reduce) {
  .rainbow-ring,
  .rainbow-btn:hover .rainbow-label {
    animation: none;
  }
}

Usage

Example.tsx
import { RainbowButton } from '@/components/ui/rainbow-button';

export default function Example() {
  return <RainbowButton>Get Unlimited Access</RainbowButton>;
}

Examples

Tune the lap speed, band thickness, and focus accent.

Props

PropTypeDefaultDescription
speednumber4Seconds for one full lap of the band.
borderWidthnumber2Thickness of the walking band, in px.
accentstring"#22d3ee"Focus-outline color.
classNamestring-Forwarded to the button.

The button also accepts every native <button> prop.

On this page0%