Web InnoventixFreeCode

Button Shine Hover Effect

Original · free

Buttons with a diagonal shine sweep on hover.

byWeb InnoventixReact + Tailwind
hoverbuttonshine
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/hover-button-shine.json
hover-button-shine.tsx
"use client";

import { useState, type ReactNode } from "react";

type ShineButtonProps = {
  children: ReactNode;
  variant?: "indigo" | "emerald" | "rose" | "slate";
  size?: "md" | "lg";
  icon?: ReactNode;
  onClick?: () => void;
};

const VARIANTS: Record<
  NonNullable<ShineButtonProps["variant"]>,
  string
> = {
  indigo:
    "bg-indigo-600 text-white hover:bg-indigo-500 focus-visible:ring-indigo-400 dark:bg-indigo-500 dark:hover:bg-indigo-400",
  emerald:
    "bg-emerald-600 text-white hover:bg-emerald-500 focus-visible:ring-emerald-400 dark:bg-emerald-500 dark:hover:bg-emerald-400",
  rose:
    "bg-rose-600 text-white hover:bg-rose-500 focus-visible:ring-rose-400 dark:bg-rose-500 dark:hover:bg-rose-400",
  slate:
    "bg-slate-900 text-white hover:bg-slate-800 focus-visible:ring-slate-400 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white dark:focus-visible:ring-slate-300",
};

function ShineButton({
  children,
  variant = "indigo",
  size = "md",
  icon,
  onClick,
}: ShineButtonProps) {
  const sizing =
    size === "lg" ? "px-7 py-3.5 text-base" : "px-5 py-2.5 text-sm";

  return (
    <button
      type="button"
      onClick={onClick}
      className={[
        "hbs-btn group relative inline-flex select-none items-center justify-center gap-2 overflow-hidden rounded-xl font-semibold tracking-tight",
        "shadow-sm transition-[transform,background-color,box-shadow] duration-200 ease-out",
        "hover:-translate-y-0.5 hover:shadow-lg active:translate-y-0 active:shadow-sm",
        "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
        "focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-950",
        sizing,
        VARIANTS[variant],
      ].join(" ")}
    >
      {/* diagonal shine sweep */}
      <span
        aria-hidden="true"
        className="pointer-events-none absolute inset-0 z-0"
      >
        <span className="hbs-shine absolute inset-y-0 -left-1/3 w-1/3 -skew-x-12 bg-gradient-to-r from-transparent via-white/55 to-transparent dark:via-white/40" />
      </span>

      {icon ? (
        <span className="relative z-10 inline-flex shrink-0" aria-hidden="true">
          {icon}
        </span>
      ) : null}
      <span className="relative z-10">{children}</span>
    </button>
  );
}

export default function HoverButtonShine() {
  const [copied, setCopied] = useState(false);

  const handleCopy = () => {
    setCopied(true);
    window.setTimeout(() => setCopied(false), 1600);
  };

  return (
    <section className="relative w-full bg-white px-6 py-20 dark:bg-slate-950 sm:px-8 sm:py-24">
      <style>{`
        @keyframes hbs-sweep {
          0%   { transform: translateX(0) skewX(-12deg); }
          100% { transform: translateX(520%) skewX(-12deg); }
        }
        .hbs-btn .hbs-shine {
          opacity: 0;
          transform: translateX(0) skewX(-12deg);
        }
        .hbs-btn:hover .hbs-shine,
        .hbs-btn:focus-visible .hbs-shine {
          opacity: 1;
          animation: hbs-sweep 0.85s ease-out;
        }
        @media (prefers-reduced-motion: reduce) {
          .hbs-btn { transition: background-color 0.2s ease; }
          .hbs-btn:hover { transform: none; }
          .hbs-btn .hbs-shine { display: none; }
        }
      `}</style>

      <div className="mx-auto max-w-3xl">
        <div className="mx-auto mb-12 max-w-xl text-center">
          <span className="inline-flex items-center rounded-full border border-slate-200 bg-slate-50 px-3 py-1 text-xs font-medium uppercase tracking-widest text-slate-600 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-400">
            Hover interaction
          </span>
          <h2 className="mt-5 text-3xl font-bold tracking-tight text-slate-900 dark:text-white sm:text-4xl">
            Shine-sweep buttons
          </h2>
          <p className="mt-4 text-base leading-relaxed text-slate-600 dark:text-slate-400">
            Hover or tab to any button and a diagonal light streak sweeps across
            it once. Keyboard focus triggers the same shine, and it respects
            reduced-motion.
          </p>
        </div>

        <div className="flex flex-wrap items-center justify-center gap-4">
          <ShineButton variant="indigo" size="lg" onClick={handleCopy}>
            {copied ? "Copied to clipboard" : "Get started free"}
          </ShineButton>

          <ShineButton
            variant="slate"
            size="lg"
            icon={
              <svg
                viewBox="0 0 20 20"
                className="h-4 w-4"
                fill="currentColor"
              >
                <path d="M10 1.5a8.5 8.5 0 0 0-2.687 16.566c.425.078.58-.184.58-.41 0-.202-.007-.737-.011-1.447-2.365.514-2.865-1.14-2.865-1.14-.387-.983-.944-1.245-.944-1.245-.772-.527.058-.516.058-.516.854.06 1.303.877 1.303.877.759 1.3 1.99.925 2.475.707.077-.55.297-.925.54-1.138-1.888-.215-3.873-.944-3.873-4.2 0-.928.331-1.687.875-2.282-.088-.215-.38-1.08.083-2.25 0 0 .713-.229 2.337.871A8.13 8.13 0 0 1 10 5.84c.722.003 1.45.098 2.13.287 1.622-1.1 2.334-.871 2.334-.871.464 1.17.172 2.035.084 2.25.545.595.874 1.354.874 2.282 0 3.265-1.988 3.983-3.882 4.193.305.263.577.783.577 1.578 0 1.139-.01 2.058-.01 2.337 0 .228.153.492.585.409A8.502 8.502 0 0 0 10 1.5Z" />
              </svg>
            }
          >
            Continue with GitHub
          </ShineButton>

          <ShineButton variant="emerald">Upgrade to Pro</ShineButton>

          <ShineButton
            variant="rose"
            icon={
              <svg
                viewBox="0 0 20 20"
                className="h-4 w-4"
                fill="currentColor"
              >
                <path d="M9.653 16.915l-.005-.003-.019-.01a20.759 20.759 0 0 1-1.162-.682 22.045 22.045 0 0 1-2.582-1.9C4.045 12.733 2 10.352 2 7.5a4.5 4.5 0 0 1 8-2.828A4.5 4.5 0 0 1 18 7.5c0 2.852-2.044 5.233-3.885 6.82a22.049 22.049 0 0 1-3.744 2.582l-.019.01-.005.003h-.002a.739.739 0 0 1-.69.001l-.002-.001Z" />
              </svg>
            }
          >
            Add to favorites
          </ShineButton>
        </div>

        <p className="mt-10 text-center text-sm text-slate-500 dark:text-slate-500">
          Tab through the buttons with your keyboard to see the focus ring and
          shine fire together.
        </p>
      </div>
    </section>
  );
}

Dependencies

None (React + Tailwind only).

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →