GSAP - Simple Example
by Hugo Vale Pereira
HTML
<!--Tutorial from: https://www.youtube.com/watch?v=Gy9IS0Y6dy0 Nov 2025
inspired from: https://www.wildyriftian.com/works
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/gsap.min.js"></script>
<nav>
<p>Tutorial</p>
<p>GSAP</p>
</nav>
<div class="folders">
<div class="row">
<div class="folder variant-1">
<div class="folder-preview">
<div class="folder-preview-img">
<img
src="https://images.unsplash.com/photo-1517694712202-14dd9538aa97?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80"
/>
</div>
<div class="folder-preview-img">
<img
src="https://images.unsplash.com/photo-1517694712202-14dd9538aa97?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80"
/>
</div>
<div class="folder-preview-img">
<img
src="https://images.unsplash.com/photo-1517694712202-14dd9538aa97?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80"
/>
</div>
</div>
<div class="folder-wrapper">
<div class="folder-index">
<p>01</p>
</div>
<div class="folder-name"><h1>figures</h1></div>
</div>
</div>
<div class="folder variant-2">
<div class="folder-preview">
<div class="folder-preview-img">
<img
src="https://images.unsplash.com/photo-1517694712202-14dd9538aa97?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80"
/>
</div>
<div class="folder-preview-img">
<img
src="https://images.unsplash.com/photo-1517694712202-14dd9538aa97?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80"
/>
</div>
<div class="folder-preview-img">
<img
...
CSS
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap');
:root {
--bg: #dddddd;
--fg: #1d1d1d;
--variant-1: oklch(0.9412 0.2245 111.18);
--variant-2: oklch(0.6647 0.2695 31.76);
--variant-3: #bdbdbd;
--disabled-folder-bg: #d2d2d2;
--disabled-folder-fg: #8a8a8a;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: var(--bg);
color: var(--fg);
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
h1{
font-family: "DM Sans" ,-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
font-size: 2.75rem;
font-weight: 400;
}
p{
text-transform: uppercase;
font-family: "DM Mono";
font-size: 0.8rem;
font-weight: 500;
}
h1,
p {
transition: color 250ms ease;
}
nav {
position: absolute;
top: 0;
width: 100%;
padding: 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.folders {
width: 100%;
height: 100svh;
display: flex;
flex-direction: column;
justify-content: flex-end;
overflow: hidden;
}
.row {
position: relative;
width: 100%;
display: flex;
}
.folder {
position: relative;
flex: 1;
height: 200px;
display: flex;
flex-direction: column;
cursor: pointer;
}
.row:nth-child(2) .folder:nth-child(1) {
flex: 2;
}
.row:nth-child(2) .folder:nth-child(2) {
flex: 3;
}
.folder-preview {
position: absolute;
top: 0;
left: 0;
width: 25rem;
height: 100%;
pointer-events: none;
}
.folder-preview-img {
position: absolute;
top: 50%;
width: 8rem;
height: 12rem;
}
.folder-preview-img:nth-child(1) {
left: 20%;
transform-origin: top left;
}
.folder-preview-img:nth-child(2) {
left: 50%;
transform-origin:...
JavaScript
const folders = document.querySelectorAll(".folder");
const folderWrappers = document.querySelectorAll(".folder-wrapper");
let isMobile = window.innerWidth < 678;
function setInitialPositions() {
gsap.set(folderWrappers, { y: isMobile ? 0 : 25 });
}
folders.forEach((folder, index) => {
const previewImages = folder.querySelectorAll(".folder-preview-img");
folder.addEventListener("mouseenter", () => {
if (isMobile) return;
folders.forEach((siblingFolder) => {
if (siblingFolder !== folder) {
siblingFolder.classList.add("disabled");
}
});
gsap.to(folderWrappers[index], {
y: 0,
duration: 0.25,
ease: "back.out(1.7)",
});
previewImages.forEach((img, imgIndex) => {
let rotation;
if (imgIndex === 0) {
rotation = gsap.utils.random(-20, -10);
} else if (imgIndex === 1) {
rotation = gsap.utils.random(-10, 10);
} else if (imgIndex === 2) {
rotation = gsap.utils.random(10, 20);
}
gsap.to(img, {
y: "-100%",
rotation: rotation,
duration: 0.25,
ease: "back.out(1.7)",
delay: imgIndex * 0.025,
});
});
});
folder.addEventListener("mouseleave", () => {
if (isMobile) return;
folders.forEach((siblingFolder) => {
siblingFolder.classList.remove("disabled");
});
gsap.to(folderWrappers[index], {
y: 25,
duration: 0.25,
ease: "back.out(1.7)",
});
previewImages.
forEach((img, imgIndex) => {
gsap.to(img, {
y: "0%",
rotation: 0,
duration: 0.25,
ease: "back.out(1.7)",
delay: imgIndex * 0.05,
});
});
});
});
window.addEventListener('resize', ()=> {
const currentBreakpoint = window.innerWidth < 678;
if (currentBreakpoint !== isMobile){
isMobile = currentBreakpoint;
setInitialPositions();
...