The real power of CSS Transitions is in allowing a smooth passage from point A to point B. The user is driven through the change, he is not presented with an immediate new result. It's these extra keyframes that make it possible to create pleasent motion-like web experiences.
In this example we take advantage of CSS Transitions and Transformations, and of the background-attachment CSS property to create a "diving-in" effect and reveal additional content for each project.
Image credits: Unsplash.com.
Creating the structure
The HTML structure is an unordered list wrapped inside a <div>
element. Each list item contains a div.cd-title
(title and brief description) and a div.cd-project-info
(additional information). The project image is set as background image of the list item ::after
pseudo-element.
<div class="projects-container">
<ul>
<li>
<div class="cd-title">
<h2>Project 1</h2>
<p>Brief description of the project here</p>
</div> <!-- .cd-title -->
<div class="cd-project-info">
<p><!-- your content here --></p>
</div> <!-- .cd-project-info -->
</li>
<li>
<!-- .... -->
</li>
<!-- .... -->
</ul>
<a href="#0" class="cd-close">Close</a>
<a href="#0" class="cd-scroll">Scroll</a>
</div> <!-- .project-container -->
Adding style
On small devices, each list item has width equal to the viewport width, height equal to one-fourth of the viewport height (4 projects in our demo) and a translateX(-100%)
so that it is moved outside the viewport. After the background-images are loaded (event detected in jQuery), the .is-loaded
class is assigned to the list items (translateX(0)
) to move them back in the viewport. CSS3 transitions has been used to achieve a smooth animation.
.projects-container li {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 25%;
transition: transform 0.4s;
/* on mobile - move items outside the viewport */
transform: translateX(-100%);
}
.projects-container li.is-loaded {
/* move items in the viewport when background images have been loaded */
transform: translateX(0);
}
.projects-container li::after {
/* background image */
background-image: url("../img/img-1-small.jpg");
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
}
.projects-container li:nth-of-type(2) {
top: 25vh;
}
.projects-container li:nth-of-type(2)::after {
background-image: url("../img/img-2-small.jpg");
}
/*other projects*/
When user clicks one project, the .is-full-width
class is assigned to the selected list item: the ::after
pseudo-element height is set to 100vh
(viewport units), while the .cd-project-info
visibility is changed to visible.
.projects-container li.is-full-width {
/* selected item */
top: 0;
height: auto;
z-index: 1;
}
.projects-container li.is-full-width::after {
height: 100vh;
}
.cd-project-info {
visibility: hidden;
opacity: 0;
}
.is-full-width .cd-project-info {
visibility: visible;
opacity: 1;
}
On bigger screens, each list item has height equal to the viewport height and width equal to one-fourth of the viewport width. Besides, the background-attachment
of the list items ::after
pseudo-element has been set to fixed: this way the image is fixed with regard to the viewport (doesn't move while the selected project is scrolled) and covers the entire viewport (background-size: cover).
One note: we have been using the list-item ::before
pseudo-element content property to access (in the js file) the background-image url attribute (this is used to detect if the background-images have been loaded). Therefore, each time you set a new background-image for the ::after
pseudo-element, you need to update the content attribute of the ::before
pseudo element as well.
.projects-container li::after {
background-image: url("../img/img-1-small.jpg");
}
.projects-container li::before {
/* never visible - this is used in jQuery to detect if the background image has been loaded */
content: 'img/img-1-small.jpg';
display: none;
}
@media only screen and (min-width: 1024px) {
.projects-container li:first-of-type::after {
background-image: url("../img/img-1-large.jpg");
}
.projects-container li:first-of-type::before {
content: 'img/img-1-large.jpg';
}
}
/*other projects*/
Events handling
We used jQuery to detect when project background-images are loaded: as soon as they are, the loop function showCaption()
is called to assign the .is-loaded
class to each list item.
Besides, the click event on the .cd-close
and list item elements is detected to expand/close a project.