Button
A momentary push button that rests embossed, depresses to flush while held, and springs back with a machined overshoot.
@emboss/button
Installation
pnpm dlx shadcn@latest add @emboss/buttonUsage
import { Button } from "@/components/ui/button";
export function Example() {
return (
<Button variant="accent" size="lg">
Engage
</Button>
);
}Examples
Sizes
Three control heights plus square icon footprints. The lg size meets the 44px touch minimum.
Show codeHide code
import { Settings } from "lucide-react";
import { Button } from "@/components/ui/button";
export default function ButtonSizes() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
<Button size="icon" aria-label="Settings">
<Settings />
</Button>
</div>
);
}With icon
Icons inherit a 16px footprint unless sized explicitly.
Show codeHide code
import { ArrowDownToLine, Power } from "lucide-react";
import { Button } from "@/components/ui/button";
export default function ButtonWithIcon() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Button variant="accent">
<Power /> Engage
</Button>
<Button>
<ArrowDownToLine /> Download
</Button>
</div>
);
}Composed onto a link
The render prop hands the button's behavior and depth to any element.
Show codeHide code
import { buttonVariants } from "@/components/ui/button";
export default function ButtonAsLink() {
return (
<a href="/docs" className={buttonVariants({ variant: "accent" })}>
Read the docs
</a>
);
}API reference
Button
Extends the native button element. All standard button props are supported.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "accent" | "danger" | "ghost" | "link" | "default" | Visual role. Accent and danger carry color in addition to depth; ghost rests flat and presses into the surface; link renders as an inline link. |
| size | "sm" | "md" | "lg" | "icon" | "icon-sm" | "icon-lg" | "md" | Control height. The icon sizes render a square footprint for icon-only buttons. |
| render | ReactElement | (props, state) => ReactElement | — | Swaps the rendered element while keeping button behavior — the element must stay a real button. For links styled as buttons, put buttonVariants classes on the anchor instead. |
| disabled | boolean | false | Disables interaction and dims the control. |
Keyboard
| Keys | Action |
|---|---|
| Space | Activates and holds the press. |
| Enter | Activates the button. |
Source
View source — button.tsxHide source — button.tsx
import { Button as BaseButton } from "@base-ui/react/button";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap focus-ring transition-[translate,box-shadow,background-color,color] duration-(--duration-release) ease-(--ease-release) select-none active:translate-[calc(var(--away-x)*1px)_calc(var(--away-y)*1px)] active:duration-(--duration-press) active:ease-(--ease-press) disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
default:
"bg-surface-2 text-ink shadow-emboss-1 hover:shadow-emboss-2 active:shadow-flush",
accent:
"bg-accent text-accent-fg shadow-emboss-1 hover:shadow-emboss-2 active:bg-accent-deep active:shadow-flush",
danger:
"bg-danger text-danger-fg shadow-emboss-1 hover:shadow-emboss-2 active:shadow-flush",
ghost:
"text-ink hover:bg-surface-1 hover:shadow-flush active:bg-well active:shadow-deboss-1",
link: "h-auto px-0 text-accent-ink underline-offset-4 hover:underline active:translate-none",
},
size: {
sm: "h-(--control-sm) px-3",
md: "h-(--control-md) px-4",
lg: "h-(--control-lg) px-5 text-base",
icon: "size-(--control-md)",
"icon-sm": "size-(--control-sm)",
"icon-lg": "size-(--control-lg)",
},
},
defaultVariants: {
variant: "default",
size: "md",
},
},
);
export type ButtonProps = React.ComponentProps<typeof BaseButton> &
VariantProps<typeof buttonVariants>;
/**
* A momentary push button. Rests embossed, lifts on hover, and physically
* depresses to flush while held — pointer or Space alike — then springs
* back with a machined overshoot. Server-safe: the press physics are pure
* CSS. For links styled as buttons, put `buttonVariants` classes on the
* anchor so it keeps link semantics.
*
* @example
* <Button variant="accent" size="lg">Engage</Button>
* @example
* // A link dressed as a button keeps link semantics:
* <a href="/docs" className={buttonVariants({ variant: "accent" })}>Docs</a>
*/
function Button({ className, variant, size, ...props }: ButtonProps) {
return (
<BaseButton
data-slot="button"
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
);
}
export { Button, buttonVariants };