CodePen

HTML

            
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<div>
  <h1>3D Parallax Buttons</h1>

  <p>Observe the 3D effect as you scroll around. The larger the monitor you use, the bigger the effect you'll be able to see.</p>
  <p></p>

<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
<p>Observe all the sides of the buttons here as you scroll down.</p>
<p>The effect is noticable but not distracting.</p>
<p>Looks great on iOS and Android devices as well.</p>
<p>The JavaScript won't be a performance hit.</p>
<p>However, having a hundred 3D transformed objects will most likely slow down mobile devices and weaker computers so use them wisely.</p>
<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
<button>!</button>
  <h2>This title doesn't need JS, just like the &lt;hr&gt;s down below!</h2>
<p>You can use this technique for both inline links and important call to action buttons.</p>
<p>Combining different types also seems to work well:</p>
<button>Fancy Button!</button><br>
<button class="social">
<svg width="17" height="15" xmlns="http://www.w3.org/2000/svg">
 <g>
  <title>Layer 1</title>
  <g id="g2989" transform="matrix(0.0124718 0 0 -0.0124718 27.9323 53.5044)" fill="#00aced">
   <path id="path2991" d="m-1372.63623,4272.02979c-125,-45 -204,-160.99951 -195,-287.99951l3,-49l-50,6c-182,23 -341,101 -476.00024,231.99951l-66,65l-17,-48c-36,-106.99951 -13,-219.99951 62,-295.99951c40,-42 31,-48 -38,-23c-24,8 -45,14 -47,11c-7,-7 17,-98 36,-134c26,-50 79,-99 137.00024,-128l49,-23l-58,-1c-56.00024,0 -58.00024,-1 -52.00024,-22c20,-65 99.00024,-134 187.00024,-164l62,-21l-54,-32c-80,-46 -174.00024,-72 -268.00024,-74c-45,-1 -82,-5 -82,-8c0,-10 122,-66 193.00012,-88c213.00012,-65 466.00012,-37 656.00012,74c135,79 270,236 333,388c34,81 67.99994,229 67.99994,300c0,46 3,52 59,107c33,32 64,66.99951 70,76.99951c10,19 9,19 -42,2c-85,-29.99951 -97,-25.99951 -55,19c31,32 68,90 68,107c0,3 -15,-2 -32,-11c-18,-10 -58,-25 -88,-34l-53.99994,-17l-49,33c-27,18 -65,38 -85,44c-51,14 -129,12 -175,-4z" fill="#000000"/>
  </g>
 </g>
</svg>
</button>
<button class="social">
<svg width="7" height="15" xmlns="http://www.w3.org/2000/svg">
 <title>Facebook Home</title>
 <g>
  <title>Layer 1</title>
  <path id="path3857-4" d="m4.92885,0c-2.49491,0 -3.37211,1.26051 -3.37211,3.37858l0,1.55835l-1.55673,0l0,2.59617l1.55673,0l0,7.5331l3.11185,0l0,-7.5331l2.07726,0l0.27481,-2.59617l-2.35207,0l0.00323,-1.2997c0,-0.67724 0.06482,-1.03944 1.0362,-1.03944l1.29809,0l0,-2.59779l-2.07726,0z" mask="none" fill="#000000"/>
 </g>
</svg>
</button>
<button class="social">
<svg width="15" height="15" xmlns="http://www.w3.org/2000/svg">
   <circle id="svg_4" fill="#000" r="2.05961" cy="12.95838" cx="2.05961"/>
   <path id="svg_5" fill="#000" d="m9.95478,15.01799l-2.91778,0a7.037,7.037 0 0 0 -7.037,-7.037l0,-2.91778a9.95478,9.95478 0 0 1 9.95478,9.95478z"/>
   <path id="svg_6" fill="#000" d="m12.01439,15.01799a12.01439,12.01439 0 0 0 -12.01439,-12.01439l0,-3.0036a15.01799,15.01799 0 0 1 15.01799,15.01799l-3.0036,0z"/>
</svg>
</button>
<hr />
</div>
            
          
!
via HTML Inspector

CSS

            
              /* button variables */
$bsize: 18px;
$blue: #aad1ee;
$titlefoldsize: 50px;
$animtiming: 80ms steps(20) both;

html {
  height: 100%;
  overflow: hidden;
  background: #777;
}


/* the perspective set here is the most important declaration for the 3d look of the buttons (values over 1000px make the buttons look almost flat) */
body {
  margin: 0;
  height: 100%;
  overflow-y: scroll;
  font-family: sans-serif;
  font-size: 16px;
  perspective: 350px;
}

div {
  width: 600px;
  margin: 0 auto;
  padding: 20px 20px 600px;
  background: #fff;
  transform-style: preserve-3d;
}

h1, h2 {
  text-align: center;
}

h1, h2, hr {
  position: relative;
  background: #444;
  color: #fff;
  line-height: 2em;
  margin-left: -$titlefoldsize;
  margin-right: -$titlefoldsize;
  transform-style: preserve-3d;
  transform: translateZ(1px);
  
  &::before, &::after {
    content:"";
    position: absolute;
    top: 0;
    width: $titlefoldsize;
    height: 100%;
    background: darken(#444, 10%);
  }
    
  &::before {
    left: -$titlefoldsize;
    transform-origin: 100% 0%;
    transform: rotateY(-130deg);
  }
  &::after {
    right: -$titlefoldsize;
    transform-origin: 0% 0%;
    transform: rotateY(130deg);
  }
}

hr {
  border: 0;
  height: 10px;
  background: #444;
}

button {
  position: relative;
  display: inline-block;
  padding: 4px 16px;
  border: 0;
  min-width: 48px;
  margin: 10px;
  font-size: 24px;
  font-weight: bold;
  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( $bsize );
  cursor: pointer;
  user-select: none;

  &:focus {outline: none;}
  /* double colon syntax for pseudo elements because IE8 doesn't need to see them (no transforms) */
  &::before, &::after {
    position: absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
    background: darken($blue, 25%);
    transform-origin: 0% 0%;
  }
  &::before {
    content:"";
    height: $bsize;
    background: darken($blue, 15%);
    transform: rotateX(-90deg);
  }
  &::after {
    content:"";
    width: $bsize;
    transform: rotateY(90deg);
  }

  &.top-half::before {
    top: auto;
    bottom: 0;
    background: darken($blue, 35%);
    transform-origin: 100% 100%;
    transform: rotateX(90deg);
  }

  &.left-half::after {
    left: auto;
    right: 0;
    transform-origin: 100% 100%;
    transform: rotateY(-90deg);
  }


/* pushing the button down; done with animations with steps because transitions are too buggy and jumpy */
  &:active {  
    animation: button $animtiming;
    &::before {
      animation: buttonbefore $animtiming;
    }
    &::after {
      animation: buttonafter $animtiming;
    }
  }
}

@keyframes button {
    to { transform: translateZ( $bsize/2 ) }
}
@keyframes buttonbefore {
    to { height: $bsize/2; }
}
@keyframes buttonafter {
    to { width: $bsize/2; }
}


/* hide pseudo elements in IE9 and IE10 (no 3D transforms) */
@media screen and (min-width:0\0) {  
  button::before, button::after {
    display: none;
  }
}

/* hide pseudo elements in non-webkit Opera) */
x:-o-prefocus, button::before, button::after {
  display: none;
}
            
          
!
? ?
? ?
Must be a valid URL.
+ add another resource
via CSS Lint

JS

            
              // changing the sides of the button only on load and on browser resize
$(window).on("load resize", function() {
	topHalf();
	leftHalf();
});

// when scrolling vertically only top and bottom sides need to be recalculated
$("body").scroll(function() {
	topHalf();
});

// toggle button class if it's in the top or bottom half of the screen
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);
	});
}

// toggle button class if it's in left or right half of the screen
function leftHalf() {
	$("button").each(function() {
		var self = $(this),
        offLeft = self.offset().left,
        halfWindowWidth = ($(window).width())/2;

		self.toggleClass("left-half", offLeft < halfWindowWidth);
	});
}
            
          
!
Must be a valid URL.
+ add another resource
via JS Hint
Loading ..................