Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Buttons

Pro

Depth Button for React

A button that stands raised on an edge of its own color and drops onto it when pressed. For React with Base UI, when a button should feel like a key.

Framework
UI library

The button sits a few pixels up on a darker edge of its own color. While it's pressed, the face drops onto the edge, and it comes back up on release, at once, the way a key gives under a finger.

Use it where pressing should feel physical: games, playful products, a keypad, a row of formatting toggles. In a dense, quiet interface a flat Button fits better; for press feedback that doesn't change the button's shape, use the Ripple Button. It wraps the Button, so variant and size work as usual, and each variant gets an edge in its own shade. It's CSS only: the edge is a shadow under the face, and the press moves the face down by the edge's height, with no transition and no JavaScript, so there's nothing for reduced motion to take away.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { DepthButton } from "@/components/ui/depth-button";
<DepthButton>Start game</DepthButton>

Demos

Intensity

intensity sets how high the button stands and how far it drops: subtle is 3px, medium 5px and strong 7px, with medium by default. A subtle edge suits small buttons and toolbars; a strong one suits a big, single action.

Preview width

Pressed

pressed holds the button down, which makes it the on state of a toggle. These formatting buttons keep pressed and aria-pressed in step, so the state shows and is announced. Pressing a held button still activates it.

Preview width

Accessibility

The edge and the drop are visual. The button is announced and activated as any Button. pressed only changes the look: for a toggle, also pass aria-pressed, as the Pressed demo does, so screen readers say whether it's on.

Enter
Activate the button.
Space
Press the button down and activate it on release.

FAQ

How do I make it more or less raised?

Set intensity to subtle (3px), medium (5px, the default) or strong (7px). The button drops the same distance when pressed.

Can I keep it pressed, like a toggle?

Yes. Pass pressed and it stays down until you set it back. Pass aria-pressed with the same value so the state is announced, as in the Pressed demo.

Does it press down from the keyboard?

With Space, yes: the browser shows a button as pressed while Space is held, as it does for a pointer. Enter activates the button at once, with no held state to show.

Why does the face drop at once, without a transition?

Because the edge is a shadow, and a shadow can't follow a moving face without animating it on every frame. Dropping at once keeps the edge and the face in step, and it's how a real key feels: it gives as soon as you press it.

How is the edge colored, and can I change it?

Each variant gets a shade of its own color: the primary color mixed toward the foreground for the default, the border for outline, and so on, in both themes. To pick your own, set the --depth-button-edge variable in className, such as [--depth-button-edge:var(--ring)].

Does the edge take up space?

No. It's a shadow under the button, so it doesn't change the layout. Leave a few pixels below the button so the edge isn't covered by what comes next.

What happens with reduced motion?

Nothing changes: the face already drops at once, with no transition, so the press stays visible without any motion to reduce.

API Reference

DepthButton

PropType
focusableWhenDisabled?boolean

Whether the button should be focusable when disabled.

Default
false
intensity?"subtle" | "medium" | "strong"

How high the button stands on its edge, and so how far it drops when pressed: subtle is 3px, medium 5px, strong 7px.

Type
"subtle" | "medium" | "strong"
Default
"medium"
nativeButton?boolean

Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).

Default
true
pressed?boolean

Hold the button down on its edge, as the on state of a toggle. Add aria-pressed too when it is one, so the state is announced.

Default
false
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

AttributeType
data-down?string

Present when pressed is set.

CSS variableType
--depth-button-depthstring

How high the button stands on its edge (5px).

--depth-button-edgestring

The edge's color (a shade of the variant's).

--depth-button-lightstring

The light line along the face's top.

All 35 buttons