Back
Learn how to build a tactile, three-dimensional button with shadows, highlights, and satisfying press animation using CSS transforms.
<div class="button-container">
<button class="btn-3d">Click Me</button>
</div>
The button text can be customized to match your specific call-to-action.
.button-container {
display: flex;
justify-content: center;
align-items: center;
}
Next, we define the 3D button with CSS variables for easy customization:
.btn-3d {
--button-color: #4F46E5;
--button-color-dark: #3730A3;
--button-color-light: #818CF8;
--button-text: white;
--button-height: 12px;
--button-radius: 8px;
--button-padding: 16px 32px;
--button-font-size: 16px;
position: relative;
font-family: system-ui, -apple-system, sans-serif;
font-weight: 600;
font-size: var(--button-font-size);
color: var(--button-text);
background-color: var(--button-color);
border: none;
border-radius: var(--button-radius);
padding: var(--button-padding);
cursor: pointer;
box-shadow:
/* Bottom edge */
0 var(--button-height) 0 var(--button-color-dark),
/* Subtle inner highlight */
inset 0 1px 0 var(--button-color-light),
/* Dark bottom shadow */
0 calc(var(--button-height) + 2px) 0 rgba(0, 0, 0, 0.2);
transform: translateY(0);
transition: transform 0.15s ease, box-shadow 0.15s ease;
user-select: none;
}
The key properties here:
- We define three shades of our button color (main, dark, and light) to create the 3D effect
- The box-shadow property creates multiple shadows: one for the bottom edge, one for the inner highlight, and one for the ground shadow
- We set a faster transition time (0.15s) for a snappier press feel
- user-select: none prevents text selection when clicking rapidly
Next, we create the hover and active states:
.btn-3d:hover {
background-color: #4338CA;
}
.btn-3d:active {
transform: translateY(var(--button-height));
box-shadow:
/* Flattened bottom edge */
0 0 0 var(--button-color-dark),
/* Maintain inner highlight */
inset 0 1px 0 var(--button-color-light),
/* Reduced bottom shadow */
0 2px 0 rgba(0, 0, 0, 0.2);
}
The active state is the heart of our 3D effect:
- It translates the button down by the button-height amount
- It changes the box-shadow to flatten the bottom edge (0 0 0)
- It reduces the ground shadow to complete the pressed look
To create the visible side edge of the button, we use a pseudo-element:
.btn-3d::before {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: calc(-1 * var(--button-height));
height: var(--button-height);
background-color: var(--button-color-dark);
border-bottom-left-radius: var(--button-radius);
border-bottom-right-radius: var(--button-radius);
transition: background-color 0.15s ease;
}
.btn-3d:hover::before {
background-color: #3730A3;
}
.btn-3d:active::before {
height: 0;
}
This pseudo-element creates the visible side/edge of the button. When the button is pressed (active), the height becomes 0 to complete the illusion of the button being pressed down.
Finally, we add focus styles for accessibility:
/* Focus state for accessibility */
.btn-3d:focus {
outline: none;
box-shadow:
0 var(--button-height) 0 var(--button-color-dark),
inset 0 1px 0 var(--button-color-light),
0 calc(var(--button-height) + 2px) 0 rgba(0, 0, 0, 0.2),
0 0 0 3px rgba(79, 70, 229, 0.3);
}
.btn-3d:active:focus {
box-shadow:
0 0 0 var(--button-color-dark),
inset 0 1px 0 var(--button-color-light),
0 2px 0 rgba(0, 0, 0, 0.2),
0 0 0 3px rgba(79, 70, 229, 0.3);
}
These focus styles add a subtle outline around the button for keyboard navigation while maintaining the 3D effect for both the normal and pressed states.
This 3D button offers several advantages:
- Tactile feedback: The press animation provides a physical-feeling response to interaction.
- Depth perception: The multiple shadows and highlights create a realistic sense of depth.
- Visual engagement: The button stands out on the page and clearly communicates its interactive nature.
- Playful interaction: The satisfying press effect adds a touch of playfulness to your interface.
- Accessibility: Despite its stylized appearance, it maintains keyboard focus visibility.
This button style is ideal for gaming interfaces, children's websites, interactive educational platforms, or any application where you want to create a more tangible, physical-feeling interaction.
<div class="button-container">
<button class="btn-3d">Click Me</button>
</div>
<style>
.button-container {
display: flex;
justify-content: center;
align-items: center;
}
.btn-3d {
--button-color: #4F46E5;
--button-color-dark: #3730A3;
--button-color-light: #818CF8;
--button-text: white;
--button-height: 12px;
--button-radius: 8px;
--button-padding: 16px 32px;
--button-font-size: 16px;
position: relative;
font-family: system-ui, -apple-system, sans-serif;
font-weight: 600;
font-size: var(--button-font-size);
color: var(--button-text);
background-color: var(--button-color);
border: none;
border-radius: var(--button-radius);
padding: var(--button-padding);
cursor: pointer;
box-shadow:
/* Bottom edge */
0 var(--button-height) 0 var(--button-color-dark),
/* Subtle inner highlight */
inset 0 1px 0 var(--button-color-light),
/* Dark bottom shadow */
0 calc(var(--button-height) + 2px) 0 rgba(0, 0, 0, 0.2);
transform: translateY(0);
transition: transform 0.15s ease, box-shadow 0.15s ease;
user-select: none;
}
.btn-3d:hover {
background-color: #4338CA;
}
.btn-3d:active {
transform: translateY(var(--button-height));
box-shadow:
/* Flattened bottom edge */
0 0 0 var(--button-color-dark),
/* Maintain inner highlight */
inset 0 1px 0 var(--button-color-light),
/* Reduced bottom shadow */
0 2px 0 rgba(0, 0, 0, 0.2);
}
.btn-3d::before {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: calc(-1 * var(--button-height));
height: var(--button-height);
background-color: var(--button-color-dark);
border-bottom-left-radius: var(--button-radius);
border-bottom-right-radius: var(--button-radius);
transition: background-color 0.15s ease;
}
.btn-3d:hover::before {
background-color: #3730A3;
}
.btn-3d:active::before {
height: 0;
}
/* Focus state for accessibility */
.btn-3d:focus {
outline: none;
box-shadow:
0 var(--button-height) 0 var(--button-color-dark),
inset 0 1px 0 var(--button-color-light),
0 calc(var(--button-height) + 2px) 0 rgba(0, 0, 0, 0.2),
0 0 0 3px rgba(79, 70, 229, 0.3);
}
.btn-3d:active:focus {
box-shadow:
0 0 0 var(--button-color-dark),
inset 0 1px 0 var(--button-color-light),
0 2px 0 rgba(0, 0, 0, 0.2),
0 0 0 3px rgba(79, 70, 229, 0.3);
}
</style>
Thank you for reading this article.
Comments
No comments yet. Be the first to comment!