Go to homepage

Projects /

Project Cards Template

A portfolio template with expandable projects and a full-page navigation inspired by Primer app.

Project Cards Template
Check our new component library →

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:

projects-template-explained

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.

Project duplicated

Project created

Globals imported

There was an error while trying to export your project. Please try again or contact us.