Skip to main content

Raised Button

A beautiful 3D raised button with customizable colors and dynamic text contrast.

ButtonReactTailwind CSS
CSSshadcn

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"use client";

import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import type {
	ComponentPropsWithoutRef,
	ComponentPropsWithRef,
	ElementType,
} 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",
		},
	},
);

interface RaisedButtonOwnProps extends VariantProps<typeof buttonVariants> {
	color?: string;
	className?: string;
}

export type RaisedButtonProps<E extends ElementType = "button"> =
	RaisedButtonOwnProps & {
		as?: E;
		ref?: ComponentPropsWithRef<E>["ref"];
	} & Omit<ComponentPropsWithoutRef<E>, keyof RaisedButtonOwnProps | "as" | "ref">;

// React 19 ref-as-prop pattern: forwardRef is no longer needed and is
// scheduled for deprecation per the React 19 release blog. The generic
// stays on the component itself so callers get correct typing when the
// element type is overridden via the as prop.
function RaisedButton<E extends ElementType = "button">({
	as,
	ref,
	className,
	variant,
	size,
	color,
	style,
	...props
}: RaisedButtonProps<E>) {
	const Comp = (as || "button") as ElementType;

	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",
			} as React.CSSProperties;
		} 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
			ref={ref}
			className={computedClassName}
			style={{
				...(style as React.CSSProperties),
				...dynamicStyles,
			}}
			{...props}
		/>
	);
}

export { buttonVariants, RaisedButton };

Update the import paths to match your project setup.

Similar components

Button Tilt

Gradient Party Button

Max

Animated Border Button

GitHub Stars Button

Resource details

PublishedMarch 3, 2026
CategoryButton
ReactTailwind CSS