The following is a guest post by Alexander Futekov. We recently published an article by Joshua Bader in which a 3D inset look was adjusted as the page scrolled to give it a more realistic interaction. This is similar only Alexander is using an extruded look on buttons and employing a totally different technique.
The introduction of CSS3 resulted in the explosion of beautiful and interesting buttons, styled with gradients, shadows, and borders – often to achieve a 3D effect. The problem with such 3D buttons is a static perspective. The 3D is not always very convincing when you can only see its front and perhaps a bit from an edge or two – even when the page moves and the position of the button on the page moves.
Fortunately, we can achieve a realistic perspective effect with the help of 3D transforms to adjust the sides of the buttons. We can do this without modifying existing HTML, by using pseudo elements for the sides. Then bring forward the button itself with translateZ
.
Before we start, we must first solve a problem. The perspective origin point is usually set at the vertical and horizontal center of the element. This means that when we set it based on the body we will get something like this:

By setting the height of the body to be 100%, we can fix the perspective origin point to the center of the viewport like this:

html {
height: 100%;
overflow: hidden;
}
body {
height: 100%;
overflow-y: scroll;
}
For a simple <button>
, here’s the CSS:
button {
position: relative;
display: inline-block;
padding: 4px 16px;
border: 0;
background-color: blue;
background-image: radial-gradient(ellipse at top, rgba(255, 255, 255, 0.15) 50%, transparent);
color: #222;
transform-style: preserve-3d;
transform: translateZ(20px);
}
button::before {
content: "";
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
transform-origin: 0% 0%;
transform: rotateX(-90deg);
}
button::after {
content: "";
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
transform-origin: 0% 0%;
transform: rotateY(90deg);
}
But how do we get 4 sides for each button with just 2 pseudo elements? JavaScript! if we want to avoid adding extra wrappers to each button (we do) we need scripting to move the top side down and the left side to the right depending on where the button is relative to the user’s viewport. The script for switching the top with the bottom side of the button is pretty straightforward – changing top/bottom or right/lest sides occurs only on load and on resize, and top/bottom only on scroll:
$(window).on("load resize", function() {
topHalf();
leftHalf();
});
$("body").scroll(function() {
topHalf();
});
function topHalf() {
$("button").each(function() {
var self = $(this),
offTop = self.offset().top,
scrTop = $(window).scrollTop(),
halfWindowHeight = ($(window).height())/2;
self.toggleClass("top-half", (offTop - scrTop) < halfWindowHeight);
});
}
function leftHalf() {
$("button").each(function() {
var self = $(this),
offLeft = self.offset().left,
halfWindowWidth = ($(window).width())/2;
self.toggleClass("left-half", offLeft < halfWindowWidth);
});
}
Editor’s note: This could certainly be refactored a bit to lend structure and gain some efficiency. Feel free to fork the Pen below and have a go!
Some new classes will help position the sides:
button.top-half::before {
top: auto;
bottom: 0;
transform-origin: 100% 100%;
transform: rotateX(90deg);
}
button.left-half::after {
left: auto;
right: 0;
transform-origin: 100% 100%;
transform: rotateY(-90deg);
}
Keep in mind that if you use classes to float the buttons (for example .pull-left
and .pull-right
like in Twitter Bootstrap) you will know where each button is positioned horizontally and thus be able to skip half of the JavaScript code.
Here’s a styled demo to play around with. It has an animated push-down effect for the buttons and includes some other 3D elements that don’t requre any JavaScript:
Check out this Pen!
A similar technique can also be used to replicate the 3D Inset Parallax Effect in a very rough way. It’s a bit more complex, but doesn’t require any JavaScript:
Check out this Pen!
Not working on Chrome Browser
I use Chrome and it works fine for me. Perhaps you could be more specific or take a screencast (http://www.screenr.com/) of you looking at it to demonstrate the “not working” ness.
Works for me in Chrome! Saw this on CodePen a while back :-) awesome work, Chris!
Works from chrome – its very subtle though, I didn’t notice it first time round…
Try using chromeframe, maybe helps.
Not working for me either! Chrome Version 27.0.1453.94 m (up to date) on a Win XP system. Here’s a screen cap: http://imgur.com/dW7RjZg
Also not working for me in Chrome.
Version: 27.0.1453.94 m
OS: Win XP
SZRimaging: It’s a problem with the hardware acceleration NOT being available on WinXP! Guess that’s the price we pay for living in the past… LOL
D Tondro: Not really stuck in the past, just stuck on a corporate machine… Didn’t test it on my Win 8 laptop….
Yipee for slow adoption rates in business culture!
Sorry Chris … I sent a screenshot but many other users already did and reported the not working ness on Chrome ;-)
Not working at all on Chrome 30
Awesome!!! I’ll start applying it!
Very Cool. At first I thought the perspective on the second example was backwards until I realized that it is inset instead of sticking out. It is more clear and looks even cooler with:
Not working for me either! Chrome Version 27.0.1453.94 m (up to date) on a Win XP system. Here’s a screen cap: http://imgur.com/dW7RjZg
… and here is the previous article not working (3D Inset) either! http://i.imgur.com/HuvuDcx.jpg
It seems like Chrome has some issues with 3D CSS on XP, probably due to lack of hardware acceleration.
Check this address “chrome://gpu” – it says in the beginning whether 3D CSS is available at all.
Using codepen for demos are great… again thank u for this wonderful tutorial…
Can you give me a detailed tutorial about making logo image by css3?Thank you!
I like it. Very slick, but it felt like as you press the buttons the 3d sides should disappear, almost like you are pressing the button inwards. I forked your Code Pen demo and this is what came up with. http://codepen.io/KenCorbettJr/pen/hnkmv
Fantastic fork, Ken! The change makes it feel like real button presses – skeuo3dparallax for the win!
Not working for me on Vista Business with Chrome beta channel. Checked “chrome://gpu” as suggested, and the only items not hardware accelerated are Panel Fitting, Video Decode, and Rasterization.
Not working in chrome 27.0.1453.93 in Linux either: http://www.screenr.com/3n7H
Yep ! Same problem…
Latest Chrome on a Mac – Not working for me.
Safari failed to load the CSS from the pen, it seems – Looks interesting: Imgur
Firefox – Looks awesome!
It is subtle, though it is working for me on Mac with Chrome 27.0.1453.93.
Sorry,
yeah here is a screenshot on CHrome :
http://awesomescreenshot.com/0251c3f660
The screenshot only last 3 days …
Works fine on firefox.
Config : version 25.0.1364.160 Ubuntu 12.04 (25.0.1364.160-0ubuntu0.12.04.1)
You can implement it with simple
border
.Hey Guys, it looks all good by me but when i copy the whole thing safari and chrome only shows me the front, top and the left side of the button. why? plz help
Looks like a promising css button technique but will have to wait until browser compatibility issues are resolved.
Parallax is the upcoming css3 button trends.
This is awesome. Works fine for me in Chrome 27.0.1453.94 m.
Props Chris, awesome find.
Awesome, they remind me of boxes in Super Mario, I was expecting a coin to pop out of the top when clicking.
For all those folks having problems getting this to work, you are probably dealing with an integrated graphics chipset or a non-supported graphics accelerator.
A good test of this is whether or not you see the following correctly. (It should look like a ring or a cube, not a flat rotating sprite):
https://www.webkit.org/blog-files/3d-transforms/morphing-cubes.html
This page might help solve the issue:
http://stackoverflow.com/questions/7455502/webkit-backface-visibility-not-working/14759299
Now this is cool. I must find some excuse to put these into my next design.