App Showcase with Grid Overlay

A tutorial about creating a simple grid overlay with subtle transitions for an app showcase.
App Showcase with Grid Overlay

From our monthly sponsor: monday.com is the last team management and design process tool you’ll ever need. Start your free trial.Advertisement

Today we will be creating a little app showcase with a neat effect. The idea is to show a mobile device with a screenshot of an app and when clicking on the device, a grid appears, showing some more screenshots. The effect is very subtle: the device moves back and the grid fades in and scales up. When clicking on another screenshot, the device image gets updated and the grid disappears again.

Please note: this only works as intended in browsers that support the respective CSS properties.

The beautiful iPhone mockup used in the demo is by Jakub Kejha

Let’s get started with the markup.

The Markup

The HTML will consist of a main wrapper that contains a heading, a division for the device and a division for the grid:

<div id="ac-wrapper" class="ac-wrapper">
	<h2>Weatherous <span>Concept & UI Design</span></h2>
	<div class="ac-device">
		<a href="#"><img src="images/screen1.jpg"/></a>
		<h3 class="ac-title">Gentrify small batch umami retro vegan</h3>
	</div>
	<div class="ac-grid">
		<a href="#"><img src="images/screen1.jpg"/><span>Gentrify small batch umami retro vegan</span></a>
		<a href="#"><img src="images/screen2.jpg"/><span>Chambray squid semiotics</span></a>
		<a href="#"><img src="images/screen3.jpg"/><span>Fashion axe blue bottle</span></a>
		<a href="#"><img src="images/screen4.jpg"/><span>Photo booth single-origin coffee</span></a>
		<a href="#"><img src="images/screen5.jpg"/><span>Flexitarian synth keytar blog master</span></a>
		<a href="#"><img src="images/screen6.jpg"/><span>Next level retro flexitarian freegan</span></a>
		<a href="#"><img src="images/screen7.jpg"/><span>Pour-over superious meggings terry</span></a>
		<a href="#"><img src="images/screen8.jpg"/><span>Seitan william vinyl chillwave</span></a>
	</div>
</div>

Once we click on a grid item, we will update the content of the device container. We will also make the span for each grid item appear on hover.

Let’s style everything.

The CSS

Note that the CSS will not contain any vendor prefixes, but you will find them in the files.
In this tutorial we will be going through the style of demo 1.

So let’s start with the main wrapper. This will be the container that will have perspective. The origin will not be in the center but a bit more up:

.ac-wrapper {
	width: 100%;
	position: relative;
	perspective: 1000px;
	perspective-origin: 50% 25%;
}

The heading will be positioned absolutely on the left side of the device:

.ac-wrapper h2 {
	top: 20%;
	width: 50%;
	position: absolute;
	font-weight: 300;
	font-size: 4em;
	text-align: right;
	padding: 0 180px 0 50px;
}

Let’s give a slightly different look to the span:

.ac-wrapper h2 span {
	display: block;
	font-size: 60%;
	color: #c0c0c0;
}

The device will have the iPhone mockup as a background image and we will set the right dimensions. This container will need to preserve the 3D transforms and we’ll add a transition to it. Later, we’ll define a “transition classes” that will contain the properties for the altered states.

.ac-device {
	background: url(../images/iPhone.png) no-repeat;
	width: 288px;
	height: 611px;
	margin: 0 auto;
	position: relative;
	transition: all 0.3s ease;
	transform-style: preserve-3d;
}

The screenshot will be inside of an anchor and we’ll set the dimensions here and position it to fit into the mockup:

.ac-device a {
	height: 440px;
	width: 249px;
	display: inline-block;
	margin: 85px 0 0 20px;
}

.ac-device a img {
	display: block;
}

The title for each screenshot once it’s in the mockup view will be positioned absolutely on the right side of the device:

.ac-device h3 {
	position: absolute;
	font-size: 2.5em;
	left: 100%;
	width: 100%;
	top: 60%;
	margin-left: 30px;
	font-weight: 300;
	color: #888;
}

Now, let’s style the grid. We want to display a total of eight items so a row will have four items. Let’s set a fitting width, make it absolute and center it by setting a negative left margin (half of its width) and a left value of 50%. The initial opacity is 0 and since the grid is displayed and covering the device, we’ll set the pointer events to none so that we can’t click on it when it’s invisible. We’ll also add a transition and translate it -350px on the Z axis:

.ac-grid {
	position: absolute;
	width: 620px;
	left: 50%;
	margin-left: -310px;
	height: 100%;
	z-index: 1000;
	top: 0;
	opacity: 0;
	pointer-events: none;
	transform-style: preserve-3d;
	transition: all 0.3s ease;
	transform: translateZ(-350px);
}

The anchors in the grid will be floated left and the images inside will be set to 100% width. This will come in handy later on when we apply some media queries:

.ac-grid a {
	width: 145px;
	display: block;
	position: relative;
	float: left;
	margin: 10px 5px;
	cursor: pointer;
}

.ac-grid a img {
	display: block;
	width: 100%;
}

The span for the description will be positioned absolutely on top of the anchor and we’ll fade it in and move it a bit on hover:

.ac-grid a span {
	position: absolute;
	height: 100%;
	width: 100%;
	left: 0;
	top: 0;
	text-transform: uppercase;
	padding: 3em 1em 0;
	z-index: 100;
	color: #ddd;
	background: rgba(0,0,0,0.4);
	font-weight: 700;
	opacity: 0;
	transform: translateY(-5px);
	transition: all 0.2s ease;
}

.ac-grid a:hover span {
	opacity: 1;
	transform: translateY(0);
}

Next, we’ll define the “transition classes”. When we click on the device, we’ll apply a class to the wrapper which will trigger the fading in and scaling up of the grid and the moving back of the device:

.ac-wrapper.ac-gridview .ac-device {
	transform: translateZ(-350px);
	opacity: 0.6;
}

.ac-wrapper.ac-gridview .ac-grid {
	transform: translateZ(0px);
	opacity: 1;
	pointer-events: auto;
}

Once the grid is there, we also set the pointer-events to auto again.

Our layout has some absolutely positioned elements and we’ll need to take care of them on smaller screens. The idea is that we will switch the main heading to the right side first and then center everything once the screen is very small. The second media query takes care of the grid structure. Here we will set a fluid width for both, the grid and the anchors:

@media screen and (max-width: 63.875em) {
	.ac-wrapper { 
		font-size: 60%; 
		width: 100%; 
		padding: 0 20px;
	}

	.ac-device {
		margin: 0;
		width: 100%;
	}

	.ac-device h3 { 
		width: 50%;
		left: 290px;
	}

	.ac-wrapper h2 { 
		left: 308px; 
		padding: 0; 
		text-align: left; 
		margin-left: 30px;
	}
}

@media screen and (max-width: 39.8125em) {
	.ac-grid {
		width: 90%;
		left: 5%;
		margin-left: 0;
		padding-top: 150px;
	}

	.ac-grid a {
		width: 22%;
	}
}

@media screen and (max-width: 35.6875em) {
	.ac-wrapper {
		padding: 0 20px 100px;
	}

	.ac-wrapper h2 { 
		width: 100%;
		text-align: center;
		margin: 0 0 1em;
		top: 0;
		left: auto;
		position: relative;
	}

	.ac-device {
		margin: 0 auto;
		width: 288px;
	}

	.ac-device h3 {
		position: relative;
		margin: 0;
		left: auto;
		width: 100%;
		top: 100px;
		display: block;
		text-align: center;
	}
}

And that’s all the style! Let’s take a look at the JavaScript.

The JavaScript

Let’s start by caching some elements and initialize some variables:

	var $el = $( '#ac-wrapper' ),
		// device element
		$device = $el.find( '.ac-device' ),
		// the device image wrapper
		$trigger = $device.children( 'a:first' ),
		// the screens
		$screens = $el.find( '.ac-grid > a' ),
		// the device screen image
		$screenImg = $device.find( 'img' ),
		// the device screen title
		$screenTitle = $device.find( '.ac-title' ),
		// HTML Body element
		$body = $( 'body' ); 	

We will bind the events to the device’s image wrapper (anchor) and to the screen elements.

	function init() {
		// show grid
		$trigger.on( 'click', showGrid );
		// when a gridΒ΄s screen is clicked, show the respective image on the device
		$screens.on( 'click', function() {
			showScreen( $( this ) );
			return false;
		} );
	}

When the device’s image is clicked, the grid is shown. For this to happen the class “ac-gridview” has to be added to the ac-wrapper element:

	function showGrid() {
		$el.addClass( 'ac-gridview' );
		// clicking somewhere else on the page closes the grid view
		$body.off( 'click' ).on( 'click', function() { showScreen(); } );
		return false;
	}

When a screen element is clicked we remove the “ac-gridview” class from the ac-wrapper element, and update both, image source and title on the respective elements:

	function showScreen( $screen ) {
		$el.removeClass( 'ac-gridview' );
		if( $screen ) {
			// update image and title on the device
			$screenImg.attr( 'src', $screen.find( 'img' ).attr( 'src' ) );
			$screenTitle.text( $screen.find( 'span' ).text() );
		}
	}

For the third demo we also want to offer the possibility to navigate through the screenshots without having to open the grid. Depending to the direction we are navigating, the next screen will either scale up / fade in (navigating to the next screen) or move up / fade in (navigating to the previous screen). The same logic applies to the current screenshot. In order for this to work, we need to add the next/previous screen’s image to the DOM right before the current screen’s image (both images being absolute). When the transition ends we remove the old one:

	function navigate( direction ) {

		// if currently animating return
		if( animating ) {
			return false;
		}

		animating = true;
		
		// update current
		if( direction === 'next' ) {
			current = current < screensCount - 1 ? ++current : 0;
		}
		else if( direction === 'prev' ) {
			current = current > 0 ? --current : screensCount - 1;
		}
		
		// next screen to show
		var $nextScreen = $screens.eq( current );

		// if css transitions support:
		if( support ) {

			// append new image to the device and set the transition and initial style
			var $nextScreenImg = $( '' ).css( {
				transition : 'all 0.5s ease',
				opacity : 0,
				transform : direction === 'next' ? 'scale(0.9)' : 'translateY(100px)'
			} ).insertBefore( $screenImg );

			// update title
			$screenTitle.text( $nextScreen.find( 'span' ).text() );

			setTimeout( function() {

				// current image fades out / new image fades in
				$screenImg.css( {
					opacity : 0,
					transform : direction === 'next' ? 'translateY(100px)' : 'scale(0.9)' 
				} ).on( transEndEventName, function() { $( this ).remove(); } );

				$nextScreenImg.css( {
					opacity : 1,
					transform : direction === 'next' ? 'scale(1)' : 'translateY(0px)' 
				} ).on( transEndEventName, function() {
					$screenImg = $( this ).off( transEndEventName );
					animating = false;
				} );

			}, 25 );

		}
		else {
			// update image and title on the device
			$screenImg.attr( 'src', $nextScreen.find( 'img' ).attr( 'src' ) );
			$screenTitle.text( $nextScreen.find( 'span' ).text() );
			animating = false;
		}

	}

This navigation concept was introduced by Evan You and you can check it out here, or watch the video.

And that’s all! We hope you enjoyed this tutorial and find it inspiring!

Tagged with:

ML is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

http://www.codrops.com

Receive our bi-weekly Collective or official newsletter right in your inbox.

CSS Reference

Learn about all important CSS properties from the basics with our extensive and easy-to-read CSS Reference.

It doesn't matter if you are a beginner or intermediate, start learning CSS now.

Feedback 32

Comments are closed.
  1. Wow! I love the looks of this, seems like a super-great way to showcase an application. Bookmarking this one πŸ™‚

  2. Beautiful transition effect! Mary how could you apply this to levels? For instance it has a two level depth now, is it possible to take it three or four levels down?

  3. Impressive article!
    Would be awesome if some links or content could be added in the ac-title h3

    Thanks

  4. Last code block in the article seems garbled.
    Thanks for the nice article for my upcoming app,

  5. Hey Mary great work, but I tried testing this on IE10, doesn’t seem to work as it should. Seems like IE10 doesn’t remove the grid. It’s like an invisible grid on default.

  6. Hi Mary Lou,
    Beautiful work! The only matter is I can’t seem to make two ac-devices with it. I examined the Javascript, and found that based on the order I have of link Javascripts, the first link for a grid will always work, while the following javascript links will not work. How can I use 2 ac-devices for this?
    Thank you,
    Adam

  7. Awesome as always! πŸ™‚

    I have an idea in my to-do list that’s a bit similar to this, but not exactly the same πŸ™‚

  8. Ohhh, talented mary lou
    really, really love you
    your work is so tasty
    but we Β΄ll soon see a nasty
    copy of this in codecanyon

    πŸ™‚