Quick implementation
.press-button-3d {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 1.75rem;
background: linear-gradient(135deg, oklch(0.72 0.19 265), oklch(0.62 0.19 276));
color: white;
font-weight: 700;
font-size: 0.95rem;
border: none;
border-radius: var(--radius);
cursor: pointer;
transition: all 0.05s cubic-bezier(0.1, 0.8, 0.9, 1);
box-shadow: 0 0.5rem 0 oklch(0.4 0.1 265 / 0.5),
0 1rem 1rem oklch(0 0 0 / 0.15);
user-select: none;
}
.press-button-3d:hover {
box-shadow: 0 0.75rem 0 oklch(0.4 0.1 265 / 0.5),
0 1.25rem 1.5rem oklch(0 0 0 / 0.2);
transform: translateY(-3px);
}
.press-button-3d:active {
box-shadow: 0 0.1rem 0 oklch(0.4 0.1 265 / 0.5),
0 0.2rem 0.25rem oklch(0 0 0 / 0.1);
transform: translateY(0.4rem);
}
.press-button-3d:focus-visible {
outline: 2px solid oklch(0.72 0.19 265);
outline-offset: 2px;
}
.press-button-3d:disabled {
opacity: 0.5;
cursor: not-allowed;
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer specializing in tactile, delightful button interactions.
Your goal: Create a 3D press button component that simulates physical button mechanics—with layered shadow depth that changes on hover and click to create a convincing pressed/released effect.
Technical constraints:
1. Use box-shadow (not CSS 3D transforms) to create depth illusion
2. Shadow must have two layers: one for "3D depth" (hard shadow) and one for ambient shadow
3. On hover, increase depth and lift button slightly (translateY -2 to -3px)
4. On active/click, compress shadow to simulate pressing down (move button down slightly)
5. Transition must be very fast (0.05s) for tactile responsiveness
6. Use cubic-bezier timing function (not ease or linear) for snappy feel
7. Shadow colors should use oklch() with opacity
8. Must prevent text selection during click with user-select: none
Vanilla implementation:
- Define base shadow with two layers (depth shadow + ambient)
- Use :hover to increase both shadows and lift button
- Use :active to compress shadows and reduce lift (button presses down)
- Use a very fast cubic-bezier transition (0.05s) for responsiveness
- Set user-select: none to prevent text selection on rapid clicks
React implementation:
- Create Button component with press prop to enable 3D effect
- Use CSS Modules with @keyframes bounce animation on click
- Support onClick handler that fires immediately
- Add haptic feedback hook for mobile: navigator.vibrate([10, 5, 10])
- Create usePress hook that tracks mouse down/up state
Edge cases to handle:
1. Touch devices: use :active instead of mousedown for mobile feedback
2. Rapid clicks: transition speed (0.05s) ensures snappy repeated feedback
3. Very large buttons: shadow depth should scale proportionally
4. Disabled state: reduce shadow depth and remove pointer-events
5. RTL text: ensure shadow direction works in right-to-left layouts
6. High contrast mode: shadow may disappear; add border as fallback
Why this matters in 2026
Users crave tactile feedback. A button that physically responds to touch—compressing when pressed, rising when released—feels satisfying and premium. This isn't just eye candy; it's cognitive feedback that confirms user intent. A 3D press button says "your click matters" through physics-based motion.
In 2026, boring flat buttons are dead. Progressive designs use layered shadows and micro-interactions to create depth perception. The 3D press button is the sweet spot: simple to implement with pure CSS, zero JavaScript required, and dramatically more satisfying than a flat button. This micro-investment in UX detail pays dividends in user satisfaction.
The logic
The 3D press button uses a two-layer box-shadow system: a hard "depth" shadow (0.5rem offset) that simulates the button's thickness, and a soft "ambient" shadow (1rem offset) that grounds the button to the surface. On hover, both shadows expand and the button lifts via translateY(-3px), exaggerating the button's separation from the surface.
On active (click), the shadows compress dramatically—the depth shadow shrinks to 0.25rem and the ambient shadow to 0.5rem. The button moves down to translateY(-0.25rem), simulating the physical action of pressing. The crucial detail is the 0.05s transition with a custom cubic-bezier timing function (0.1, 0.8, 0.9, 1) that prioritizes snappiness over smoothness. This creates immediate tactile feedback that makes the button feel responsive.
Accessibility & performance
Accessibility: The 3D press button must have high contrast between text and background. The shadow depth aids visual perception but shouldn't be relied upon for understanding the button state; use color, text, or focus outline as well. The :focus-visible outline is essential for keyboard users. Include a :disabled state with reduced opacity so unavailable buttons are visually distinct.
Performance: The 0.05s transition is very fast, but box-shadow changes are still GPU-accelerated. The cubic-bezier easing function creates the snappy feel without sacrificing performance. The user-select: none prevents unwanted text selection during rapid clicks. Avoid animating shadow color or offset distance, which can cause repaints; only change shadow values on state transitions (hover, active).