
Documentation
Webflow
Code
Setup: External Scripts
External Scripts in Webflow
Make sure to always put the External Scripts before the Javascript step of the resource.
In this video you learn where to put these in your Webflow project? Or how to include a paid GSAP Club plugin in your project?
HTML
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.7/dist/gsap.min.js"></script>
Step 1: Copy structure to Webflow
Copy structure to Webflow
In the video below we described how you can copy + paste the structure of this resource to your Webflow project.
Copy to Webflow
Webflow structure is not required for this resource.
Step 1: Add HTML
HTML
<div
class="bg"
data-grid=""
data-grid-size-desktop="20"
data-grid-size-mobile="8"
data-grid-background="#FFFFFF"
data-grid-border-size="2"
data-grid-border-color="rgba(0, 0, 0, 0.2)"
data-grid-colors="[#C5D4FF, #B7B0FF, #FF5FCE, #4136FF, #FFF751, #87FEFF, #C4FF3F]"
></div>
HTML structure is not required for this resource.
Step 2: Add CSS
CSS
.grid-bg {
position: absolute;
inset: 0%;
z-index: 0;
}
Step 2: Add custom Javascript
Custom Javascript in Webflow
In this video, Ilja gives you some guidance about using JavaScript in Webflow:
Step 2: Add Javascript
Step 3: Add Javascript
Javascript
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
}
}
function initGrid(el) {
// Define default values that are used if no attributes other than [data-grid] are used
const defaults = {
gridBackground: "#FFFFFF",
gridSizeDesktop: 20,
gridSizeMobile: 8,
gridBorderSize: 2,
gridBorderColor: "rgba(0, 0, 0, 0.2)",
gridColors: ["#C5D4FF", "#B7B0FF", "#FF5FCE", "#4136FF", "#FFF751", "#87FEFF", "#C4FF3F"]
};
const gridBackground = el.getAttribute("data-grid-background") || defaults.gridBackground;
const gridSizeDesktop = parseInt(el.getAttribute("data-grid-size-desktop")) || defaults.gridSizeDesktop;
const gridSizeMobile = parseInt(el.getAttribute("data-grid-size-mobile")) || defaults.gridSizeMobile;
const gridBorderSize = parseFloat(el.getAttribute("data-grid-border-size")) || defaults.gridBorderSize;
const gridBorderColor = el.getAttribute("data-grid-border-color") || defaults.gridBorderColor;
// Parse grid colors so you can use HEX or RGBA values in the attribute
let gridColors = defaults.gridColors;
const attrColors = el.getAttribute("data-grid-colors");
if (attrColors) {
try {
gridColors = JSON.parse(attrColors);
} catch (e) {
try {
gridColors = JSON.parse(attrColors.replace(/'/g, '"'));
} catch (e2) {
gridColors = defaults.gridColors;
}
}
}
el.style.backgroundColor = gridBackground;
const canvas = document.createElement("canvas");
el.appendChild(canvas);
const ctx = canvas.getContext("2d");
let cols, rows, squareSize, blocks, lastHoveredIndex = null;
// Generate the actual grid
function setupGrid() {
canvas.width = el.offsetWidth;
canvas.height = el.offsetHeight;
cols = (window.innerWidth < 768) ? gridSizeMobile : gridSizeDesktop;
squareSize = canvas.width / cols;
rows = Math.ceil(canvas.height / squareSize);
blocks = [];
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
blocks.push({ x: x * squareSize, y: y * squareSize, color: "white", alpha: 0 });
}
}
}
// Draw the squares
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
blocks.forEach(block => {
ctx.fillStyle = block.color;
ctx.globalAlpha = block.alpha;
ctx.fillRect(block.x, block.y, squareSize, squareSize);
ctx.globalAlpha = 1;
ctx.strokeStyle = gridBorderColor;
ctx.lineWidth = gridBorderSize;
ctx.strokeRect(block.x, block.y, squareSize, squareSize);
});
requestAnimationFrame(draw);
}
// Define how long it takes for blocks to fade out
function fadeOut(block) {
gsap.to(block, { alpha: 0, duration: 2, delay: 0.5 });
}
function supportsTouch() {
return "ontouchstart" in window || navigator.maxTouchPoints;
}
// Init mousemove listener if we're NOT on a touchscreen
if (!supportsTouch()) {
canvas.addEventListener("mousemove", (event) => {
const rect = canvas.getBoundingClientRect();
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
const hoveredIndex = blocks.findIndex(block =>
mouseX >= block.x &&
mouseX < block.x + squareSize &&
mouseY >= block.y &&
mouseY < block.y + squareSize
);
if (hoveredIndex !== -1 && hoveredIndex !== lastHoveredIndex) {
const block = blocks[hoveredIndex];
block.color = gridColors[Math.floor(Math.random() * gridColors.length)];
// Define duration of fade in animation
gsap.to(block, { alpha: 1, duration: 0.1, overwrite:true });
// Start fade out
fadeOut(block);
lastHoveredIndex = hoveredIndex;
}
});
}
window.addEventListener("resize", debounce(setupGrid, 200));
setupGrid();
draw();
}
function initGrids() {
document.querySelectorAll("[data-grid]").forEach(el => initGrid(el));
}
document.addEventListener("DOMContentLoaded", () => {
initGrids();
});
Step 3: Add custom CSS
Step 2: Add custom CSS
Custom CSS in Webflow
Curious about where to put custom CSS in Webflow? Ilja explains it in the below video:
CSS
Implementation
This script is setup to be as flexible as possible. In essence, all you need is a div with [data-grid]
attribute. All of the attributes (also listed below) are optional overrides of the default values listed in the JavaScript. So imagine you want to have the same grid all over your website, just change the default values, and you don't have to include any extra variables on your data-grid
elements.
Default options:
const defaults = {
gridBackground: "#FFFFFF", // Background color of the canvas
gridSizeDesktop: 20, // Amount of blocks in a row for desktop
gridSizeMobile: 8, // Amount of blocks for screens < 768px
gridBorderSize: 2, // Stroke width of the blocks
gridBorderColor: "rgba(0, 0, 0, 0.2)", // Stroke color
// Aray with default color options for hover
gridColors: ["#C5D4FF", "#B7B0FF", "#FF5FCE", "#4136FF", "#FFF751", "#87FEFF", "#C4FF3F"]
};
Available HTML attributes:
data-grid=""
→ The main selector, not optional
data-grid-size-desktop="20"
→ Controls the amount of blocks that will fit in a row.
data-grid-size-mobile="8"
→ Same as above, but for when the screen is smaller than 768px.
data-grid-background="#FFFFFF"
→ Set the background color of the canvas, can be 'transparent' too.
data-grid-border-size="2"
→ Set the stroke width of the blocks.
data-grid-border-color="rgba(0, 0, 0, 0.2)"
→ Set the color of the stroke on all the blocks.
data-grid-colors="[#C5D4FF, #B7B0FF, #FF5FCE, #4136FF, #FFF751, #87FEFF, #C4FF3F]"
→ An array of colors that are randomized on hover. This can be rgba values too, just wrap each color in between ' ' like so: ['rgba(0,0,0,0.1)', 'rgba(0,0,0,0.2)'].
Resource Details
Last updated
February 28, 2025
Type
The Vault
Category
Hover Interactions
Need help?
Join Slack