Docs / Button

3D Glass Button

MIT License

A 3D button component plus custom hex color support. Highlight and dark shades are automatically derived from your hex input.

Preview

Installation

dependencies

npm i motion class-variance-authority clsx tailwind-merge

import

import Button3D from "@/components/3d-button";

Variants

white usage

<Button3D colorTheme="white">White</Button3D>

black usage

<Button3D colorTheme="black">Black</Button3D>

Custom Hex Color

custom color usage

<Button3D customColor="#22c55e">Custom Green</Button3D>

Sizes

sm usage

<Button3D size="sm">Small</Button3D>

md usage

<Button3D size="md">Medium</Button3D>

lg usage

<Button3D size="lg">Large</Button3D>

Source Code

components/3d-button.tsx

// components/3d-button.tsx
// full copy-ready source used in this project

type ColorTheme = "white" | "black";

interface Button3DProps {
  colorTheme?: ColorTheme;
  customColor?: string; // accepts hex like #22c55e
  size?: "sm" | "md" | "lg";
}

// defaults
const baseThemes = {
  white: { r: 244, g: 244, b: 245 },
  black: { r: 22, g: 22, b: 24 },
};

// customColor flow
// 1) parse hex -> RGB
// 2) compute dark shade by mixing with black
// 3) compute highlight by mixing with white
// 4) build gradient + glow + border from the computed shades

function buildPalette(base: { r: number; g: number; b: number }) {
  const dark = mix(base, { r: 0, g: 0, b: 0 }, 0.35);
  const top = mix(base, { r: 255, g: 255, b: 255 }, 0.22);
  const highlight = mix(base, { r: 255, g: 255, b: 255 }, 0.65);
  const innerGlow = mix(base, { r: 255, g: 255, b: 255 }, 0.45);

  return {
    backgroundImage: "linear-gradient(135deg, " + rgb(top) + " 0%, " + rgb(base) + " 56%, " + rgb(dark) + " 100%)",
    borderColor: rgba(mix(base, { r: 0, g: 0, b: 0 }, 0.5), 0.55),
    textColor: luminance(base) > 0.58 ? "#070d0d" : "#ffffff",
    innerGlow: "linear-gradient(135deg, " + rgba(innerGlow, 0.35) + " 0%, " + rgba(innerGlow, 0.1) + " 70%, transparent 100%)",
    highlight: rgba(highlight, 0.84),
  };
}

// sizes (lg is larger than md at every breakpoint)
const sizeVariants = {
  sm: "h-10 px-3 text-xs",
  md: "h-11 sm:h-12 lg:h-14 px-4 sm:px-5 lg:px-7 text-xs sm:text-sm lg:text-base",
  lg: "h-12 sm:h-14 lg:h-16 xl:h-[4.5rem] px-5 sm:px-7 lg:px-9 xl:px-10 text-sm sm:text-base lg:text-lg",
};

// usage
// <Button3D colorTheme="white">White</Button3D>
// <Button3D colorTheme="black">Black</Button3D>
// <Button3D customColor="#22c55e">Custom</Button3D>

// For the full current implementation, copy from:
// /Users/tew/Documents/Project/GitHub/mathus-port/mathus/components/3d-button.tsx