Scroll Animation, Elementor, GSAP
How to create a Shrinking Sticky Header in Elementor with GSAP
SHARE
Setup in Elementor
First we need to setup a wrapper Container, because GSAP cannot animate which have the position “fixed”. And we need the position “fixed” for our sticky header.
Setup Wrapper Container:
- Full width, Width 100%, no gaps
- HTML Tag: Header
- No margin, no padding
Setup Sticky Header Container:
- Boxed or Full width, I used Boxed in the tutorial demo
- (width 100% or boxed 1200px)
- height: don’t enter any height value, otherwise the animation will stop working!
- Direction: Row horizontal
- Justify Content: space between
- Align Items: center
- background: rgba(255, 255, 255, 0.2); (for glassmorphism effect, but you can enter any other color whether here or in the CSS Code as a starting color
- Wrap: No wrap
- Position: fixed
- z-index: 9999
- padding: top and bottom 0, left and right 20px
- CSS Class: lux-header
Setup Logo Image:
- Width: clamp(3.75rem, 3.205vw + 3.029rem, 6.875rem)
- Upload your Logo
- CSS Class: lux-logo
Setup Button:
- CSS Class: lux-button
- Give it some Text like “Get started” or other Call to Action
- Give it a background Color, border radius and border (if widhed)
GSAP Code
Add this into your HTML widget
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
<script>
gsap.registerPlugin(ScrollTrigger);
// Define the elements
const header = document.querySelector(".lux-header");
const logo = document.querySelector(".lux-logo img");
const button = document.querySelector(".lux-button");
const toggle = document.querySelector(".elementor-menu-toggle");
const menuLinks = document.querySelectorAll(".lux-navigation a");
const headerNav = document.querySelector(".lux-navigation"); // Navigation container
// Check if the key elements exist on the page
if (header && logo && button) {
ScrollTrigger.getAll().forEach((trigger) => trigger.kill());
// Create a timeline with ScrollTrigger
let tl = gsap.timeline({
scrollTrigger: {
trigger: header, // The header triggers the animation
start: "top top", // Starts when the top of the header is 50px above the viewport
end: "+=300", // Ends after the user scrolls 300px
scrub: 1, // Smoothly links the animation to the scroll position
invalidateOnRefresh: true,
},
});
// Use a fromTo animation for the height
tl.fromTo(
header,
{ height: "clamp(6.25rem, 3.205vw + 5.529rem, 9.375rem)" }, // Initial dynamic height, must match with CSS
{
height: "clamp(3.75rem, 3.205vw + 3.029rem, 6.875rem)", // Target height
backgroundColor: "rgba(255, 255, 255, 0.3)", // Change background color
backdropFilter: "blur(15px)", // Apply blur effect
ease: "none", // No additional easing
}
);
// Scale down the button
tl.to(button, {
scale: 0.8, // Shrinks the button slightly
transformOrigin: "center center", // Shrinks from the center
ease: "none", // Smooth transition without extra easing
}, "<"); // Synchronize this animation with the previous one
// Scale down the logo
tl.to(logo, {
scale: 0.7, // Shrinks the logo
transformOrigin: "center center", // Shrinks from the center
ease: "none", // Smooth scaling
}, "<"); // Synchronize with the previous animation
// Scale down the mobile menu toggle
tl.to(toggle, {
scale: 0.8, // Shrinks the toggle button slightly
transformOrigin: "center center", // Shrinks from the center
ease: "none",
}, "<"); // Synchronize with the previous animation
// Scale down the navigation container
tl.to(headerNav, {
scale: 0.8, // Shrinks the entire navigation container
transformOrigin: "center center", // Shrinks from the center
ease: "none",
}, "<"); // Synchronize with the previous animation
// Change the color of each menu link
menuLinks.forEach((link) => {
tl.to(link, {
color: "#FFF", // Changes the link color to white
ease: "power1.inOut", // Smooth easing for the color change
}, "<"); // Synchronize with the previous animation
});
} else {
// Log an error if one or more selectors are missing
console.error("One or more key elements are missing from the page.");
}
window.addEventListener('resize', () => {
ScrollTrigger.refresh();
});
</script>
CSS Code
Add this into the wrapper Container or better in the wordpress customizer or in your WordPress CSS
.lux-header {
position: fixed; /* Sticks the header to the top of the viewport */
top: 0; /* Aligns the header at the very top */
width: 100%; /* Full width of the viewport */
height: clamp(6.25rem, 3.205vw + 5.529rem, 9.375rem); /* Responsive height using CSS clamp */
background: rgba(255, 255, 255, 0.2); /* Transparent white background */
backdrop-filter: blur(10px); /* Applies a blur effect for a frosted glass look */
display: flex; /* Flexbox layout for alignment */
align-items: center; /* Centers items vertically */
justify-content: space-between; /* Spaces items evenly */
padding: 0 20px; /* Adds padding to the left and right */
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* Subtle shadow for depth */
z-index: 1000; /* Ensures the header stays above other elements */
transition: all 0.3s ease; /* Smooth transition for all properties */
will-change: transform; /* Optimizes for transform animations */
}
.lux-logo img {
width: clamp(6.25rem, 3.205vw + 5.529rem, 9.375rem);
height: auto; /* Maintains aspect ratio */
object-fit: contain; /* Ensures the image fits within its container */
display: block; /* Removes extra spacing from inline images */
transition: all 0.3s ease; /* Smooth transition for all properties */
transform-origin: center center; /* Scales from the center */
will-change: transform; /* Optimizes for transform animations */
}
.lux-navigation {
display: flex; /* Flexbox layout for navigation links */
justify-content: center; /* Centers links horizontally */
align-items: center; /* Aligns links vertically */
width: 100%; /* Full width for alignment */
position: relative; /* Allows positioning of child elements */
transform-origin: center center; /* Scaling from the center */
transition: all 0.3s ease; /* Smooth transition */
}
.lux-navigation a {
text-decoration: none; /* Removes underline from links */
font-size: 1rem; /* Default font size */
margin: 0px; /* Spacing between links */
transition: font-size 0.3s ease, color 0.3s ease; /* Smooth transitions for size and color */
will-change: transform, color; /* Optimizes for these properties */
text-align: center; /* Centers text inside the links */
}
.elementor-menu-toggle {
align-self: center; /* Vertically centers the toggle */
font-size: 35px; /* Default font size */
cursor: pointer; /* Changes cursor to pointer */
transition: transform 0.3s ease; /* Smooth transition for scaling */
transform-origin: center center; /* Scales from the center */
}
.lux-button {
box-sizing: border-box; /* Includes padding and border in width/height */
display: inline-flex; /* Flexbox for alignment */
justify-content: center; /* Centers content horizontally */
align-items: center; /* Centers content vertically */
transform-origin: center center; /* Scales from the center */
transition: transform 0.3s ease; /* Smooth scaling */
}
.lux-button a {
display: inline-block; /* Allows padding and width adjustments */
text-align: center; /* Centers text inside the button */
padding: 10px 25px; /* Adds padding inside the button */
white-space: nowrap; /* Prevents text wrapping */
line-height: 1.5; /* Improves readability */
transition: all 0.3s ease; /* Smooth transition for all properties */
}
.lux-icon {
width: 30px; /* Default width for the icon */
height: 2px; /* Height for the bars */
background: #333; /* Dark gray color for the bars */
display: block; /* Displays the icon as a block element */
margin: 6px 0; /* Spacing between bars */
transition: all 0.3s ease; /* Smooth transition */
}
/* Responsive adjustments */
@media (max-width: 1024px) {
.lux-logo img {
/* Adjust width for tablets */
transition: all 0.3s ease; /* Smooth transition */
transform-origin: center center; /* Scaling from the center */
}
.elementor-menu-toggle {
font-size: 30px; /* Smaller toggle size for tablets */
transition: all 0.3s ease; /* Smooth transition */
}
.lux-navigation a {
font-size: 1.2rem; /* Larger font size for better readability */
transition: all 0.3s ease; /* Smooth transition */
}
}
@media (max-width: 767px) {
.lux-logo img {
/* Adjust width for mobile */
transition: all 0.3s ease; /* Smooth transition */
transform-origin: center center; /* Scaling from the center */
}
.elementor-menu-toggle {
font-size: 30px; /* Adjust size for mobile */
transition: all 0.3s ease; /* Smooth transition */
}
.lux-navigation a {
font-size: 1.2rem; /* Default font size for links */
color: rgba(30, 30, 30, 1); /* Darker link color */
transition: all 0.3s ease; /* Smooth transition */
}
}
What does this code do?
- Header Shrinking: Reduces the height of the header while adding a translucent background and a blur effect.
- Logo and Button Scaling: Scales down the logo and button for a more compact appearance.
- Navigation Scaling: Shrinks the entire navigation menu to match the smaller header.
- Menu Link Color Change: Changes the color of the menu links as you scroll for better contrast.
- Mobile Menu Toggle: Scales down the mobile menu toggle button to align with the overall shrinking effect.
Tips for Adjustements:
start
andend
values: Adjust these values to control when the animation begins and ends.- Custom Colors: Replace
#FFF
with any color code to match their design. - Scrub Setting: Experiment with
scrub: 1
to fine-tune the scroll-linked animation smoothness.