All components

Sidebar Slide-Out Menu

GSAP animation component · Published 2026-07-27 · by vanguardia.dev

Open live demo ↗ Raw prompt (.md)

What it does

Clicking the bottom-right 'Get the Overlay' toggle slides a column of three cards in from the right edge (right: -110% to 0%) with a 0.075s GSAP stagger on a power4.out ease, while the page content blurs to 15px; clicking any card reverses the tween to slide the cards back out and unblur. Cards expand their copy and swap colours on CSS hover.

How it's built

Categorymenu
Techgsap
Complexitysection
Performance costlight
Mobile-safeyes

sidebar slide-out overlay stagger blur cards gsap minimal

Rebuild it with AI

To reproduce this animation in your own project, copy the prompt below into Claude Code, Cursor or any AI coding agent. The prompt is validated — it describes the exact structure, timing and easing, so the agent rebuilds the effect faithfully and you can then adapt colors, copy and layout to your design.

The full prompt

Sidebar Slide-Out Menu — Staggered Cards Sliding In From The Right + Background Blur

Goal

Build a fullscreen hero page with a small "Get the Overlay" toggle pinned in the bottom-right corner. Clicking it slides a fixed column of three white cards in from the right edge of the screen with a fast GSAP 0.075s stagger on a power4.out ease, while the whole page content blurs to 15px and the toggle button itself slides off-screen to the right. Clicking any card reverses everything — the cards slide back out past the right edge, the toggle slides back into its corner, and the page un-blurs. On top of that, each card has pure-CSS hover behavior: hovering a card swaps it to a deep-blue fill and reveals its body copy, and the first card additionally pops in a large close (×) icon. It is entirely click- and hover-driven — no scroll, no autoplay.

Tech

Vanilla HTML/CSS/JS with ES module imports. Use gsap (npm) only — no GSAP plugins, no ScrollTrigger, no smooth-scroll library. Import as:

import gsap from "gsap";

Icons come from the Phosphor Icons web font, loaded via a <script> tag in <head>:

<script src="https://unpkg.com/@phosphor-icons/web"></script>

Used icon classes: ph ph-arrow-right (the arrow in the toggle) and ph-thin ph-x (the close × on card 1).

Layout / HTML

.container                             (fullscreen, position:relative — holds the site + toggle; this is what blurs)
  .website-content
    nav                                (fixed top bar, flex, 3 equal columns)
      .logo            "Motionprompts"
      .cta             "Subscribe"      (center column)
      .links                            (right column, flex-end)
        a "About"  a "Infos"  a "Contact"
    .hero                               (left-aligned headline block, vertically centered)
      h1  "Elite web designs"
      p   (a paragraph of body copy)
  .overlay-toggle                       (bottom-right white pill — the OPEN click target)
    p  "Get the Overlay"
    p  > i.ph.ph-arrow-right

.sidebar                               (fixed full-height column on the right; clips the cards)
  .card                                 (card 1)
    .card-title
      div  "Showreel"
      .close-btn > i.ph-thin.ph-x       (large × — only on card 1)
    .card-copy > p                       (body copy, hidden until hover)
  .card                                 (card 2)
    .card-title  "Community"
    .card-copy > p
  .card                                 (card 3)
    .card-title  "Catalog"
    .card-copy > p

Note the .sidebar is a sibling of .container, not inside it — so when .container blurs, the sidebar cards stay sharp. Use neutral, fictional labels (Motionprompts / Subscribe / About / Infos / Contact / Showreel / Community / Catalog) — no real brand names. Card body copies are short lorem-ipsum sentences.

Styling

Font: "Neue Montreal" (a clean neutral grotesque sans-serif). If unavailable, fall back to a similar sans (system-ui, Helvetica). html, body { width:100%; height:100%; background:#000; color:#fff; font-family:"Neue Montreal"; }.

Global reset: * { margin:0; padding:0; box-sizing:border-box; }.

Color tokens:

  • Page background: #000 (black)
  • Text on hero: #fff; hero paragraph muted grey #a0a0a0
  • Toggle pill + cards default fill: #fff with #000 text
  • Card hover fill: #020B44 (deep navy blue) with #fff text

Structural / load-bearing CSS (these dimensions and the right offsets are what the GSAP tweens animate to/from — keep them exact):

  • .container: position: relative; width: 100vw; height: 100vh; fullscreen hero background image no-repeat 50% 50%, background-size: cover.
  • nav: position: fixed; width: 100%; display: flex; padding: 1.5em; with nav > div { flex: 1; }. .cta { text-align: center; }. .links { display: flex; justify-content: flex-end; gap: 2em; }. a { text-decoration: none; color: #fff; }.
  • .hero: position: absolute; top: 50%; transform: translateY(-50%); padding: 0 1.5em; width: 35%;. .hero h1 { font-size: 40px; font-weight: 400; margin-bottom: 10px; }. .hero p { color: #a0a0a0; line-height: 1.5; }.
  • .overlay-toggle: position: absolute; right: 0; bottom: 0; width: 250px; height: 150px; margin: 0.75em; padding: 1em; border-radius: 0.5em; background: #fff; color: #000; display: flex; justify-content: space-between; cursor: pointer; (label top-left, arrow icon top-right). Its right is the animated property (starts at the CSS 0, effectively right: 0.75em visually via the margin).
  • .sidebar: position: fixed; top: 0; right: 0; width: 30vw; height: 100vh; padding: 0.75em; display: flex; flex-direction: column; gap: 0.75em; overflow: hidden; pointer-events: none;. overflow: hidden clips the cards while they sit off-screen at right: -110%; pointer-events: none is the closed state (JS toggles it to all when open so the cards become clickable).
  • .card: position: relative; right: -110%; (this off-screen offset is the closed state the GSAP tween animates from) padding: 1em; flex: 1; background: #fff; color: #000; border-radius: 0.5em; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; cursor: pointer;. Three equal-height cards stacked vertically.
  • .card-title: font-size: 50px; letter-spacing: -0.035em;.
  • .card:nth-child(1) .card-title { display: flex; justify-content: space-between; } (title text on the left, close-btn on the right — only card 1).
  • .close-btn: position: relative; font-size: 60px; transform: scale(0); transition: transform 0.3s; transform-origin: center; — hidden by default, scaled up on card-1 hover.
  • .card-copy: position: relative; opacity: 0; transform: translateY(20px); font-size: 15px; transition: transform 0.3s, opacity 0.3s; — hidden by default, revealed on hover.

Pure-CSS hover behavior (NOT GSAP):

.card:hover                 { background: #020B44; color: #fff; }
.card:hover .card-copy      { opacity: 1; transform: translateY(0px); }   /* copy slides up + fades in over 0.3s */
.card:nth-child(1):hover .close-btn { transform: scale(1); }              /* × pops from scale 0→1 over 0.3s */

GSAP effect (exhaustive)

No plugins, no timeline object — the effect is built from standalone gsap.to() tweens fired inside two functions (animateCardsIn on open, animateCardsOut on close). Grab refs once on DOMContentLoaded:

const container     = document.querySelector(".container");
const sidebar       = document.querySelector(".sidebar");
const cards         = gsap.utils.toArray(".card");        // the 3 cards, in DOM order
const overlayToggle = document.querySelector(".overlay-toggle");
Open — animateCardsIn()

Fires three parallel standalone tweens, all starting on the same tick (see note on "<" below):

  1. Toggle slides off-screen right:

``js gsap.to(overlayToggle, { right: "-500px", duration: 1, ease: "power4.out" }); ` Animates the pill's right from its resting 0 to -500px, sliding it out past the right edge. duration: 1, ease: "power4.out"`.

  1. Cards slide in, staggered:

``js gsap.to(cards, { right: "0%", stagger: 0.075, duration: 1, ease: "power4.out" }, "<"); ` Animates each card's right from the CSS -110% (fully off-screen to the right, clipped by the sidebar's overflow: hidden) to 0% (flush in place). stagger: 0.075 fires them 0.075s apart in DOM order — card 1 first (Showreel), then card 2 (Community), then card 3 (Catalog). duration: 1 each, ease: "power4.out"` (fast launch, long smooth settle).

  1. Page content blurs:

``js gsap.to(container, { filter: "blur(15px)", duration: 1, immediateRender: false }, "<"); ` Animates the .container's CSS filter from blur(0px) to blur(15px) over duration: 1. No ease specified → GSAP default power1.out. immediateRender: false prevents GSAP from snapping the blur to its end value before the tween runs. Because the sidebar is a sibling (outside .container`), the incoming cards remain crisp while the site behind them goes soft.

Close — animateCardsOut()

The mirror image, again three parallel standalone tweens:

  1. Toggle slides back into its corner:

``js gsap.to(overlayToggle, { right: "0px", duration: 1, ease: "power4.out" }); ` right: -500px → 0px, duration: 1, ease: "power4.out"`.

  1. Cards slide back out, staggered:

``js gsap.to(cards, { right: "-110%", stagger: 0.075, duration: 1, ease: "power4.out" }); ` right: 0% → -110% (back off-screen), same stagger: 0.075 in DOM order, duration: 1, ease: "power4.out"`.

  1. Page un-blurs:

``js gsap.to(container, { filter: "blur(0px)", duration: 1, immediateRender: false }, "<"); ` blur(15px) → blur(0px), duration: 1, default power1.out ease, immediateRender: false`.

Note on the "<" third argument: in the source it is passed to some of these standalone gsap.to() calls, but the position parameter ("<") is only meaningful on timeline methods — on the global gsap.to() it is simply ignored. The practical result is that all three tweens in each function start together on the same tick. (You may include it verbatim for fidelity, or omit it — behavior is identical.)

Wiring
overlayToggle.addEventListener("click", () => {
  sidebar.style.pointerEvents = "all";   // enable card clicks while open
  animateCardsIn();
});

cards.forEach((card) => {
  card.addEventListener("click", () => {
    sidebar.style.pointerEvents = "none"; // disable card clicks while closed
    animateCardsOut();
  });
});
  • Open: clicking the .overlay-toggle sets the sidebar to pointer-events: all (so the cards become clickable) and runs animateCardsIn().
  • Close: clicking any card sets the sidebar back to pointer-events: none and runs animateCardsOut(). (Every card closes the menu, including card 1 — the × icon is decorative CSS, not a separate handler.)
  • There is no re-entrancy guard; GSAP overwrites in-flight tweens naturally if the user clicks rapidly.

Wrap everything in document.addEventListener("DOMContentLoaded", () => { ... }).

Assets / images

  • 1 hero background image — role: *fullscreen background behind the whole site, sits inside .container and is what blurs*. A moody, dark, photographic image (low-key, cinematic). Displayed background-size: cover at 50% 50%. Landscape ~16:9 (e.g. 1920×1080 or larger). Because the UI text is white and the card hover fill is a deep navy, a darker image reads best.

Behavior notes

  • Entirely click + hover driven — no scroll, no ScrollTrigger, no loops, no autoplay.
  • The card enter/exit + toggle slide + page blur are GSAP; the card color swap, body-copy reveal, and card-1 × pop are pure CSS :hover transitions (all 0.3s).
  • Closed state is expressed in CSS (.card { right: -110% }, .sidebar { pointer-events: none }); JS only ever tweens toward/away from it and toggles pointer-events.
  • Responsive (max-width: 900px): .hero { top: 25%; width: 100%; } and .sidebar { width: 100vw; } (the sidebar becomes a full-width overlay; cards still slide in from the right with the same stagger).