I have created this page and when the “got it” button on the top panel is clicked, the panel will slide up and disappear while the page will also move up to cover the panel space. I have noticed that when the animation plays, it is not that smooth as white space can be seen in the split second that the page is sliding up. Is there any way to improve the animation to remove the split second in which the white space can be seen?
The code below is only a part of the complete one and won’t be working properly like this one here as it is for reference only. Only pure HTML, CSS and Javascript are used.
const panel = document.getElementById("panel"); const page = document.getElementById("main-page"); function closePanel() { panel.classList.add("panel-animation"); page.classList.add("page-animation"); setTimeout((panel.style.transform = "translateY(-1000px)"), 1000); setTimeout((page.style.top = "0"), 1000); }
* { margin: 0; padding: 0; } .panel { height: 10%; width: 100%; } .notif-panel { z-index: 1; position: fixed; display: grid; height: 10%; width: 100%; grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "panel-content"; } .panel-content { display: flex; height: 100%; background-color: var(--smokeGrey); justify-content: center; align-items: center; } .panel-text { display: inline; } .cookie, .privacy, .tos { text-decoration: none; } .panel-button { display: inline; margin-left: 40px; padding: 10px 16px; background-color: #007bc1; color: white; border: none; border-radius: 2px; } .panel-button:hover { background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); } @media (max-width: 675px) { .notif-panel { height: 10%; } .panel-content { padding: 0 5px; } .panel-text { padding-right: 15px; } .panel-button { font-size: 17px; margin: 0; } } @media (max-width: 605px) { .notif-panel { height: 15%; } .panel-content { align-items: left; display: block; padding: 10px; font-size: 18px; } .panel-text { padding-bottom: 10px; display: block; } .panel-button { font-size: 17px; margin: 0; } } .main-page { position: absolute; width: 100%; } .hero { display: flex; justify-content: center; align-items: center; background-image: linear-gradient( rgba(0, 74, 117, 0.52), rgba(0, 74, 117, 0.52) ), url("assets/work-desk__dustin-lee.jpg"); height: 600px; } .logo { height: 50px; width: 50px; position: absolute; left: 30px; top: 25px; } .hero-content { margin: 0; text-align: center; color: white; } .name { font-size: 30px; } .contact-text { margin-top: 8px; padding-bottom: 25px; } .contact-button { font-weight: bold; color: white; background-color: transparent; border: white 2px solid; padding: 12px 15px; border-radius: 2px; } .contact-button:hover { color: var(--blue); background-color: white; } .panel-animation { animation: animation1 1s ease-in-out; } .page-animation { animation: animation2 0.2s ease-in-out; } @keyframes animation1 { from { transform: translateY(0); } to { transform: translateY(-500px); } } @keyframes animation2 { 0% { top: 10%; } 100% { top: 0; } }
<div class="panel" id="panel"> <div class="notif-panel" id="notifPanel"> <div class="panel-content"> <p class="panel-text"> By accessing and using this website, you acknowledge that you have read and <span class="brCust" id="brCust"></span>understand our <a class="cookie" href="#">Cookie Policy</a>, <a class="privacy" href="#">Privacy Policy</a>, and our <a class="tos" href="#">Terms of Service</a>. </p> <button class="panel-button" onclick="closePanel()">Got it</button> </div> </div> </div> <div class="main-page" id="main-page"> <section class="hero" id="hero"> <img src="assets/y-logo-white.png" class="logo" /> <div class="hero-content"> <p class="name">Hello! I'm Dylan Anderton</p> <h2 class="slogan">Consult, Design, and Develop Websites</h2> <p class="contact-text"> Have something great in mind? Feel free to contact me.<br /> I'll help you to make it happen. </p> <button class="contact-button">LET'S MAKE CONTACT</button> </div> </section> </div>
Advertisement
Answer
The problem
Your #panel
is a sibling of main
, but you want #panel
to be fixed on the upper side of the viewport even when scrolling: You solved that with position: fixed
.
But using position: fixed
means that #panel
will only be perfectly layed out on certain screen sizes. On any other screen size, and it may overlap main
‘s content. Also, it may cause visual jitter when animating (see section “Using position: sticky
“).
You also want #panel
to slide out when requested, and main
to take up the then-empty space: You solved that slide-out and “slide into place” with 2 different animations; #panel
moves out by a lot, but main
moves only enough to take up the empty space. Additionally, they move for a different duration.
The different distances and durations of the animations cause them to be not in sync with each other. Ideally both should move by the same amount and in the same time to make it look smooth.
Overexaggerated example of the issue:
section:hover #banner { top: -400px; transition-duration: 1s; } section:hover #content { top: -80px; transition-duration: 2s; } /* PRESENTATIONAL STYLING */ body {font-family:sans-serif} section { width: fit-content; display: grid; grid-template-columns: repeat(2, 1fr); } #viewport { border: 1px solid black; grid-row: 1/1; grid-column: 1/-1; } #page { grid-row: 1/1; grid-column: 2/2; } #banner, #content { z-index: -1; --size: 80px; position: relative; top: 0; width: var(--size); height: var(--size); color: white; transition-property: top; } #banner {background-color:firebrick} #content {background-color:darkblue}
<section> <div id="viewport">Viewport</div> <div id="page"> <div id="banner">Banner</div> <div id="content">Content</div> </div> </section>
Solutions
Using position: sticky
Using position: sticky
on #panel
means that when scrolled, #panel
will be on top of main
‘s content, as with position: fixed
. But unlike fixed
, sticky
will still place the element in the flow, which means it will not overlap main
when not scrolled. No magic numbers or JS needed for this!
And when wanting to hide #panel
, we can shrink it and slide it out of the viewport, so that main
can take up the empty space:
const panel = document.getElementById('panel'); const page = document.getElementById('main-page'); function closePanel(){ const {bottom} = panel.getBoundingClientRect(); panel.style.marginBottom = 0 + "px"; panel.addEventListener("transitionend", () => panel.replaceWith()); // marginBottom shrinks the element; // translateY() slides it out. setTimeout(() => { panel.style.marginBottom = `${-bottom}px`; panel.style.transform = `translateY(${-bottom}px)`; }, 0); }
.panel{ z-index: 1; position: sticky; top: 0; transition: margin-bottom 1s, transform 1s; } .hero { position: relative; } *{ margin:0; padding:0; } html {--smokeGrey:#eee} .notif-panel{ display:grid; grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "panel-content"; } .panel-content{ display:flex; justify-content:center; align-items:center; background-color:var(--smokeGrey); } .panel-text{ display:inline; } .cookie, .privacy, .tos{ text-decoration:none; } .panel-button{ display:inline; padding: 10px 16px; background-color: #007bc1; color: white; border: none; border-radius: 2px; } .panel-button:hover{ background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); } @media(max-width:675px){ .notif-panel{ height: 10%; } .panel-content{ padding: 0 5px; } .panel-text{ padding-right: 15px; } .panel-button{ font-size: 17px; } } @media(max-width:605px){ .notif-panel{ height: 15%; } .panel-content{ align-items: left; display: block; padding: 10px; font-size: 18px; } .panel-text{ padding-bottom: 10px; display: block; } .panel-button{ font-size: 17px; } } .main-page{ width: 100%; } .hero{ display: flex; justify-content: center; align-items: center; background-image: linear-gradient(rgba(0,74,117,.52),rgba(0,74,117,.52)), url('assets/work-desk__dustin-lee.jpg'); height: 600px; } .logo{ height: 50px; width: 50px; position: absolute; left: 30px; top: 25px; } .hero-content{ margin:0; text-align: center; color: white; } .name{ font-size: 30px; } .contact-text{ margin-top: 8px; padding-bottom: 25px; } .contact-button{ font-weight: bold; color: white; background-color: transparent; border: white 2px solid; padding: 12px 15px; border-radius: 2px; } .contact-button:hover{ color: var(--blue); background-color: white; }
<div class="panel" id="panel"> <div class="notif-panel" id="notifPanel"> <div class="panel-content"> <p class="panel-text">By accessing and using this website, you acknowledge that you have read and <span class="brCust" id="brCust"></span>understand our <a class="cookie" href="#">Cookie Policy</a>, <a class="privacy" href="#">Privacy Policy</a>, and our <a class="tos" href="#">Terms of Service</a>. </p> <button class="panel-button" onclick="closePanel()">Got it</button> </div> </div> </div> <div class="main-page" id="main-page"> <section class="hero" id="hero"> <img src="assets/y-logo-white.png" class="logo"> <div class="hero-content"> <p class="name">Hello! I'm Dylan Anderton</p> <h2 class="slogan">Consult, Design, and Develop Websites</h2> <p class="contact-text">Have something great in mind? Feel free to contact me.<br> I'll help you to make it happen.</p> <button class="contact-button">LET'S MAKE CONTACT</button> </div> </section> </div>
Did you notice that when scrolled and hiding #panel
, main
won’t jitter? Using position: sticky
is the least visually jarring, which makes it my preferred method to use. Compare with the visual jitter of the other examples, or even your own webpage on smaller screen sizes!
Using transitions
To move both elements (#panel
and main
) by the same amount, we have to find the y-position of #panel
. This can be done with Element.getBoundingClientRect()
.
Then, we want to move both elements upwards:
- Move bottom of
#panel
from found y-position to 0. - Move top of
main
from found y-position to 0.
This can be done as follows:
const panel = document.getElementById('panel'); const page = document.getElementById('main-page'); function closePanel(){ const rectPanel = panel.getBoundingClientRect(); panel.style.top = 0; page.style.top = rectPanel.bottom + "px"; panel.style.transition = "top 1s ease-in-out"; page.style.transition = "top 1s ease-in-out"; // Don't forget to remove it from the DOM panel.addEventListener("transitionend", () => panel.replaceWith()); setTimeout(() => { panel.style.top = -rectPanel.bottom + "px"; page.style.top = 0; }, 0); }
/* I had to move position:fixed to this top-level element */ .panel{ z-index: 1; position: fixed; } *{ margin:0; padding:0; } html {--smokeGrey:#eee} .notif-panel{ display:grid; grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "panel-content"; } .panel-content{ display:flex; justify-content:center; align-items:center; background-color:var(--smokeGrey); } .panel-text{ display:inline; } .cookie, .privacy, .tos{ text-decoration:none; } .panel-button{ display:inline; padding: 10px 16px; background-color: #007bc1; color: white; border: none; border-radius: 2px; } .panel-button:hover{ background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); } @media(max-width:675px){ .notif-panel{ height: 10%; } .panel-content{ padding: 0 5px; } .panel-text{ padding-right: 15px; } .panel-button{ font-size: 17px; } } @media(max-width:605px){ .notif-panel{ height: 15%; } .panel-content{ align-items: left; display: block; padding: 10px; font-size: 18px; } .panel-text{ padding-bottom: 10px; display: block; } .panel-button{ font-size: 17px; } } .main-page{ position: absolute; width: 100%; } .hero{ display: flex; justify-content: center; align-items: center; background-image: linear-gradient(rgba(0,74,117,.52),rgba(0,74,117,.52)), url('assets/work-desk__dustin-lee.jpg'); height: 600px; } .logo{ height: 50px; width: 50px; position: absolute; left: 30px; top: 25px; } .hero-content{ margin:0; text-align: center; color: white; } .name{ font-size: 30px; } .contact-text{ margin-top: 8px; padding-bottom: 25px; } .contact-button{ font-weight: bold; color: white; background-color: transparent; border: white 2px solid; padding: 12px 15px; border-radius: 2px; } .contact-button:hover{ color: var(--blue); background-color: white; }
<div class="panel" id="panel"> <div class="notif-panel" id="notifPanel"> <div class="panel-content"> <p class="panel-text">By accessing and using this website, you acknowledge that you have read and <span class="brCust" id="brCust"></span>understand our <a class="cookie" href="#">Cookie Policy</a>, <a class="privacy" href="#">Privacy Policy</a>, and our <a class="tos" href="#">Terms of Service</a>. </p> <button class="panel-button" onclick="closePanel()">Got it</button> </div> </div> </div> <div class="main-page" id="main-page"> <section class="hero" id="hero"> <img src="assets/y-logo-white.png" class="logo"> <div class="hero-content"> <p class="name">Hello! I'm Dylan Anderton</p> <h2 class="slogan">Consult, Design, and Develop Websites</h2> <p class="contact-text">Have something great in mind? Feel free to contact me.<br> I'll help you to make it happen.</p> <button class="contact-button">LET'S MAKE CONTACT</button> </div> </section> </div>
Using Web Animations API
Same steps as when using transitions, but implemented via the Web Animations API:
const panel = document.getElementById('panel'); const page = document.getElementById('main-page'); function closePanel(){ const {bottom} = panel.getBoundingClientRect(); const duration = 1000; // ms panel.animate( { top: [0, `${-bottom}px`] }, { duration } ).finished.then(() => { panel.replaceWith(); // Don't forget to remove element from DOM! }); page.animate( { top: [`${bottom}px`, 0] }, { duration } ); }
/* I had to move position:fixed to this top-level element */ .panel{ z-index: 1; position: fixed; } *{ margin:0; padding:0; } html {--smokeGrey:#eee} .notif-panel{ display:grid; grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "panel-content"; } .panel-content{ display:flex; justify-content:center; align-items:center; background-color:var(--smokeGrey); } .panel-text{ display:inline; } .cookie, .privacy, .tos{ text-decoration:none; } .panel-button{ display:inline; padding: 10px 16px; background-color: #007bc1; color: white; border: none; border-radius: 2px; } .panel-button:hover{ background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); } @media(max-width:675px){ .notif-panel{ height: 10%; } .panel-content{ padding: 0 5px; } .panel-text{ padding-right: 15px; } .panel-button{ font-size: 17px; } } @media(max-width:605px){ .notif-panel{ height: 15%; } .panel-content{ align-items: left; display: block; padding: 10px; font-size: 18px; } .panel-text{ padding-bottom: 10px; display: block; } .panel-button{ font-size: 17px; } } .main-page{ position: absolute; width: 100%; } .hero{ display: flex; justify-content: center; align-items: center; background-image: linear-gradient(rgba(0,74,117,.52),rgba(0,74,117,.52)), url('assets/work-desk__dustin-lee.jpg'); height: 600px; } .logo{ height: 50px; width: 50px; position: absolute; left: 30px; top: 25px; } .hero-content{ margin:0; text-align: center; color: white; } .name{ font-size: 30px; } .contact-text{ margin-top: 8px; padding-bottom: 25px; } .contact-button{ font-weight: bold; color: white; background-color: transparent; border: white 2px solid; padding: 12px 15px; border-radius: 2px; } .contact-button:hover{ color: var(--blue); background-color: white; }
<div class="panel" id="panel"> <div class="notif-panel" id="notifPanel"> <div class="panel-content"> <p class="panel-text">By accessing and using this website, you acknowledge that you have read and <span class="brCust" id="brCust"></span>understand our <a class="cookie" href="#">Cookie Policy</a>, <a class="privacy" href="#">Privacy Policy</a>, and our <a class="tos" href="#">Terms of Service</a>. </p> <button class="panel-button" onclick="closePanel()">Got it</button> </div> </div> </div> <div class="main-page" id="main-page"> <section class="hero" id="hero"> <img src="assets/y-logo-white.png" class="logo"> <div class="hero-content"> <p class="name">Hello! I'm Dylan Anderton</p> <h2 class="slogan">Consult, Design, and Develop Websites</h2> <p class="contact-text">Have something great in mind? Feel free to contact me.<br> I'll help you to make it happen.</p> <button class="contact-button">LET'S MAKE CONTACT</button> </div> </section> </div>
Notice that I used a keyframe format where I specify the values of a property in steps.