Today’s resource is inspired by the Primer app, which makes a great use of cards and motion throughout its design. We applied similar effects to a portfolio template, with expandable items and a bold, full-page navigation.

Images: Unsplash

Creating the structure

The HTML structure is composed by 3 main elements: a .cd-nav-trigger for the menu icon, a nav.cd-primary-nav for the main navigation, and a .cd-projects-container wrapping the unordered list of projects.

Each project contains a div.cd-title with the project title and a div.cd-project-info with project description. The project image is set as background-image of the .cd-title::before pseudo-element.

<header> <a href="#0" class="cd-logo"><img src="img/cd-logo.svg"></a> <button class="cd-nav-trigger">Menu<span aria-hidden="true" class="cd-icon"></span></button> </header> <nav class="cd-primary-nav"> <ul> <li class="cd-label">Navigation</li> <li><a href="#0">The team</a></li> <!-- other navigation items here --> </ul> </nav> <!-- .cd-primary-nav --> <div class="cd-projects-container"> <ul> <li class="single-project"> <div class="cd-title"> <h2>Project 1</h2> </div> <!-- .cd-title --> <div class="cd-project-info"> <button class="cd-scroll">Scroll down</button> <div class="content-wrapper"> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quisquam molestias suscipit mollitia vitae ea non ex, dignissimos aperiam minus magni totam sint culpa vel voluptate ipsa sunt repellendus. Ab, magni! </p> <!-- additional project info here --> </div> </div> <!-- .cd-project-info --> </li> <!-- other projects here --> </ul> </div> <!-- .cd-projects-container -->

Adding style

The div.cd-projects-container has a height of 100% and a relative position. The single projects are in absolute position, have a height of 100% and are placed one on top of the others in the top left corner of their wrapper .cd-projects-container .

The second and third projects are then translated along the Y axis of, respectively, one-third and two-thirds of the .cd-projects-container height. This way, only one-third of the viewport height is visible for each project.

.cd-projects-container { height: 100%; position: relative; overflow: hidden; } .cd-projects-container .single-project { position: absolute; top: 0px; left: 0px; height: 100%; width: 100%; transition: transform 0.4s; } .cd-projects-container .single-project:nth-of-type(2) { transform: translateY(33.3333333333%); } .cd-projects-container .single-project:nth-of-type(3) { transform: translateY(66.6666666667%); }

Here's a quick animation that explains the cards positioning:

We then set a height of 33.33% to the .cd-title (one-third of the viewport height ), and a height: 300% to the .cd-title::before pseudo-element (equal to the viewport height).

.cd-title { height: 33.3333333333%; } .cd-title::before { /* background image */ content: ''; position: absolute; top: 0; left: 0; height: 300%; width: 100%; background-position: center center; background-repeat: no-repeat; background-size: cover; } .single-project:nth-of-type(1) .cd-title::before { background-image: url(../img/img-1.jpg); }

When a project is selected, we use the .selected class to assign a translateY(0) to the selected project, while we translate its project siblings to the bottom (translateY(100%)) so that the whole project image is revealed.

.cd-projects-container .single-project.selected { /* selected project */ transform: translateY(0); } .cd-projects-container .single-project.selected ~ li { /* hide siblings projects */ transform: translateY(100%); }

As for the .cd-project-info , it has a height of 100%, an overflow: auto (to be able to scroll it) and is placed in the top-left corner of its .single-project parent. Its ::before pseudo-element is then used to push the div.content-wrapper below the project image.

.cd-project-info { position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: auto; opacity: 0; visibility: hidden; transition: opacity 0.4s, visibility 0.4s; } .cd-project-info::before { /* use to push the .content-wrapper below the intro project image */ content: ''; display: block; height: 100%; width: 100%; pointer-events: none; } .cd-project-info .content-wrapper { position: relative; z-index: 2; padding: 2em 0 3em; background-color: #FFFFFF; } .selected .cd-project-info { opacity: 1; visibility: visible; transition: opacity 0s, visibility 0s; }

As for the full-page navigation, the .cd-primary-nav is placed below the .cd-projects-container ; when the user clicks the .cd-nav-trigger , the single projects are translated to the bottom to reveal the navigation.

.cd-primary-nav { position: absolute; top: 0; left: 0; /* height = (100% - 9%) - 9% is the space taken by the projects when the navigation is open */ height: 91%; width: 100%; overflow: auto; opacity: 0; } .cd-primary-nav ul { transform: translateY(50px); transition: transform 0.4s; } .cd-primary-nav.nav-open { opacity: 1; } .cd-primary-nav.nav-open ul { transform: translateY(0); } .cd-projects-container.nav-open .single-project { box-shadow: 0 0 30px rgba(0, 0, 0, 0.5); transform: translateY(91%); } .cd-projects-container.nav-open .single-project:nth-of-type(2) { transform: translateY(94%); } .cd-projects-container.nav-open .single-project:nth-of-type(3) { transform: translateY(97%); }

Events handling