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
We used jQuery to listen to the click events on the .cd-nav-trigger
and .single-project
and to add/remove classes accordingly.