Hardware Button
A realistic physical button — a gunmetal bezel housing a recessed, illuminated glass panel with embossed text and a colored light bloom. Pure CSS; presses into its housing.
press it — the glass sinks into the housing
Usage
import { HardwareButton } from "~/components/buttons/hardware-button";<HardwareButton color="#FFD400" onClick={start}>LETS VENTURE.</HardwareButton>
How the hardware look works
It's three nested layers. The outer button is the chrome bezel: a steep gray gradient with inset bevels — a bright top edge, a dark bottom and right — so it reads as polished metal lit from above. Its padding is the bezel thickness.
Inside sits a black recessed well with its own deep inset shadow — that's the dark margin the glass appears to float in. Then the glass lens: a dark→bright→dark vertical gradient so it reads as a convex tube catching light through its middle. Two pseudo-elements sell the glass — a curved top sheen and a soft waterline reflection across the lower third — and an outer box-shadow bloom spills its color onto the surface. Press it and the whole thing drops 2px while the glass shadow deepens, sinking into the housing. Everything derives from one --hw-color.
/* 1. Chrome bezel — metal gradient, beveled insets, outer bloom */.hw-btn {--hw-color: #ffd400;padding: 12px; /* the bezel thickness */border-radius: 30px;background: linear-gradient(156deg, #cfcfd6, #8c8c93 8%, #4a4a50 28%, #232327 60%, #101013);box-shadow:0 30px 55px -16px rgba(0,0,0,.9),0 16px 70px -6px color-mix(in srgb, var(--hw-color) 45%, transparent), /* glow */inset 0 2px 1px rgba(255,255,255,.7), /* bright top edge */inset -2px 0 3px rgba(0,0,0,.6),inset 0 -4px 6px rgba(0,0,0,.75);}/* 2. Black recessed well — the dark margin the glass floats in */.hw-btn__well {padding: 13px;border-radius: 20px;background: linear-gradient(180deg, #050506, #121214);box-shadow: inset 0 4px 9px rgba(0,0,0,.9), inset 0 0 0 1px rgba(0,0,0,.9);}/* 3. Convex glass — dark→bright→dark = a lit tube */.hw-btn__glass {border-radius: 13px;padding: 20px 40px;background: linear-gradient(180deg,color-mix(in srgb,var(--hw-color) 72%,#000) 0%, var(--hw-color) 26%,color-mix(in srgb,var(--hw-color) 52%,#fff) 52%, var(--hw-color) 74%,color-mix(in srgb,var(--hw-color) 72%,#000) 100%);box-shadow: 0 0 26px color-mix(in srgb,var(--hw-color) 70%,transparent),inset 0 1px 0 rgba(255,255,255,.55);}.hw-btn__glass::before { /* curved top sheen */content:""; position:absolute; inset:1px 1px auto 1px; height:50%;border-radius: 12px 12px 50% 50% / 12px 12px 26px 26px;background: linear-gradient(180deg, rgba(255,255,255,.6), rgba(255,255,255,.05));}.hw-btn__glass::after { /* convex waterline reflection */content:""; position:absolute; left:7%; right:7%; bottom:13%; height:18%;border-radius:100%; filter:blur(1.5px);background: linear-gradient(180deg, rgba(255,255,255,.5), transparent);}/* 4. Press it into the housing */.hw-btn:active { transform: translateY(2px); }.hw-btn:active .hw-btn__glass { box-shadow: inset 0 6px 12px rgba(0,0,0,.6); }
Props
| prop | type | default | description |
|---|---|---|---|
| color | string | "#FFD400" | Glass + glow color; the whole panel derives from it. |
| arrow | boolean | true | Show the trailing ↗ arrow. |
| children | ReactNode | "LETS VENTURE." | Button label. |
| labelColor | string | "#171200" | Label / arrow color. |
| disabled | boolean | false | Dims and desaturates; cancels press. |
| …props | ButtonHTMLAttributes | — | Any native button prop (onClick, type, aria-*). |
Source
download .zipThe full implementation, across 2 files. Copy it in or grab the zip. It leans on a cn() class helper and the theme tokens (--primary, --border, …).
import { ArrowUpRight } from "lucide-react";import { cn } from "~/lib/utils";export interface HardwareButtonPropsextends React.ButtonHTMLAttributes<HTMLButtonElement> {/** Illuminated glass + glow color. The whole panel derives from it. Default #FFD400. */color?: string;/** Label / arrow color. Default a near-black for bright panels. */labelColor?: string;/** Show the trailing ↗ arrow. Default true. */arrow?: boolean;}/*** A realistic physical button: a gunmetal bezel housing a recessed, illuminated* glass panel. The look is pure CSS — a metal gradient frame with beveled inset* highlights, a radial-gradient "lit glass" insert, a glossy sheen pseudo-* element, embossed text, and an outer colored bloom. Press it and it sinks* into the housing. Recolor the whole thing via `color`.*/export function HardwareButton({color,labelColor,arrow = true,children = "LETS VENTURE.",className,type = "button",style,...props}: HardwareButtonProps) {const vars = { ...style } as Record<string, string>;if (color) vars["--hw-color"] = color;if (labelColor) vars["--hw-label"] = labelColor;return (<buttontype={type}className={cn("hw-btn", className)}style={vars as React.CSSProperties}{...props}><span className="hw-btn__well"><span className="hw-btn__glass"><span className="hw-btn__label">{children}{arrow ? (<ArrowUpRight className="size-[1.1em]" strokeWidth={2.5} />) : null}</span></span></span></button>);}
/* ─── Hardware button (illuminated glass + chrome bezel) ───────────── *//* Three nested layers: polished metal bezel → black recessed well →convex lit-glass lens floating inside it. */.hw-btn {--hw-color: #ffd400;--hw-light: color-mix(in srgb, var(--hw-color) 52%, #fff);--hw-dark: color-mix(in srgb, var(--hw-color) 72%, #000);--hw-label: #171200;position: relative;display: inline-flex;cursor: pointer;border: none;padding: 17px;border-radius: 36px;/* polished chrome: bright top edge, dark belly, faint bottom rim-light */background: linear-gradient(180deg,#ececf2 0%,#b6b6bf 5%,#6e6e77 15%,#34343a 40%,#1a1a1e 68%,#2a2a30 88%,#131316 100%);box-shadow:0 42px 70px -18px rgba(0, 0, 0, 0.95),0 10px 20px -6px rgba(0, 0, 0, 0.8),0 24px 95px -10px color-mix(in srgb, var(--hw-color) 55%, transparent),inset 0 3px 2px -1px rgba(255, 255, 255, 0.95),inset 0 1px 0 rgba(255, 255, 255, 0.55),inset 0 0 0 1px rgba(255, 255, 255, 0.07),inset 3px 0 4px rgba(255, 255, 255, 0.16),inset -3px 0 4px rgba(0, 0, 0, 0.65),inset 0 -5px 7px rgba(0, 0, 0, 0.85);transition:transform 0.15s cubic-bezier(0.2, 0.7, 0.2, 1),box-shadow 0.2s ease,filter 0.2s ease;}/* diagonal chrome streak across the bezel (sits behind the well) */.hw-btn::before {content: "";position: absolute;inset: 0;z-index: 0;border-radius: 36px;background: linear-gradient(125deg,rgba(255, 255, 255, 0.28) 0%,rgba(255, 255, 255, 0) 24%,rgba(255, 255, 255, 0) 78%,rgba(255, 255, 255, 0.06) 100%);pointer-events: none;}/* black recessed well — gives the dark margin the glass floats inside */.hw-btn__well {position: relative;z-index: 1;display: flex;width: 100%;padding: 15px;border-radius: 24px;background: linear-gradient(180deg, #050506, #121214);box-shadow:inset 0 5px 11px rgba(0, 0, 0, 0.92),inset 0 -1px 1px rgba(255, 255, 255, 0.06),inset 0 0 0 1px rgba(0, 0, 0, 0.9);}/* convex glass lens */.hw-btn__glass {position: relative;overflow: hidden;flex: 1;display: inline-flex;align-items: center;justify-content: center;padding: 23px 46px;border-radius: 15px;/* dark-bright-dark vertical = a convex tube catching light through the middle */background:radial-gradient(120% 85% at 50% 118%,color-mix(in srgb, var(--hw-color) 32%, #fff) 0%,transparent 58%),linear-gradient(180deg,var(--hw-dark) 0%,var(--hw-color) 26%,var(--hw-light) 52%,var(--hw-color) 74%,var(--hw-dark) 100%);box-shadow:0 0 26px color-mix(in srgb, var(--hw-color) 70%, transparent),inset 0 1px 0 rgba(255, 255, 255, 0.55),inset 0 -1px 2px rgba(0, 0, 0, 0.45),inset 0 0 0 1px rgba(0, 0, 0, 0.3);}/* top specular sheen, curved at its lower edge */.hw-btn__glass::before {content: "";position: absolute;inset: 1px 1px auto 1px;height: 50%;border-radius: 12px 12px 50% 50% / 12px 12px 26px 26px;background: linear-gradient(180deg,rgba(255, 255, 255, 0.6),rgba(255, 255, 255, 0.05));pointer-events: none;}/* convex "waterline" reflection across the lower third */.hw-btn__glass::after {content: "";position: absolute;left: 7%;right: 7%;bottom: 13%;height: 18%;border-radius: 100%;background: linear-gradient(180deg,rgba(255, 255, 255, 0.5),rgba(255, 255, 255, 0));filter: blur(1.5px);pointer-events: none;}.hw-btn__label {position: relative;z-index: 1;display: inline-flex;align-items: center;gap: 0.5em;font-family: var(--font-sans);font-weight: 700;font-size: 1.6rem;letter-spacing: 0.06em;white-space: nowrap;color: var(--hw-label);text-shadow: 0 1px 0 rgba(255, 255, 255, 0.28);}.hw-btn:hover {filter: brightness(1.05);box-shadow:0 34px 60px -16px rgba(0, 0, 0, 0.9),0 20px 90px -4px color-mix(in srgb, var(--hw-color) 60%, transparent),inset 0 2px 1px rgba(255, 255, 255, 0.75),inset 0 0 0 1px rgba(255, 255, 255, 0.07),inset 2px 0 3px rgba(255, 255, 255, 0.16),inset -2px 0 3px rgba(0, 0, 0, 0.6),inset 0 -4px 6px rgba(0, 0, 0, 0.75);}.hw-btn:active {transform: translateY(2px);filter: brightness(0.97);}.hw-btn:active .hw-btn__glass {box-shadow:0 0 14px color-mix(in srgb, var(--hw-color) 55%, transparent),inset 0 6px 12px rgba(0, 0, 0, 0.6),inset 0 0 0 1px rgba(0, 0, 0, 0.4);}.hw-btn:focus-visible {outline: 3px solid color-mix(in srgb, var(--hw-color) 60%, transparent);outline-offset: 3px;}.hw-btn:disabled {cursor: not-allowed;filter: grayscale(0.6) brightness(0.6);}@media (prefers-reduced-motion: reduce) {.hw-btn {transition: none;}}