<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@500&display=swap" rel="stylesheet">
<div id="dark-light-container">
<div class="white-container">
<button class="button"><span>Hover!</span></button>
</div>
<div class="black-container">
<button class="button green" data-filter-color="rgba(71, 255, 111, 0.4)"><span>🔧 Work</span></button>
</div>
<div class="black-container">
<button class="button animated" data-filter-color="#00a1ff57"><span>🐭 Mouse Over</span></button>
</div>
<div class="white-container">
<button class="button purple" data-filter-color="#e347ff8f" data-custom-perspective="900px"><span>⚙️ Another Example</span></button>
</div>
</div>
<a href="https://fjolt.com/article/css-javascript-3d-depth-hover-buttons" target="_blank">Read Article Here</a>
body {
margin: 0;
background: rgb(10 13 37);
padding: 0;
}
#dark-light-container {
float: left;
width: 100%;
margin: 0 0 0 0;
}
a {
color: rgba(255,255,255,0.5);
font-size: 1rem;
font-family: Inter, sans-serif;
border-top: 1px solid rgba(255,255,255,0.2);
display: block;
font-size: 1.25rem;
padding: 1rem 1.5rem;
float: left;
width: 100%;
}
#dark-light-container > div {
float: left;
box-sizing: border-box;
position: relative;
padding: 2rem;
width: 50%;
text-align: center;
}
.white-container { background: white; }
.black-container { background: black; }
button {
box-shadow: none;
background: transparent;
font-family: Inter,system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;
transform-style: preserve-3d;
padding: 0;
height: auto;
float: none;
appearance: none;
border: none;
letter-spacing: 1px;
}
button span {
background: linear-gradient(180deg, #ff7147, #e0417f);
font-size: 2rem;
color: white;
padding: 1rem 2rem;
line-height: 3rem;
will-change: transform, filter;
float: none;
margin: 0;
transition: all 0.15s ease-out;
height: auto;
border-radius: 100px;
overflow: hidden;
display: block;
margin: 0px auto;
display: block;
transform: rotateX(0deg) rotateY(0deg) scale(1);
filter: drop-shadow(0 15px 15px rgba(0,0,0,0.3));
font-weight: 500;
perspective-origin: 0 0;
letter-spacing: 0;
}
button.animated span:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 600%;
height: 200%;
background: linear-gradient(90deg, #00a1ff, #af4ae6, #00a1ff, #2cefff);
animation: colorCycle 8s infinite alternate;
z-index: -1;
}
button.green span {
background: linear-gradient(180deg, #47ff6f, #417ce0);
filter: drop-shadow(0 15px 15px rgba(71, 255, 111, 0.4));
}
button.animated span {
filter: drop-shadow(0 15px 15px #00a1ff57);
}
button.purple span {
background: linear-gradient(180deg, #e347ff, #7b41e0);
}
button.animated.example span:after {
width: 200%;
height: 100%;
animation: colorCycleExample 4s infinite alternate;
}
@keyframes colorCycle {
0% {
top: 0;
left: 0;
transform: scale(1);
}
50% {
top: -50%;
transform: scale(1.4);
}
100% {
top: 0;
left: -500%;
transform: scale(1);
}
}
@keyframes colorCycleExample {
0% {
top: 0;
left: 0;
transform: scale(1);
}
100% {
top: 0;
left: -100%;
transform: scale(1);
}
}
@media screen and (max-width: 1000px) {
#dark-light-container > div {
width: 100%;
}
}
let calculateAngle = function(e, item, parent) {
let dropShadowColor = `rgba(0, 0, 0, 0.3)`
if(parent.getAttribute('data-filter-color') !== null) {
dropShadowColor = parent.getAttribute('data-filter-color');
}
// Get the x position of the users mouse, relative to the button itself
let x = Math.abs(item.getBoundingClientRect().x - e.clientX);
// Get the y position relative to the button
let y = Math.abs(item.getBoundingClientRect().y - e.clientY);
// Calculate half the width and height
let halfWidth = item.getBoundingClientRect().width / 2;
let halfHeight = item.getBoundingClientRect().height / 2;
// Use this to create an angle. I have divided by 6 and 4 respectively so the effect looks good.
// Changing these numbers will change the depth of the effect.
let calcAngleX = (x - halfWidth) / 6;
let calcAngleY = (y - halfHeight) / 4;
// Set the items transform CSS property
item.style.transform = `rotateY(${calcAngleX}deg) rotateX(${calcAngleY}deg) scale(1.15)`;
// And set its container's perspective.
parent.style.perspective = `${halfWidth * 2}px`
item.style.perspective = `${halfWidth * 3}px`
if(parent.getAttribute('data-custom-perspective') !== null) {
parent.style.perspective = `${parent.getAttribute('data-custom-perspective')}`
}
// Reapply this to the shadow, with different dividers
let calcShadowX = (x - halfWidth) / 3;
let calcShadowY = (y - halfHeight) / 3;
// Add a filter shadow - this is more performant to animate than a regular box shadow.
item.style.filter = `drop-shadow(${-calcShadowX}px ${calcShadowY}px 15px ${dropShadowColor})`;
}
document.querySelectorAll('.button').forEach(function(item) {
item.addEventListener('mouseenter', function(e) {
calculateAngle(e, this.querySelector('span'), this);
});
item.addEventListener('mousemove', function(e) {
calculateAngle(e, this.querySelector('span'), this);
});
item.addEventListener('mouseleave', function(e) {
let dropShadowColor = `rgba(0, 0, 0, 0.3)`
if(item.getAttribute('data-filter-color') !== null) {
dropShadowColor = item.getAttribute('data-filter-color')
}
item.querySelector('span').style.transform = `rotateY(0deg) rotateX(0deg) scale(1)`;
item.querySelector('span').style.filter = `drop-shadow(0 10px 15px ${dropShadowColor})`;
});
})
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.