Build a Grid-to-Full-Screen Image Animation With CSS (and a Touch of JavaScript)

In this tutorial, I’ll show you a motion effect where a split-screen image grid layout will turn into a full-screen image.

1. Begin With the HTML Markup

We’ll define three sections:

  • The first and third sections won’t have any real value, but we’ll utilize them to prove that the effect can work inside any page content.
  • The second important section will include four Unsplash images along with their captions.

Here’s the markup:

1
<section class="section-text">...</section>
2

3
<!--IMPORTANT SECTION-->
4
<section>
5
  <div class="grid">
6
    <div class="col col-1">
7
      <figure>
8
        <img src="castle1-unsplash.jpg" alt="Lake Bled, Bled, Slovenia">
9
        <figcaption>Lake Bled, Bled, Slovenia</figcaption>
10
      </figure>
11
    </div>
12
    <div class="col col-2">
13
      <figure>
14
        <img src="castle2-unsplash.jpg" alt="Schloss Neuschwanstein, Neuschwansteinstraße, Schwangau, Deutschland">
15
        <figcaption>Schloss Neuschwanstein, Schwangau, Deutschland</figcaption>
16
      </figure>
17
    </div>
18
    <div class="col col-3">
19
      <figure>
20
        <img src="castle3-unsplash.jpg" alt="Isola di Capo Passero, Italia">
21
        <figcaption>Isola di Capo Passero, Italia</figcaption>
22
      </figure>
23
    </div>
24
    <div class="col col-4">
25
      <figure>
26
        <img src="castle4-unsplash.jpg" alt="Eilean Donan Castle, Dornie, United Kingdom">
27
        <figcaption>Eilean Donan Castle, Dornie, United Kingdom</figcaption>
28
      </figure>
29
    </div>
30
  </div>
31
</section>
32

33
<section class="section-text">...</section>

2. Add the CSS

Let’s focus on the image grid styles—you can see all of them by clicking on the CSS tab of the demo project.

  • The image gallery will cover the entire screen.
  • Each column (grid item) will sit on top of the other. That said, by default, the last image will appear. 

The page layout with the full-screen imageThe page layout with the full-screen imageThe page layout with the full-screen image

  • The clip-path property will help us create the two-by-two grid that initially appears. The idea is that each column will cover one-fourth of the viewport size.  

The page layout with the grid of imagesThe page layout with the grid of imagesThe page layout with the grid of images

  • Optionally, we’ll use the ::before and ::after pseudo-elements of the .grid element to create some dividers between its columns.
The grid dividersThe grid dividersThe grid dividers
  • All figure elements will be absolutely positioned and their position will depend on the top and left values.
  • We’ll use the object-fit: cover property value to place the images inside their parent. This way they will perfectly fit inside it without losing their aspect ratio. Plus, the object-position: top property value will move the image on top of the wrapper. 
  • By default, all image captions won’t appear.

These are all the main styles:

1
.grid {
2
  position: relative;
3
  display: grid;
4
  height: 100vh;
5
}
6

7
.grid::before,
8
.grid::after {
9
  content: "";
10
  position: absolute;
11
  background: #333;
12
  z-index: 1;
13
}
14

15
.grid::before {
16
  top: 0;
17
  left: 50%;
18
  width: 1px;
19
  height: 100%;
20
}
21

22
.grid::after {
23
  top: 50%;
24
  left: 0;
25
  width: 100%;
26
  height: 1px;
27
}
28

29
.grid .col {
30
  position: relative;
31
  grid-area: 1/1;
32
  cursor: pointer;
33
  transition: clip-path 0.5s;
34
}
35

36
.grid .col-1 {
37
  clip-path: inset(0 50% 50% 0);
38
}
39

40
.grid .col-2 {
41
  clip-path: inset(0 0 50% 50%);
42
}
43

44
.grid .col-3 {
45
  clip-path: inset(50% 50% 0 0);
46
}
47

48
.grid .col-4 {
49
  clip-path: inset(50% 0 0 50%);
50
}
51

52
.grid .col figure {
53
  position: absolute;
54
  top: 0;
55
  left: 0;
56
  width: 50%;
57
  height: 50%;
58
  margin: 0;
59
  transition: all 0.5s;
60
}
61

62
.grid .col-2 figure {
63
  left: 50%;
64
}
65

66
.grid .col-3 figure {
67
  top: 50%;
68
}
69

70
.grid .col-4 figure {
71
  top: 50%;
72
  left: 50%;
73
}
74

75
.grid .col img {
76
  width: 100%;
77
  height: 100%;
78
  object-fit: cover;
79
  object-position: top;
80
}
81

82
.grid .col figcaption {
83
  position: absolute;
84
  bottom: 20px;
85
  left: 50%;
86
  transform: translateX(-50%);
87
  padding: 6px 12px;
88
  border-radius: 5px;
89
  width: max-content;
90
  max-width: 95%;
91
  line-height: 1;
92
  color: white;
93
  background: #ff0054;
94
  text-align: center;
95
  opacity: 0;
96
  transition-property: opacity;
97
}

3. Apply the JavaScript

Each time we click on a column, we’ll toggle its animate-col class. At that point, the grid of images will morph into a full-screen image.

One important thing to note is that each time an image scales, it should sit on top of all the others. To implement this behavior, we’ll do something simple: we’ll utilize the transitionstart and transitionend events to apply a higher z-index to the target column when it scales in and restore its default stack order when it scales down. Any z-index higher than one will work, and place the full-screen image above the grid dividers.

Another notable thing is that the caption should smoothly appear after the completion of the full-screen image animation and disappear without animation when we click on the column. We follow this approach to prevent the awkward text wrapping on different lines on small screens. To understand it, make the caption always visible and run the animation from a small screen.

Here’s the required JavaScript code:

1
const cols = document.querySelectorAll(".grid .col");
2
const CLASS1 = "animate-col";
3
const CLASS2 = "animate-caption";
4

5
cols.forEach(function (col) {
6
  col.addEventListener("click", function () {
7
    this.classList.toggle(CLASS1);
8
    if (this.classList.contains(CLASS2)) {
9
      this.classList.remove(CLASS2);
10
    }
11
  });
12

13
  col.addEventListener("transitionstart", function () {
14
    this.style.zIndex = 2;
15
  });
16

17
  col.addEventListener("transitionend", function () {
18
    if (col.classList.contains(CLASS1)) {
19
      col.classList.add(CLASS2);
20
    } else {
21
      col.style.zIndex = "auto";
22
    }
23
  });
24
});

And the associated styles:

1
.grid .col {
2
  transition: clip-path 0.5s;
3
}
4

5
.grid .col figure {
6
  transition: all 0.5s;
7
}
8

9
.grid .col figcaption {
10
  transition-property: opacity;
11
}
12

13
.grid .col.animate-col {
14
  clip-path: inset(0);
15
}
16

17
.grid .col.animate-col figure {
18
  top: 0;
19
  left: 0;
20
  width: 100%;
21
  height: 100%;
22
}
23

24
.grid .col.animate-caption figcaption {
25
  opacity: 1;
26
  transition-duration: 0.3s;
27
}

Conclusion

In this short tutorial, we learned how to take advantage of the clip-path property and turn an image grid into a full-screen image. This gives us endless possibilities for creating appealing motion effects that behave like single-page applications delivering AJAX-driven content with just a little CSS and JavaScript!

Let’s look once more at our creation:

As always, thanks a lot for reading!

More Clip-path Tutorials on Tuts+

Master the CSS clip-path property with these tutorials: