UI Components Easy
Shiny Button
A button with a glossy reflection effect that glides across on hover, creating a polished premium feel.
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: 2rem;
}
/* --- Shiny button base --- */
.shiny-btn {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.875rem 2rem;
border-radius: 10px;
font-size: 0.9375rem;
font-weight: 600;
letter-spacing: 0.01em;
cursor: pointer;
border: none;
outline: none;
color: #fff;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.shiny-btn::before {
content: "";
position: absolute;
top: 0;
left: -75%;
width: 50%;
height: 100%;
background: linear-gradient(
120deg,
transparent 0%,
rgba(255, 255, 255, 0.05) 20%,
rgba(255, 255, 255, 0.3) 45%,
rgba(255, 255, 255, 0.5) 50%,
rgba(255, 255, 255, 0.3) 55%,
rgba(255, 255, 255, 0.05) 80%,
transparent 100%
);
transition: left 0.5s ease;
pointer-events: none;
}
.shiny-btn:hover::before {
left: 125%;
}
.shiny-btn:hover {
transform: translateY(-2px);
}
.shiny-btn:active {
transform: translateY(0);
}
/* Sizes */
.shiny-btn--sm {
padding: 0.625rem 1.5rem;
font-size: 0.8125rem;
}
.shiny-btn--lg {
padding: 1.125rem 2.75rem;
font-size: 1.0625rem;
}
/* Color variants */
.shiny-btn--blue {
background: #3b82f6;
box-shadow: 0 4px 14px rgba(59, 130, 246, 0.3);
}
.shiny-btn--blue:hover {
box-shadow: 0 8px 24px rgba(59, 130, 246, 0.45);
}
.shiny-btn--purple {
background: #8b5cf6;
box-shadow: 0 4px 14px rgba(139, 92, 246, 0.3);
}
.shiny-btn--purple:hover {
box-shadow: 0 8px 24px rgba(139, 92, 246, 0.45);
}
.shiny-btn--emerald {
background: #10b981;
box-shadow: 0 4px 14px rgba(16, 185, 129, 0.3);
}
.shiny-btn--emerald:hover {
box-shadow: 0 8px 24px rgba(16, 185, 129, 0.45);
}
.shiny-btn--rose {
background: #f43f5e;
box-shadow: 0 4px 14px rgba(244, 63, 94, 0.3);
}
.shiny-btn--rose:hover {
box-shadow: 0 8px 24px rgba(244, 63, 94, 0.45);
}
.shiny-btn--dark {
background: #1e293b;
border: 1px solid #334155;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
}
.shiny-btn--dark:hover {
border-color: #475569;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}
.shiny-btn--amber {
background: #f59e0b;
color: #0a0a0a;
box-shadow: 0 4px 14px rgba(245, 158, 11, 0.3);
}
.shiny-btn--amber:hover {
box-shadow: 0 8px 24px rgba(245, 158, 11, 0.45);
}
.shiny-btn--gradient {
background: linear-gradient(135deg, #6366f1, #8b5cf6, #a855f7);
box-shadow: 0 4px 14px rgba(139, 92, 246, 0.35);
}
.shiny-btn--gradient:hover {
box-shadow: 0 8px 24px rgba(139, 92, 246, 0.5);
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
.shiny-btn::before {
transition: none;
display: none;
}
}// Shiny Button — minimal JS (CSS handles the hover shine)
(function () {
"use strict";
// No JS needed — the shine is handled entirely by CSS :hover
// This file is kept for consistency with the snippet structure
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Shiny Button</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<h2 class="demo-title">Shiny Buttons</h2>
<p class="hint">Hover over the buttons to see the shine effect</p>
<div class="button-row">
<button class="shiny-btn shiny-btn--blue">
Get Started
</button>
<button class="shiny-btn shiny-btn--purple">
Upgrade Now
</button>
<button class="shiny-btn shiny-btn--dark">
View Details
</button>
</div>
<div class="button-row">
<button class="shiny-btn shiny-btn--emerald shiny-btn--lg">
Download App
</button>
<button class="shiny-btn shiny-btn--rose shiny-btn--sm">
Subscribe
</button>
</div>
<div class="button-row">
<button class="shiny-btn shiny-btn--gradient">
Premium Access
</button>
<button class="shiny-btn shiny-btn--amber">
Go Pro
</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>import { useState, type CSSProperties, type ReactNode, type MouseEvent } from "react";
interface ShinyButtonProps {
children: ReactNode;
onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
variant?: "blue" | "purple" | "emerald" | "rose" | "dark" | "amber" | "gradient";
size?: "sm" | "md" | "lg";
className?: string;
}
const variantStyles: Record<string, CSSProperties> = {
blue: { background: "#3b82f6", color: "#fff", boxShadow: "0 4px 14px rgba(59,130,246,0.3)" },
purple: { background: "#8b5cf6", color: "#fff", boxShadow: "0 4px 14px rgba(139,92,246,0.3)" },
emerald: { background: "#10b981", color: "#fff", boxShadow: "0 4px 14px rgba(16,185,129,0.3)" },
rose: { background: "#f43f5e", color: "#fff", boxShadow: "0 4px 14px rgba(244,63,94,0.3)" },
dark: {
background: "#1e293b",
color: "#fff",
border: "1px solid #334155",
boxShadow: "0 4px 14px rgba(0,0,0,0.3)",
},
amber: { background: "#f59e0b", color: "#0a0a0a", boxShadow: "0 4px 14px rgba(245,158,11,0.3)" },
gradient: {
background: "linear-gradient(135deg, #6366f1, #8b5cf6, #a855f7)",
color: "#fff",
boxShadow: "0 4px 14px rgba(139,92,246,0.35)",
},
};
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 ShinyButton({
children,
onClick,
variant = "blue",
size = "md",
className = "",
}: ShinyButtonProps) {
const [hovered, setHovered] = useState(false);
const btnStyle: CSSProperties = {
position: "relative",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "10px",
fontWeight: 600,
letterSpacing: "0.01em",
cursor: "pointer",
border: "none",
outline: "none",
overflow: "hidden",
transition: "transform 0.2s ease, box-shadow 0.2s ease",
transform: hovered ? "translateY(-2px)" : "translateY(0)",
...sizeStyles[size],
...variantStyles[variant],
};
const shineStyle: CSSProperties = {
position: "absolute",
top: 0,
left: hovered ? "125%" : "-75%",
width: "50%",
height: "100%",
background:
"linear-gradient(120deg, transparent 0%, rgba(255,255,255,0.05) 20%, rgba(255,255,255,0.3) 45%, rgba(255,255,255,0.5) 50%, rgba(255,255,255,0.3) 55%, rgba(255,255,255,0.05) 80%, transparent 100%)",
transition: "left 0.5s ease",
pointerEvents: "none",
};
return (
<button
onClick={onClick}
className={className}
style={btnStyle}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<span style={shineStyle} />
<span style={{ position: "relative", zIndex: 1 }}>{children}</span>
</button>
);
}
export default function ShinyButtonDemo() {
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" }}>Shiny Buttons</h2>
<p style={{ color: "#525252", fontSize: "0.875rem" }}>
Hover over the buttons to see the shine effect
</p>
<div style={{ display: "flex", flexWrap: "wrap", gap: "2rem", justifyContent: "center" }}>
<ShinyButton variant="blue">Get Started</ShinyButton>
<ShinyButton variant="purple">Upgrade Now</ShinyButton>
<ShinyButton variant="dark">View Details</ShinyButton>
</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: "2rem", justifyContent: "center" }}>
<ShinyButton variant="emerald" size="lg">
Download App
</ShinyButton>
<ShinyButton variant="rose" size="sm">
Subscribe
</ShinyButton>
</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: "2rem", justifyContent: "center" }}>
<ShinyButton variant="gradient">Premium Access</ShinyButton>
<ShinyButton variant="amber">Go Pro</ShinyButton>
</div>
</div>
);
}<script setup>
import { ref } from "vue";
const hovered = ref({});
const variantStyles = {
blue: { background: "#3b82f6", color: "#fff", boxShadow: "0 4px 14px rgba(59,130,246,0.3)" },
purple: { background: "#8b5cf6", color: "#fff", boxShadow: "0 4px 14px rgba(139,92,246,0.3)" },
emerald: { background: "#10b981", color: "#fff", boxShadow: "0 4px 14px rgba(16,185,129,0.3)" },
rose: { background: "#f43f5e", color: "#fff", boxShadow: "0 4px 14px rgba(244,63,94,0.3)" },
dark: {
background: "#1e293b",
color: "#fff",
border: "1px solid #334155",
boxShadow: "0 4px 14px rgba(0,0,0,0.3)",
},
amber: { background: "#f59e0b", color: "#0a0a0a", boxShadow: "0 4px 14px rgba(245,158,11,0.3)" },
gradient: {
background: "linear-gradient(135deg, #6366f1, #8b5cf6, #a855f7)",
color: "#fff",
boxShadow: "0 4px 14px rgba(139,92,246,0.35)",
},
};
const sizeStyles = {
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" },
};
const buttons = [
{ variant: "blue", size: "md", label: "Get Started", row: 1 },
{ variant: "purple", size: "md", label: "Upgrade Now", row: 1 },
{ variant: "dark", size: "md", label: "View Details", row: 1 },
{ variant: "emerald", size: "lg", label: "Download App", row: 2 },
{ variant: "rose", size: "sm", label: "Subscribe", row: 2 },
{ variant: "gradient", size: "md", label: "Premium Access", row: 3 },
{ variant: "amber", size: "md", label: "Go Pro", row: 3 },
];
function btnStyle(variant, size, isHovered) {
return {
position: "relative",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "10px",
fontWeight: 600,
letterSpacing: "0.01em",
cursor: "pointer",
border: "none",
outline: "none",
overflow: "hidden",
transition: "transform 0.2s ease, box-shadow 0.2s ease",
transform: isHovered ? "translateY(-2px)" : "translateY(0)",
...sizeStyles[size],
...variantStyles[variant],
};
}
function shineStyle(isHovered) {
return {
position: "absolute",
top: "0",
left: isHovered ? "125%" : "-75%",
width: "50%",
height: "100%",
background:
"linear-gradient(120deg, transparent 0%, rgba(255,255,255,0.05) 20%, rgba(255,255,255,0.3) 45%, rgba(255,255,255,0.5) 50%, rgba(255,255,255,0.3) 55%, rgba(255,255,255,0.05) 80%, transparent 100%)",
transition: "left 0.5s ease",
pointerEvents: "none",
};
}
function rowButtons(row) {
return buttons.filter((b) => b.row === row);
}
</script>
<template>
<div class="wrapper">
<h2 class="title">Shiny Buttons</h2>
<p class="subtitle">Hover over the buttons to see the shine effect</p>
<div v-for="row in [1, 2, 3]" :key="row" class="btn-row">
<button
v-for="btn in rowButtons(row)"
:key="btn.label"
:style="btnStyle(btn.variant, btn.size, hovered[btn.label])"
@mouseenter="hovered[btn.label] = true"
@mouseleave="hovered[btn.label] = false"
>
<span :style="shineStyle(hovered[btn.label])"></span>
<span style="position: relative; z-index: 1;">{{ btn.label }}</span>
</button>
</div>
</div>
</template>
<style scoped>
.wrapper {
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;
}
.title {
font-size: 1.5rem;
font-weight: 700;
color: #e2e8f0;
}
.subtitle {
color: #525252;
font-size: 0.875rem;
}
.btn-row {
display: flex;
flex-wrap: wrap;
gap: 2rem;
justify-content: center;
}
</style><script>
let hovered = {};
const variantStyles = {
blue: { background: "#3b82f6", color: "#fff", boxShadow: "0 4px 14px rgba(59,130,246,0.3)" },
purple: { background: "#8b5cf6", color: "#fff", boxShadow: "0 4px 14px rgba(139,92,246,0.3)" },
emerald: { background: "#10b981", color: "#fff", boxShadow: "0 4px 14px rgba(16,185,129,0.3)" },
rose: { background: "#f43f5e", color: "#fff", boxShadow: "0 4px 14px rgba(244,63,94,0.3)" },
dark: {
background: "#1e293b",
color: "#fff",
border: "1px solid #334155",
boxShadow: "0 4px 14px rgba(0,0,0,0.3)",
},
amber: { background: "#f59e0b", color: "#0a0a0a", boxShadow: "0 4px 14px rgba(245,158,11,0.3)" },
gradient: {
background: "linear-gradient(135deg, #6366f1, #8b5cf6, #a855f7)",
color: "#fff",
boxShadow: "0 4px 14px rgba(139,92,246,0.35)",
},
};
const sizeStyles = {
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" },
};
const buttons = [
{ variant: "blue", size: "md", label: "Get Started", row: 1 },
{ variant: "purple", size: "md", label: "Upgrade Now", row: 1 },
{ variant: "dark", size: "md", label: "View Details", row: 1 },
{ variant: "emerald", size: "lg", label: "Download App", row: 2 },
{ variant: "rose", size: "sm", label: "Subscribe", row: 2 },
{ variant: "gradient", size: "md", label: "Premium Access", row: 3 },
{ variant: "amber", size: "md", label: "Go Pro", row: 3 },
];
function toStyle(obj) {
return Object.entries(obj)
.map(([k, v]) => {
const prop = k.replace(/([A-Z])/g, "-$1").toLowerCase();
return `${prop}: ${v}`;
})
.join("; ");
}
function btnStyle(variant, size, isHovered) {
const base = {
position: "relative",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "10px",
fontWeight: "600",
letterSpacing: "0.01em",
cursor: "pointer",
border: "none",
outline: "none",
overflow: "hidden",
transition: "transform 0.2s ease, box-shadow 0.2s ease",
transform: isHovered ? "translateY(-2px)" : "translateY(0)",
...sizeStyles[size],
...variantStyles[variant],
};
return toStyle(base);
}
function shineStyle(isHovered) {
return toStyle({
position: "absolute",
top: "0",
left: isHovered ? "125%" : "-75%",
width: "50%",
height: "100%",
background:
"linear-gradient(120deg, transparent 0%, rgba(255,255,255,0.05) 20%, rgba(255,255,255,0.3) 45%, rgba(255,255,255,0.5) 50%, rgba(255,255,255,0.3) 55%, rgba(255,255,255,0.05) 80%, transparent 100%)",
transition: "left 0.5s ease",
pointerEvents: "none",
});
}
</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;">Shiny Buttons</h2>
<p style="color: #525252; font-size: 0.875rem;">Hover over the buttons to see the shine effect</p>
{#each [1, 2, 3] as row}
<div style="display: flex; flex-wrap: wrap; gap: 2rem; justify-content: center;">
{#each buttons.filter(b => b.row === row) as btn (btn.label)}
<button
style={btnStyle(btn.variant, btn.size, hovered[btn.label])}
on:mouseenter={() => hovered = { ...hovered, [btn.label]: true }}
on:mouseleave={() => hovered = { ...hovered, [btn.label]: false }}
>
<span style={shineStyle(hovered[btn.label])}></span>
<span style="position: relative; z-index: 1;">{btn.label}</span>
</button>
{/each}
</div>
{/each}
</div>Shiny Button
A button that reveals a glossy light reflection on hover, sweeping diagonally across the surface for a premium, polished look.
How it works
- A
::beforepseudo-element holds a bright diagonal linear gradient - On the default state, it is positioned off the left edge of the button
- On
:hover, it transitions to the right side, creating a sweeping shine overflow: hiddenkeeps the shine confined within the button boundaries
Customization
- Shine intensity: Adjust the gradient’s white opacity values
- Speed: Change the
transition-durationon the pseudo-element - Angle: Modify the
linear-gradientangle for different sweep directions
When to use it
- Hero section CTAs
- Pricing plan action buttons
- Any button that should feel premium and interactive