How to Animate a “Full-Screen Section to Fixed Header” on Page Scroll
In this new tutorial, we’ll start with a full-screen section with a heading and learn how to animate it to a fixed page header on scroll. This type of effect is ideal for layouts with hero sections like a landing or a single post page.
What We’re Building
Here’s what we’re going to create (scroll to test the behavior, or check out the full-screen version):
1. Begin With the Page Markup (HTML)
We’ll define two sections:
- The first section will be full-screen and contain a heading and page header. By default, only the heading will appear. Inside the header, we’ll position the logo and menu.
- The second section will include some dummy content. Below this section, we can have many more.
Here’s the markup:
1 |
<section class="first"> |
2 |
<h1>Full-Screen Section to Fixed Header</h1> |
3 |
<header class="page-header"> |
4 |
<nav class="nav"> |
5 |
<a href="" class="logo-wrapper uk-flex uk-flex-middle"> |
6 |
<img width="178" height="38" src="forecastr-logo-white.svg" alt="forecastr logo"> |
7 |
</a>
|
8 |
<ul>
|
9 |
<li>
|
10 |
<a href="">Home</a> |
11 |
</li>
|
12 |
<li>
|
13 |
<a href="">About</a> |
14 |
</li>
|
15 |
<li>
|
16 |
<a href="">Contact</a> |
17 |
</li>
|
18 |
</ul>
|
19 |
</nav>
|
20 |
</header>
|
21 |
</section>
|
22 |
<section class="second"> |
23 |
<div class="container">...</div> |
24 |
</section>
|
2. Set the Main Styles (CSS)
Let’s take note of the main styles—you can see all of them by clicking on the CSS tab of the demo project:
- The first section will be a fixed-positioned element and full-screen. Additionally, it’ll behave as a grid container with vertically centered content. You can alter the height of the first section through the
hero-height
CSS variable. For instance, if you want this section to cover only half of the screen, change the CSS value from 100vh to 50vh.



- As we’ve done many times in the past, we’ll use CSS Grid to stack the heading and page header. To do so, we’ll give them
grid-area: 1/1
. This is the shorthand of settinggrid-column: 1
andgrid-row: 1
. - As we’ve already discussed, the page header will initially be invisible. To do so, we’ll give it
opacity: 0
andvisibility: hidden
. The last rule is required to prevent click events. - The
nav
element will be a flex container with vertically centered content. On large screens, the logo will sit on the left side and the menu on the right side. On small screens (≤600px), we’ll stack them.






- The second section should sit underneath the first one. To accomplish this, we’ll give it
padding-top: 110vh
. This value matches the height of the first section (100vh) plus 10vh to create the gap between the text and the section’s top edge (equal to the bottom padding). If we don’t give it anypadding-top
value, the second section will sit behind the first section at the top of the page. Remember that the first section is a fixed-positioned element, and thus it’s removed from the normal document flow.



Here are the main styles:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.first { |
4 |
position: fixed; |
5 |
top: 0; |
6 |
left: 0; |
7 |
width: 100%; |
8 |
height: var(--hero-height); |
9 |
background: url(mountain-full.jpg) no-repeat center / cover; |
10 |
background-blend-mode: multiply; |
11 |
display: grid; |
12 |
place-items: center; |
13 |
z-index: 1; |
14 |
}
|
15 |
|
16 |
section, |
17 |
.first > * { |
18 |
transition: all 0.6s; |
19 |
}
|
20 |
|
21 |
.first > * { |
22 |
grid-area: 1/1; |
23 |
padding: 0 20px; |
24 |
width: 100%; |
25 |
}
|
26 |
|
27 |
.first .page-header { |
28 |
opacity: 0; |
29 |
visibility: hidden; |
30 |
}
|
31 |
|
32 |
.first .nav { |
33 |
display: flex; |
34 |
flex-wrap: wrap; |
35 |
align-items: center; |
36 |
justify-content: space-between; |
37 |
}
|
38 |
|
39 |
.first .nav ul { |
40 |
display: flex; |
41 |
list-style: none; |
42 |
}
|
43 |
|
44 |
.second { |
45 |
padding: calc(var(--hero-height) + 10vh) 0 10vh 0; |
46 |
}
|
47 |
|
48 |
@media (max-width: 600px) { |
49 |
.first .nav { |
50 |
flex-direction: column; |
51 |
justify-content: center; |
52 |
}
|
53 |
}
|
3. Animate on Scroll (JavaScript)
As we start scrolling down the page, we‘ll toggle the is-sticky
class of the body
element.
At that point, the following actions will take place:
- We’ll smoothly decrease the height of the first section from 100vh to 90px. Again, you can alter the value of the sticky header height through the
sticky-header-height
CSS variable. In addition, we’ll assign it a background color that will blend with its image and result in a purple (darker) image. Notice in the CSS above that this section hasbackground-blend-mode: multiply
. - We’ll smoothly reveal the page header and hide the heading.
- We’ll smoothly decrease the
padding-top
value of the second section from 110vh to 90px + 10vh. - We’ll sync the animations of all elements. Notice in the CSS above that their animation duration will be 0.6s.
- We’ll define the
::before
pseudo-element of thebody
element that will append a box shadow to the page header.



Here’s the required JavaScript code:
1 |
const body = document.body; |
2 |
const toggleClass = "is-sticky"; |
3 |
|
4 |
window.addEventListener("scroll", () => { |
5 |
const currentScroll = window.pageYOffset; |
6 |
if (currentScroll > 0) { |
7 |
body.classList.add(toggleClass); |
8 |
} else { |
9 |
body.classList.remove(toggleClass); |
10 |
}
|
11 |
});
|
And the associated styles:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.is-sticky::before { |
4 |
content: ""; |
5 |
position: fixed; |
6 |
top: 65px; |
7 |
left: 0; |
8 |
width: 100%; |
9 |
height: 15px; |
10 |
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.7); |
11 |
}
|
12 |
|
13 |
.is-sticky .first { |
14 |
height: var(--sticky-header-height); |
15 |
background-color: var(--purple); |
16 |
}
|
17 |
|
18 |
.is-sticky .first h1 { |
19 |
opacity: 0; |
20 |
visibility: hidden; |
21 |
}
|
22 |
|
23 |
.is-sticky .first .page-header { |
24 |
opacity: 1; |
25 |
visibility: visible; |
26 |
}
|
27 |
|
28 |
.is-sticky .second { |
29 |
padding-top: calc(var(--sticky-header-height) + 10vh); |
30 |
}
|
Conclusion
Another scrolling tutorial came to an end, folks! During this exercise, we covered how to transform a full-screen section to a sticky header on a page scroll by toggling just one class. Hopefully, this implementation was clear enough and inspired you to utilize it on an upcoming project! Of course, you can build on it and add some extra features like adding parallax functionality on scroll by changing perhaps the first section’s background position.
Let’s look again at our creation:
Last but not least, don’t forget to look at the Tuts+ library for more header effects tutorials on scroll.
As always, thanks a lot for reading!