Raised Button
A beautiful 3D raised button with customizable colors and dynamic text contrast.
Category
ButtonReact
CSS
shadcn
Manual
Create a file and paste the following code into it.
raised-button.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
"use client";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/lib/cn";
import {
getContrastColor,
getLuminance,
parseColor,
} from "@/lib/utils/colorUtils";
const buttonVariants = cva(
"inline-flex items-center justify-center dark:bg-zinc-500 dark:text-white whitespace-nowrap text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 relative bg-primary text-primary-foreground hover:bg-primary/90 border border-primary/50 shadow-md before:absolute before:inset-0 before:border-t before:border-white/40 before:bg-gradient-to-b before:from-white/20 before:to-transparent cursor-pointer transition-transform duration-200 active:scale-[0.96] subpixel-antialiased gap-2",
{
variants: {
variant: {
default: "",
},
size: {
default: "h-10 px-4 py-2 rounded-xl before:rounded-xl",
sm: "h-9 rounded-lg px-3 before:rounded-xl",
lg: "h-11 rounded-lg px-8 before:rounded-lg",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
color?: string;
}
const RaisedButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, color, style = {}, ...props }, ref) => {
const Comp = "button";
const dynamicStyles = React.useMemo(() => {
if (!color) return {};
try {
const rgb = parseColor(color);
if (!rgb) return {};
const luminance = getLuminance(rgb);
const textColor = getContrastColor(luminance);
const borderOpacity = 0.5;
const hoverOpacity = 0.9;
const whiteBorderOpacity = 0.6;
const whiteGradientOpacity = 0.3;
const shadowOpacity = 0.2;
const shadowSpread = "0px";
const shadowBlur = "5px";
return {
backgroundColor: color,
color: textColor,
borderColor: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${borderOpacity})`,
"--hover-bg": `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${hoverOpacity})`,
"--border": `rgba(255, 255, 255, ${whiteBorderOpacity})`,
"--gradient": `rgba(255, 255, 255, ${whiteGradientOpacity})`,
"--shadow-color": `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${shadowOpacity})`,
boxShadow: `0 4px ${shadowBlur} ${shadowSpread} var(--shadow-color)`,
transition: "all 0.2s ease-in-out",
};
} catch (e) {
console.error("Error processing color:", e);
return {};
}
}, [color]);
const computedClassName = cn(
buttonVariants({ variant, size, className }),
color &&
"hover:bg-[color:var(--hover-bg)] before:border-[color:var(--border)] before:from-[color:var(--gradient)] hover:opacity-80 overflow-hidden",
);
return (
<Comp
className={computedClassName}
ref={ref}
style={{
...style,
...dynamicStyles,
}}
{...props}
/>
);
},
);
export { buttonVariants, RaisedButton };
Update the import paths to match your project setup.