Animated Grid Overlay (Columns)
A fixed full-viewport column grid overlay that slides in and out with a staggered GSAP animation. Useful during development to verify that layouts align with the underlying grid. Toggled by keyboard shortcut (Shift+G) or any element with [data-animated-grid-toggle]. State is persisted in localStorage.
Setup — External Scripts
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>Code
<div data-animated-grid class="animated-grid">
<div class="container is--full-height">
<div class="animated-grid__row">
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
<div data-animated-grid-col class="animated-grid__col"></div>
</div>
</div>
</div>.animated-grid {
position: fixed;
inset: 0;
z-index: 200;
pointer-events: none;
display: block;
}
.container {
width: 100%;
max-width: var(--size-container);
margin-inline: auto;
padding-inline: 1.5em;
}
.container.is--full-height {
height: 100%;
}
.animated-grid__row {
display: grid;
grid-template-columns: repeat(12, minmax(0, 1fr));
gap: 1.5em;
width: 100%;
height: 100%;
overflow: clip;
}
.animated-grid__col {
width: 100%;
height: 100%;
min-height: 100%;
opacity: 0.2;
background-color: #f4f4f4;
}
@media screen and (max-width: 991px) {
.animated-grid__row {
grid-template-columns: repeat(6, minmax(0, 1fr));
}
}
@media screen and (max-width: 767px) {
.animated-grid__row {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}function initAnimatedGrid() {
const grid = document.querySelector("[data-animated-grid]");
const cols = document.querySelectorAll("[data-animated-grid-col]");
const toggles = document.querySelectorAll("[data-animated-grid-toggle]");
if (!grid || !cols.length) return;
const storageKey = "animatedGridState";
let isOpen = localStorage.getItem(storageKey) === "open";
gsap.set(grid, { display: "block" });
if (isOpen) {
gsap.set(cols, { yPercent: 0 });
} else {
gsap.set(cols, { yPercent: 100 });
}
function openGrid() {
isOpen = true;
localStorage.setItem(storageKey, "open");
gsap.fromTo(cols, { yPercent: 100 }, {
yPercent: 0,
duration: 1,
ease: "expo.inOut",
stagger: { each: 0.03, from: "start" },
overwrite: true,
});
}
function closeGrid() {
isOpen = false;
localStorage.setItem(storageKey, "closed");
gsap.fromTo(cols, { yPercent: 0 }, {
yPercent: -100,
duration: 1,
ease: "expo.inOut",
stagger: { each: 0.03, from: "start" },
overwrite: true,
});
}
function toggleGrid() {
if (isOpen) closeGrid();
else openGrid();
}
function isTypingContext(e) {
const el = e.target;
if (!el) return false;
const tag = (el.tagName || "").toLowerCase();
return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
}
toggles.forEach((btn) => {
btn.addEventListener("click", (e) => {
e.preventDefault();
toggleGrid();
});
});
window.addEventListener("keydown", (e) => {
if (isTypingContext(e)) return;
if (!(e.shiftKey && (e.key || "").toLowerCase() === "g")) return;
e.preventDefault();
toggleGrid();
});
}
document.addEventListener("DOMContentLoaded", () => {
initAnimatedGrid();
});Attributes
| Name | Type | Default | Description |
|---|---|---|---|
| data-animated-grid | attribute | — | Root wrapper for the overlay. Place as a direct child of <body>. The script sets display:block on it at load time — you can add display:none in CSS to hide it until JS runs. |
| data-animated-grid-col | attribute | — | Each column element inside the grid row. GSAP animates yPercent on these to slide the columns in from below (open) and out to above (close). |
| data-animated-grid-toggle | attribute | — | Attach to any button or link that should toggle the grid on click. Multiple elements are all wired up automatically. |
Notes
- •Place the [data-animated-grid] element as a direct child of <body>. Do not add position: relative to <body>.
- •The overlay is pointer-events: none so it never blocks clicks on page content.
- •This is a developer/design tool — remove or hide it before going to production.
- •State persists in localStorage so the grid stays visible across page reloads during development.
- •The Shift+G shortcut is intentionally ignored inside form fields and contenteditable elements.
Guide
Open and close animation
On open, columns animate from yPercent: 100 (below the viewport) to yPercent: 0 with a left-to-right stagger of 0.03s each, using expo.inOut over 1s. On close, they animate from yPercent: 0 to yPercent: -100 (above the viewport) with the same stagger.
Persistent state
The open/closed state is saved in localStorage under the key "animatedGridState". On page load the script reads this and immediately positions the columns without animating, so the grid stays open across page navigations if it was left open.
Keyboard shortcut
Press Shift+G anywhere on the page to toggle the grid. The shortcut is ignored when the cursor is inside an input, textarea, select, or contenteditable element.
Column count
The default is 12 columns on desktop, 6 on screens ≤991px, and 4 on screens ≤767px. Adjust grid-template-columns in the CSS and add or remove [data-animated-grid-col] divs in the HTML to match your design grid.