Hey everybody, I’m Iqbal Muthahhary, a contract artistic internet developer primarily based in Indonesia. It’s a pleasure to have the chance to encourage, educate, and study in collaboration with Codrops.
On this tutorial, we’ll discover the way to construct interactive grid format transitions triggered by buttons utilizing the GSAP Flip plugin, together with vanilla JavaScript and primary CSS Grid ideas. The interplay itself will stay easy: switching grid configurations and updating energetic button states, whereas GSAP handles the visible continuity.
The purpose is to point out how we are able to easily change the dimensions of grid objects and reorder them. It is a sample that could be very fascinating, particularly for portfolio layouts and interactive galleries.
HTML Construction
This HTML is cut up into two sections, every with a selected function in controlling and displaying the grid gallery.
The primary part acts as a configuration panel. It features a set of buttons that allow the person select totally different grid format scales. Every button has a data-size attribute that shops the goal grid measurement worth (comparable to 50%, 75%, 100%, and many others.). The presently chosen button receives the energetic class, which visually signifies the present selection and determines which worth is utilized to the gallery beneath.
The second part is the grid gallery itself. It makes use of a customized attribute, data-size-grid, to retailer the presently energetic grid measurement worth. When the person clicks a button within the configuration panel, this attribute is up to date accordingly. Altering data-size-grid updates the CSS grid format, permitting the gallery to easily adapt to the chosen configuration.
Contained in the grid gallery, every merchandise is represented by a .grid_gallery_item. Each merchandise accommodates a picture block (utilizing a background-image) and a component that shows the merchandise’s index within the gallery. Every grid merchandise additionally defines a customized CSS variable, --aspect-ratio, which controls the picture’s facet ratio. This makes it attainable to maintain proportions constant throughout layouts whereas nonetheless permitting every merchandise to have its personal distinctive form.
Grid Fashion
The gallery makes use of CSS Grid. The principle container defines the grid format, spacing, padding, and total width, whereas the data-size-grid attribute controls how compact or spacious the grid feels.
Every data-size-grid worth adjusts the variety of columns. Smaller values match extra objects on display, whereas bigger values give every merchandise extra room. This makes it straightforward to see how the format responds to the chosen choice.
Picture proportions are dealt with with the aspect-ratio property, utilizing a CSS variable outlined inline within the HTML. This fashion, every picture can maintain its personal form whereas the grid stays clear and constant.
.grid_gallery_container {
show: grid;
hole: 1.5rem;
padding: 2rem 0;
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
.grid_gallery_container[data-size-grid='50%'] {
grid-template-columns: repeat(16, 1fr);
}
.grid_gallery_container[data-size-grid='75%'] {
grid-template-columns: repeat(10, 1fr);
}
.grid_gallery_container[data-size-grid='100%'] {
grid-template-columns: repeat(8, 1fr);
}
.grid_gallery_container[data-size-grid='125%'] {
grid-template-columns: repeat(6, 1fr);
}
.grid_gallery_container[data-size-grid='150%'] {
grid-template-columns: repeat(4, 1fr);
}
.grid_gallery_item {
will-change: auto;
show: flex;
flex-direction: column;
}
.grid_gallery_item p {
font-size: 0.6745rem;
text-align: left;
}
.picture {
width: 100%;
aspect-ratio: var(--aspect-ratio, 1 / 1);
background-size: cowl;
background-position: heart;
filter: brightness(0.8);
transition: filter 0.3s cubic-bezier(0.25, 0.1, 0.25, 1);
}
.picture:hover {
filter: brightness(1);
}
@media display and (max-width: 768px) {
.grid_gallery_container[data-size-grid='50%'],
.grid_gallery_container[data-size-grid='75%'],
.grid_gallery_container[data-size-grid='100%'],
.grid_gallery_container[data-size-grid='125%'],
.grid_gallery_container[data-size-grid='150%'] {
grid-template-columns: repeat(3, 1fr);
}
}
@media display and (min-width: 768px) and (max-width: 1024px) {
.grid_gallery_container[data-size-grid='50%'] {
grid-template-columns: repeat(10, 1fr);
}
.grid_gallery_container[data-size-grid='75%'] {
grid-template-columns: repeat(8, 1fr);
}
.grid_gallery_container[data-size-grid='100%'] {
grid-template-columns: repeat(6, 1fr);
}
.grid_gallery_container[data-size-grid='125%'] {
grid-template-columns: repeat(5, 1fr);
}
.grid_gallery_container[data-size-grid='150%'] {
grid-template-columns: repeat(3, 1fr);
}
}
Alright, let’s make it interactive.
JavaScript
Create a easy grid format transition utilizing the GSAP Flip plugin
Every button controls how dense the grid format feels. When a button is clicked, the code first checks whether or not an animation is already operating. Whether it is, the clicking is ignored to maintain all the pieces steady. Subsequent, it reads the goal grid measurement from the button’s data-size attribute. If that measurement is already energetic, the clicking can be ignored since there’s nothing to replace.
When an actual change is required, interplay is briefly locked and the present format of all grid objects is captured. The grid measurement is then up to date by altering the data-size-grid attribute, which triggers a brand new CSS grid format.
The energetic button state is up to date to mirror the brand new choice, and GSAP Flip animates the grid easily from the earlier format to the brand new one. As soon as the animation finishes, interplay is unlocked and the grid is prepared for the subsequent enter.
// Flag to stop a number of animations on the identical time
// currentGridSize shops the energetic grid measurement
let animated = false,
currentGridSize = gridGallery.dataset.sizeGrid || "75%";
// Loop by means of every configuration button
triggerButtons.forEach((btn) => {
// Add click on occasion listener to every button
btn.addEventListener("click on", () => {
// Stop interplay if an animation is already operating
if (animated) return;
// Get the goal grid measurement from data-size attribute
const targetSize = btn.dataset.measurement;
// If the clicked measurement is already energetic, do nothing
if (targetSize === currentGridSize) return;
// Lock animation state
animated = true;
// Seize the present place and measurement of all grid objects
const state = Flip.getState(allGridItem);
// Replace grid measurement utilizing knowledge attribute (utilized by CSS)
gridGallery.dataset.sizeGrid = targetSize;
// Replace present grid measurement state
currentGridSize = targetSize;
// Take away "energetic" class from all buttons
triggerButtons.forEach((btn) => {
btn.classList.take away("energetic");
});
// Add "energetic" class to the clicked button
btn.classList.add("energetic");
// Animate components from the earlier state to the brand new format
Flip.from(state, {
period: 0.8, // Animation period in seconds
ease: "expo.inOut", // Easy easing for pure movement
onComplete: () => {
// Unlock animation after completion
animated = false;
},
});
});
});
Model 2
In comparison with the easier model, the core logic stays precisely the identical: button clicks, state checks, format updates, and the Flip animation all work in the identical method. The principle distinction right here is how the transition feels visually.
This model provides a visible impact to the grid container itself. Whereas the objects are rearranging, the whole grid briefly blurs and turns into brighter, then easily returns to its regular state. This impact runs in parallel with the Flip animation and helps soften the second when the format modifications, making the transition really feel extra polished and intentional.
This model additionally makes use of a stagger with a random order, so objects don’t all transfer on the identical time. Due to the staggered movement, the entire transition time is calculated by combining the Flip period and the stagger quantity. The interplay lock (animated) remains to be launched solely after the primary Flip animation finishes, making certain all the pieces stays steady earlier than the subsequent interplay.
In brief, this model doesn’t change how the grid works, but it surely enhances the way it feels by including depth, rhythm, and delicate visible suggestions through the transition.
// Flag to stop a number of animations on the identical time
// currentGridSize shops the energetic grid measurement
let animated = false,
currentGridSize = gridGallery.dataset.sizeGrid || "75%";
// Loop by means of every configuration button
triggerButtons.forEach((btn) => {
// Add click on occasion listener to every button
btn.addEventListener("click on", () => {
// Stop interplay if an animation is already operating
if (animated) return;
// Get the goal grid measurement from data-size attribute
const targetSize = btn.dataset.measurement;
// If the clicked measurement is already energetic, do nothing
if (targetSize === currentGridSize) return;
// Lock animation state
animated = true;
// Seize the present place and measurement of all grid objects
const state = Flip.getState(allGridItem);
// Replace grid measurement utilizing knowledge attribute (utilized by CSS)
gridGallery.dataset.sizeGrid = targetSize;
// Replace present grid measurement state
currentGridSize = targetSize;
// Take away "energetic" class from all buttons
triggerButtons.forEach((btn) => {
btn.classList.take away("energetic");
});
// Add "energetic" class to the clicked button
btn.classList.add("energetic");
const flipDuration = 1;
const staggerAmount = 0.3;
const totalFlipDuration = flipDuration + staggerAmount;
// Animate components from the earlier state to the brand new format
Flip.from(state, {
absolute: true,
period: flipDuration, // Animation period in seconds
ease: "expo.inOut", // Easy easing for pure movement
onComplete: () => {
// Unlock animation after completion
animated = false;
},
stagger: {
quantity: staggerAmount,
from: "random",
},
}).fromTo(
gridGallery,
{
filter: "blur(0px) brightness(100%)",
willChange: "filter",
},
{
period: totalFlipDuration,
keyframes: [
{
filter: "blur(10px) brightness(200%)",
duration: totalFlipDuration * 0.5,
ease: "power2.in",
},
{
filter: "blur(0px) brightness(100%)",
duration: totalFlipDuration * 0.5,
ease: "power2",
delay: 0.5,
},
],
},
0
);
});
});
Conclusion
This instance highlights simply considered one of many attainable instructions. By conserving the core logic the identical and solely adjusting animation particulars, the grid transition can really feel delicate, dynamic, or extra expressive. Small tweaks to timing, stagger, or visible results can already open up a variety of variations.
This flexibility makes the setup straightforward to adapt and prolong, encouraging experimentation with out including pointless complexity.
And that’s it! We’ve constructed an interactive grid format transition utilizing the GSAP Flip plugin. I hope this tutorial was helpful. Thanks for studying 😃








