<article class="kontext">
	<div class="layer one show">
		<h2>Kontext</h2>
		<p>A context-shift transition. Use the dots below or your keyoard arrows.</p>
	</div>
	<div class="layer two">
		<h2>Layer Two</h2>
	</div>
	<div class="layer three">
		<h2>Layer Three</h2>
	</div>
</article>

<ul class="bullets"></ul>
.kontext {
	width: 100%;
	height: 100%;
}

.kontext .layer {
	position: absolute;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
	visibility: hidden;
	/*box-shadow: 0px 0px 120px rgba( 0, 0, 0, 0.8 );*/
}

.kontext .layer.show {
	visibility: visible;
}

.kontext.capable {
	-webkit-perspective: 1000px;
	   -moz-perspective: 1000px;
	        perspective: 1000px;

	-webkit-transform-style: preserve-3d;
	   -moz-transform-style: preserve-3d;
	        transform-style: preserve-3d;
}

.kontext.capable .layer {
	-webkit-transform: translateZ( -100px );
	   -moz-transform: translateZ( -100px );
	        transform: translateZ( -100px );
}

.kontext.capable .layer.show {
	-webkit-transform: translateZ( 0px );
	   -moz-transform: translateZ( 0px );
	        transform: translateZ( 0px );
}

.kontext.capable.animate .layer.show.right {
	-webkit-animation: show-right 1s forwards ease;
	   -moz-animation: show-right 1s forwards ease;
	        animation: show-right 1s forwards ease;
}

.kontext.capable.animate .layer.hide.right {
	-webkit-animation: hide-right 1s forwards ease;
	   -moz-animation: hide-right 1s forwards ease;
	        animation: hide-right 1s forwards ease;
}

.kontext.capable.animate .layer.show.left {
	-webkit-animation: show-left 1s forwards ease;
	   -moz-animation: show-left 1s forwards ease;
	        animation: show-left 1s forwards ease;
}

.kontext.capable.animate .layer.hide.left {
	-webkit-animation: hide-left 1s forwards ease;
	   -moz-animation: hide-left 1s forwards ease;
	        animation: hide-left 1s forwards ease;
}


/* CSS animation keyframes */

@-webkit-keyframes show-right {
	0%   { -webkit-transform: translateZ( -200px ); }
	40%  { -webkit-transform: translate( 40%, 0 ) scale( 0.8 ) rotateY( -20deg ); }
	100% { -webkit-transform: translateZ( 0px ); }
}

@-webkit-keyframes hide-right {
	0%   { -webkit-transform: translateZ( 0px ); visibility: visible; }
	40%  { -webkit-transform: translate( -40%, 0 ) scale( 0.8 ) rotateY( 20deg ); }
	100% { -webkit-transform: translateZ( -200px ); visibility: hidden; }
}

@-moz-keyframes show-right {
	0%   { -moz-transform: translateZ( -200px ); }
	40%  { -moz-transform: translate( 40%, 0 ) scale( 0.8 ) rotateY( -20deg ); }
	100% { -moz-transform: translateZ( 0px ); }
}

@-moz-keyframes hide-right {
	0%   { -moz-transform: translateZ( 0px ); visibility: visible; }
	40%  { -moz-transform: translate( -40%, 0 ) scale( 0.8 ) rotateY( 20deg ); }
	100% { -moz-transform: translateZ( -200px ); visibility: hidden; }
}

@keyframes show-right {
	0%   { transform: translateZ( -200px ); }
	40%  { transform: translate( 40%, 0 ) scale( 0.8 ) rotateY( -20deg ); }
	100% { transform: translateZ( 0px ); }
}

@keyframes hide-right {
	0%   { transform: translateZ( 0px ); visibility: visible; }
	40%  { transform: translate( -40%, 0 ) scale( 0.8 ) rotateY( 20deg ); }
	100% { transform: translateZ( -200px ); visibility: hidden; }
}


@-webkit-keyframes show-left {
	0%   { -webkit-transform: translateZ( -200px ); }
	40%  { -webkit-transform: translate( -40%, 0 ) scale( 0.8 ) rotateY( 20deg ); }
	100% { -webkit-transform: translateZ( 0px ); }
}

@-webkit-keyframes hide-left {
	0%   { -webkit-transform: translateZ( 0px ); visibility: visible; }
	40%  { -webkit-transform: translate( 40%, 0 ) scale( 0.8 ) rotateY( -20deg ); }
	100% { -webkit-transform: translateZ( -200px ); visibility: hidden; }
}

@-moz-keyframes show-left {
	0%   { -moz-transform: translateZ( -200px ); }
	40%  { -moz-transform: translate( -40%, 0 ) scale( 0.8 ) rotateY( 20deg ); }
	100% { -moz-transform: translateZ( 0px ); }
}

@-moz-keyframes hide-left {
	0%   { -moz-transform: translateZ( 0px ); visibility: visible; }
	40%  { -moz-transform: translate( 40%, 0 ) scale( 0.8 ) rotateY( -20deg ); }
	100% { -moz-transform: translateZ( -200px ); visibility: hidden; }
}

@keyframes show-left {
	0%   { transform: translateZ( -200px ); }
	40%  { transform: translate( -40%, 0 ) scale( 0.8 ) rotateY( 20deg ); }
	100% { transform: translateZ( 0px ); }
}

@keyframes hide-left {
	0%   { transform: translateZ( 0px ); visibility: visible; }
	40%  { transform: translate( 40%, 0 ) scale( 0.8 ) rotateY( -20deg ); }
	100% { transform: translateZ( -200px ); visibility: hidden; }
}


/* Dimmer */

.kontext .layer .dimmer {
	display: block;
	position: absolute;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
	visibility: hidden;
	background: transparent;
}

	.kontext.capable.animate .layer .dimmer {
		-webkit-transition: background 1s ease;
		   -moz-transition: background 1s ease;
		        transition: background 1s ease;
	}

	.kontext.capable.animate .layer.hide .dimmer {
		visibility: visible;
		background: rgba( 0, 0, 0, 0.7 );
	}




/* Styles for the demo */

html,
body {
	width: 100%;
	height: 100%;
	padding: 0;
	margin: 0;
	overflow: hidden;
}

body {
	background-color: #222;
	background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAGklEQVQIW2NkYGD4D8SMQAwGcAY2AbBKDBUAVuYCBQPd34sAAAAASUVORK5CYII=);
	background-repeat: repeat;

	font-family: Helvetica, sans-serif;
	font-size: 16px;
	color: #fff;
}

.layer {
	text-align: center;
	text-shadow: 1px 1px 0px rgba( 0, 0, 0, 0.1 );
}

.layer h2 {
	position: relative;
	top: 20%;
	margin: 0;
	font-size: 6.25em;
}

.layer p {
	position: relative;
	top: 20%;
	margin: 0;
}

.layer.one {
	background: #dc6c5f;
}

.layer.two {
	background: #95dc84;
}

.layer.three {
	background: #64b9d2;
}

.bullets {
	position: absolute;
	width: 100%;
	bottom: 20px;
	padding: 0;
	margin: 0;
	text-align: center;
}

	.bullets li {
		display: inline-block;
		width: 20px;
		height: 20px;
		border-radius: 50%;
		margin: 0 3px;

		background: rgba( 255, 255, 255, 0.5 );
		box-shadow: 0px 0px 4px rgba( 0, 0, 0, 0.2 );
		cursor: pointer;

		-webkit-tap-highlight-color: rgba( 0, 0, 0, 0 );
	}

		.bullets li:hover {
			background: rgba( 255, 255, 255, 0.8 );
		}

		.bullets li.active {
			cursor: default;
			background: #fff;
		}

@media screen and (max-width: 400px) {
	body {
		font-size: 12px;
	}

	.layer h2 {
		font-size: 5em;
	}

	.bullets li {
		margin: 0 6px;
	}
}

window.kontext = function( container ) {

	// Dispatched when the current layer changes
	var changed = new kontext.Signal();

	// All layers in this instance of kontext
	var layers = Array.prototype.slice.call( container.querySelectorAll( '.layer' ) );

	// Flag if the browser is capable of handling our fancy transition
	var capable =	'WebkitPerspective' in document.body.style ||
					'MozPerspective' in document.body.style ||
					'msPerspective' in document.body.style ||
					'OPerspective' in document.body.style ||
					'perspective' in document.body.style;

	if( capable ) {
		container.classList.add( 'capable' );
	}

	// Create dimmer elements to fade out preceding slides
	layers.forEach( function( el, i ) {
		if( !el.querySelector( '.dimmer' ) ) el.innerHTML += '<div class="dimmer"></div>';
	} );

	/**
	 * Transitions to and shows the target layer.
	 *
	 * @param target index of layer or layer DOM element
	 */
	function show( target, direction ) {

		// Make sure our listing of available layers is up to date
		layers = Array.prototype.slice.call( container.querySelectorAll( '.layer' ) );

		// Flag to CSS that we're ready to animate transitions
		container.classList.add( 'animate' );

		// Flag which direction
		direction = direction || ( target > getIndex() ? 'right' : 'left' );

		// Accept multiple types of targets
		if( typeof target === 'string' ) target = parseInt( target );
		if( typeof target !== 'number' ) target = getIndex( target );

		// Enforce index bounds
		target = Math.max( Math.min( target, layers.length ), 0 );

		// Only navigate if were able to locate the target
		if( layers[ target ] && !layers[ target ].classList.contains( 'show' ) ) {

			layers.forEach( function( el, i ) {
				el.classList.remove( 'left', 'right' );
				el.classList.add( direction );
				if( el.classList.contains( 'show' ) ) {
					el.classList.remove( 'show' );
					el.classList.add( 'hide' );
				}
				else {
					el.classList.remove( 'hide' );
				}
			} );

			layers[ target ].classList.add( 'show' );

			changed.dispatch( layers[target], target );

		}

	}

	/**
	 * Shows the previous layer.
	 */
	function prev() {

		var index = getIndex() - 1;
		show( index >= 0 ? index : layers.length + index, 'left' );

	}

	/**
	 * Shows the next layer.
	 */
	function next() {

		show( ( getIndex() + 1 ) % layers.length, 'right' );

	}

	/**
	 * Retrieves the index of the current slide.
	 *
	 * @param of [optional] layer DOM element which index is
	 * to be returned
	 */
	function getIndex( of ) {

		var index = 0;

		layers.forEach( function( layer, i ) {
			if( ( of && of == layer ) || ( !of && layer.classList.contains( 'show' ) ) ) {
				index = i;
				return;
			}
		} );

		return index;

	}

	/**
	 * Retrieves the total number of layers.
	 */
	function getTotal() {

		return layers.length;

	}

	// API
	return {

		show: show,
		prev: prev,
		next: next,

		getIndex: getIndex,
		getTotal: getTotal,

		changed: changed

	};

};

/**
 * Minimal utility for dispatching signals (events).
 */
kontext.Signal = function() {
	this.listeners = [];
}

kontext.Signal.prototype.add = function( callback ) {
	this.listeners.push( callback );
}

kontext.Signal.prototype.remove = function( callback ) {
	var i = this.listeners.indexOf( callback );

	if( i >= 0 ) this.listeners.splice( i, 1 );
}

kontext.Signal.prototype.dispatch = function() {
	var args = Array.prototype.slice.call( arguments );
	this.listeners.forEach( function( f, i ) {
		f.apply( null, args );
	} );
}

  

  
  

// Create a new instance of kontext
var k = kontext( document.querySelector( '.kontext' ) );


// Demo page JS

var bulletsContainer = document.body.querySelector( '.bullets' );

// Create one bullet per layer
for( var i = 0, len = k.getTotal(); i < len; i++ ) {
	var bullet = document.createElement( 'li' );
	bullet.className = i === 0 ? 'active' : '';
	bullet.setAttribute( 'index', i );
	bullet.onclick = function( event ) { k.show( event.target.getAttribute( 'index' ) ) };
	bullet.ontouchstart = function( event ) { k.show( event.target.getAttribute( 'index' ) ) };
	bulletsContainer.appendChild( bullet );
}

// Update the bullets when the layer changes
k.changed.add( function( layer, index ) {
	var bullets = document.body.querySelectorAll( '.bullets li' );
	for( var i = 0, len = bullets.length; i < len; i++ ) {
		bullets[i].className = i === index ? 'active' : '';
	}
} );

document.addEventListener( 'keyup', function( event ) {
	if( event.keyCode === 37 ) k.prev();
	if( event.keyCode === 39 ) k.next();
}, false );

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.