3D✨ Premium

3D Button Press

A real violet button sitting on a darker 8 px solid edge, like a key: it rises 2 px on hover (10 px edge) and sinks 6 px when pressed (edge down to 2 px, tighter shadow), in 0.15 s — the bottom of the edge never moves, the key sinks into its base. CSS only, no JavaScript, motionless when the user reduces motion.

CSS3DButton

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. 3D has 50 effects, including 8 free. Explore the category →

3 usage examples

The quiz buzzer — players' phones during a bar's quiz night — 3D Button Press example 1

① The quiz buzzer — players' phones during a bar's quiz night

WhenOn team "Les Gratins"'s phone, question 12: a big "BUZZ!" button fills the middle of the screen, and "Buzzed first!" shows below when pressed. The scene presses the button by itself in a loop, like an eager player.
WhyA buzzer must feel like a key you slam: the edge vanishing under the finger says it without sound or vibration, and the base that never moves gives an object sitting on the table.
SettingsRoot without background, placed inside the phone; button enlarged (padding: 22px 46px, 26 px text) and recolored raspberry red #fb7185 → #e11d48, edge #9f1239 in all three states; same 8, 10 and 2 px edge, same 2 and 6 px travel.
"Stop requested" button — rider app of an on-demand minibus — 3D Button Press example 2

② "Stop requested" button — rider app of an on-demand minibus

WhenDuring the ride, the screen shows the line (Train station, Les Tilleuls, Vaux town hall…) and, on the right, the next stop with a red "Stop requested" button; once pressed, "The driver has been told" appears below.
WhyThe relief echoes the big physical button on buses: riders recognize the gesture, and the key sinking into its base confirms the press before the message even shows.
SettingsButton recolored red #f87171 → #dc2626, edge #991b1b, 16 px corners, 20 px text; blurred shadow lightened (rgba(0, 0, 0, .25)) for the app's light background; confirmation message shown on click.
"Light up the tree!" — a town hall's lighting ceremony page — 3D Button Press example 3

③ "Light up the tree!" — a town hall's lighting ceremony page

WhenOn the night of the lighting, the live page shows the drawn tree, unlit, a "3, 2, 1" countdown next to it and a big green "Light up" button below; on press, the garlands and the star light up.
WhyOne button, one solemn gesture: the 6 px downward travel and the edge being crushed give weight to the click, where a flat button would feel trivial.
SettingsGreen button #4ade80 → #16a34a, edge #166534, 19 px text; the scenery (CSS tree, garlands as dots) lights up on click; the scene plays the countdown then the press in a loop, and stops at the first real click.

How it works

The root .fx-0006-root (#0a0a0f background, at least 300 px tall, --radius-lg variable defined on it) centers a 280 × 200 px .demo-3d frame, then .button-3d-container, which carries one thing only: perspective: 500px. Inside sits a real <button class="button-3d">: 16 px × 40 px padding, bold 16 px white text, vertical gradient #6366f1 → #4f46e5, 0.75 rem corners. The edge is not an element: it is a shadow with no blur, box-shadow: #3730a3 0 8px 0, offset 8 px down, which draws a solid base under the button; a second, blurred shadow (rgba(0, 0, 0, .4) 0 15px 20px) sets it on the background.

Three states, one 0.15 s transition on everything. At rest, translateZ(20px) inside the 500 px perspective shows the button at 104% (156 × 52 px measured). On hover it rises 2 px (translateY(-2px)) and the edge grows to 10 px. When pressed (:active) it drops 6 px and moves back to translateZ(10px), the edge shrinks to 2 px and the blurred shadow tightens (5 px / 10 px). Do the math: at rest, the bottom of the edge is 0 + 8 = 8 px below the button; on hover −2 + 10 = 8 px; pressed 6 + 2 = 8 px. The base stays put while the key rises and then sinks into it. Measured in Chromium 151: 1.1% of pixels change on hover, 2.9% when pressed; Firefox 153: 0.9% and 2.7%.

Why a shadow rather than a pseudo-element or a border: it follows the button's corners, takes no room in the layout (neighbors don't move when the edge changes thickness) and transitions with everything else. The perspective adds a nuance: the button looks slightly closer at rest (×1.04) than pressed (×1.02). On the keyboard it is an ordinary button: Tab, Enter, Space, and a 3 px #a5b4fc :focus-visible ring offset by 4 px. In Chromium, holding Space presses the key like the mouse does (3.9% of pixels changed); Firefox does not apply :active from the keyboard (0% measured): the click still fires, the key just doesn't sink.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, a block scoped to .fx-0006-root keeps translateZ(20px) on hover and press: the key neither rises nor sinks. The edge thickness (8, 10, then 2 px) and the shadow still mark both states. Measured in Chromium and Firefox: same transform at rest, on hover and pressed; 0.6% of pixels change on hover and 1.2% when pressed (versus 1.1% and 2.9% without the preference).
  • Keyboard: a real <button>, reached with Tab and triggered by Enter or Space. The #a5b4fc focus ring measures 9.9:1 on the #0a0a0f background. In Safari, Tab only stops on buttons when the "Press Tab to highlight each item" option is on (otherwise Option + Tab): that is the browser's setting, measured in WebKit, not a flaw of the effect.
  • Contrast: bold 16 px white text at 4.47:1 at the top of the gradient (#6366f1) and 6.3:1 at the bottom (#4f46e5). The top is just under the 4.5:1 threshold: for text that matters, darken the top color (#5b5bd6 gives 5.4:1).
  • Touch: with a finger, the browser gives the button its hover state (measured with Chromium touch emulation and in Firefox); the press follows :active, for as long as the finger is down. Not checked on a real iPhone, where Safari shows :active only under some conditions.

Browser compatibility

CSS only (box-shadow, transform inside a perspective parent, :focus-visible), no dependencies. Measured in Chromium 151, Firefox 153 and WebKit 26.5: same render at rest, on hover and pressed (1% differences from text smoothing), same stillness under reduced motion, 0 errors. The button does not stack 3D planes: Playwright's WebKit, which flattens preserve-3d scenes, renders it like Chromium.

Chrome 86+✓ Full
Firefox 85+✓ Full (Space does not press the key from the keyboard)
Safari 15.4+✓ Full (render measured in WebKit 26; not checked on a real Safari)
Edge 86+✓ Full
Mobile iOS✓ Finger press not checked on a real iPhone
Android Chrome✓ Full

Without :focus-visible (old browser), the browser's default focus ring shows instead of the violet outline. Without transitions, the three states switch at once: the key still sinks.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0006-root">
  <div class="demo-3d">
    <div class="button-3d-container">   <!-- perspective: 500px -->
      <button class="button-3d">Press Me</button>
    </div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Key color (CSS — background of .button-3d) #6366f1 → #4f46e5 Vertical gradient, light at the top: it mimics light falling on the top of the key. Keep the edge clearly darker than the bottom of the gradient, or the base blends into the button.
Edge color (CSS — first color of the three box-shadow) #3730a3 Change it in all three rules (rest, :hover, :active), or the edge changes color when pressed.
Edge thickness and travel (CSS — 8px, 10px, 2px and translateY) 8 px at rest; −2 px / 10 px on hover; +6 px / 2 px pressed Keep offset + edge equal in all three states (here 8 px): that is what keeps the bottom of the base still. For a thicker key: 12 px at rest, −2 / 14 on hover, +9 / 3 pressed.
Speed (CSS — transition: .15s) 0.15 s Under 0.1 s the press feels dry; above 0.25 s the key feels mushy. The transition covers everything: color, shadow and position move together.
Depth (CSS — perspective: 500px and translateZ) 500 px; 20 px at rest, 10 px pressed Moving back from 20 to 10 px makes the key slightly smaller when pressed (×1.04 → ×1.02). Remove translateZ from all three states for a perfectly flat button: the vertical travel is enough.
Size (CSS — padding and font-size) 16 px × 40 px; 16 px The relief holds at any size: the scenes enlarge it to 22 px × 46 px and 26 px without touching anything else.

FAQ

Same principle: a button on an edge drawn by a shadow with no blur, rising on hover and sinking when pressed. V2 is smaller ("Click Me" label, #8b5cf6 → #6366f1 gradient, 10 px edge) and adds a 2° tilt on hover and 5° when pressed, almost invisible: 1.5% of pixels change when pressed versus 2.9% here. This one keeps the bottom of the edge still, which gives the clearest feel of a key sinking into its base.
A shadow with no blur, offset downward, draws a solid base that follows the button's corners, with no extra element and no room taken in the layout. It transitions like the rest: when it goes from 8 to 2 px while the button drops 6 px, you see the key sink. A bottom border would move the text and the neighbors; a pseudo-element would need stacking to manage.
Yes for the action: it is a real <button>, Tab reaches it, Enter and Space trigger it, and a violet ring shows the focus. The visible press follows :active: from the keyboard, Chromium shows it while Space is held, Firefox does not. With a finger, Chromium and Firefox apply the hover state on touch; on iPhone it has not been checked.