# Push Button

> A chunky, tactile 3D button that lifts a little on hover and presses down onto its base when clicked, like a real key.

Category: Buttons · Free · Updated 2026-10-30 · Live demo: https://www.motionkitui.com/components/push-button

## When to use it

Playful brands, games, onboarding and kids' products, and any one big button that deserves to feel physical.

## How it works

The button is two layers: a base in a darker mix of the face colour, made with color-mix so it works for any colour, and the face lifted above it by the depth. Hover raises the face two more pixels, and pressing drops it almost onto the base with a faster transition, which is what makes the press feel crisp. It's pure CSS, so it responds on the very first frame.

## Installation

```bash
npx shadcn@latest add https://www.motionkitui.com/r/push-button.json
```

Or copy `components/motionkit/push-button.tsx` below. Requires react and Tailwind CSS v4. Free for personal projects; commercial use needs MotionKit Plus (https://www.motionkitui.com/pricing).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `string` | `"#0E7C86"` | Face colour; the sides are a darker mix of it. |
| `depth` | `number` | `6` | How tall the button stands, in pixels. |
| `...props` | `ButtonHTMLAttributes` |  | Anything a <button> accepts. |

## Accessibility

- **Keyboard: Pass.** A native <button> with a focus ring set clear of its base.
- **Screen readers: Pass.** Announced with its label; the base is decoration.
- **Reduced motion: Pass.** With reduced motion on, it changes position instantly rather than travelling.
- **Colour contrast: Pass.** White text on the default face measures 4.9:1. Check your own colour if you change it.

## Source

```tsx
import type { CSSProperties, ComponentProps } from "react";

type PushButtonProps = ComponentProps<"button"> & {
  /** Colour of the button's face. The sides are a darker shade of it. */
  color?: string;
  /** How tall the button stands, in pixels. */
  depth?: number;
};

/**
 * A chunky, tactile 3D button: it lifts a little on hover and presses down onto its base when clicked,
 * like a real key. Pure CSS, so it responds instantly.
 */
export function PushButton({ color = "#0E7C86", depth = 6, className = "", children, style, ...props }: PushButtonProps) {
  return (
    <button
      {...props}
      style={{ "--face": color, "--depth": `${depth}px`, ...style } as CSSProperties}
      className={`group relative inline-flex rounded-xl pt-[var(--depth)] outline-none focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[#5FAEB4] disabled:pointer-events-none disabled:opacity-50 ${className}`}
    >
      {/* the base and sides: the face colour, darkened */}
      <span aria-hidden className="absolute inset-x-0 bottom-0 top-[var(--depth)] rounded-xl bg-[color-mix(in_oklab,var(--face)_55%,black)]" />
      <span
        className="relative inline-flex -translate-y-[var(--depth)] items-center gap-2 rounded-xl bg-[var(--face)] px-6 py-3 text-sm font-semibold text-white shadow-[inset_0_1px_0_rgb(255_255_255/0.25)] transition-transform duration-150 ease-out group-hover:-translate-y-[calc(var(--depth)+2px)] group-active:-translate-y-[calc(var(--depth)*0.25)] group-active:duration-75 motion-reduce:transition-none"
      >
        {children}
      </span>
    </button>
  );
}
```
