Animate a Swiss-Style Poster in Webflow with GSAP

Advanced 37:15 webflowgsapclip-pathfigmaanimation

Recreate a modernist Swiss-style poster in Webflow using a Figma-built clip-path mask and a GSAP page-load sequence of staggered text and slide-mask reveals.

Key takeaways

  • Sizing type and spacing in vh (with a fixed aspect ratio on the composition) keeps a poster fully inside the viewport on any screen, instead of overflowing on short ones.
  • To “cut” letters out of a solid shape, build the exact silhouette in Figma with a boolean subtract, export it as SVG, and use it as a CSS clip-path — no image, fully scalable.
  • An exported SVG path uses absolute pixel coordinates; normalizing it to the 0–1 range (with clipPathUnits="objectBoundingBox") is what lets the clip scale with the element it masks.
  • You can target a group of elements by a custom attribute selector instead of a shared class — handy when the elements have no class in common and you still want one staggered action to hit them all.
  • GSAP animations run after CSS paints, so elements flash in their final state for a frame; hiding the wrapper in the <head> and restoring opacity as the very first action removes that page-load flicker.

Video chapters

  1. 00:00 Intro
  2. 01:17 Building the structure of the section
  3. 04:48 Adding the content
  4. 08:39 Creating and normalizing the path in Figma, applying the clip-path
  5. 15:39 Fixing the layout and completing the section
  6. 19:42 Building the staggered text animation with GSAP
  7. 24:00 Building the slide-mask animation with GSAP
  8. 31:58 Fixing the anti-aliasing issue
  9. 32:54 Responsiveness
  10. 33:45 Fixing GSAP flicker on page load
  11. 36:20 Outro

This lesson recreates a modernist Swiss-style poster in Webflow and brings it to life with GSAP — a full-viewport composition where the letters “JAM” are cut out of a red field, then slide in and out in a looping, cinematic sequence. It’s one of my favorite kinds of build: half of it is quiet layout craft, the other half is motion, and the two have to meet in the middle perfectly.

We’ll go through it in the order it was built. First the structure and typography, sized so the whole thing behaves like a printed poster. Then the trickiest part — cutting the letters out with a CSS clip-path shaped in Figma. Then two GSAP interactions: a staggered text reveal and a slide-mask sequence for the big letters. And finally the small fixes that separate a demo from something you’d actually ship: an anti-aliasing sliver, responsiveness, and the flicker you get when GSAP loads.

None of the individual pieces are hard. The magic is in how they stack.

How it works

A poster that fits the viewport. The section is 100vh with a red background, and the content wrapper is sized to feel like print: a 2:3 aspect ratio, flex column, aligned to the bottom so the big letters sit low like a real flyer. The key decision is units — all the text is sized in vh and letter-spacing in em. That’s deliberate: by tying type to viewport height, everything scales down together on shorter screens, so the composition never overflows vertically. It’s the same instinct as designing to a fixed poster frame.

Cutting the letters out. This is the heart of the build. We don’t draw the letters — we cut them out of a solid red block using a CSS clip-path, and a clip-path only shows what’s inside its shape. So the shape we need isn’t the letters themselves, it’s the negative around them. That shape is built in Figma: type “JM”, convert to a vector, delete the anchor points that form the dot on the J, drop a rectangle behind it, and run a boolean subtract so the letters are carved out of the rectangle. Export that as SVG. The letter A is handled separately — it’s a plain SVG with fill: currentColor so it inherits its parent’s color, sitting in its own wrapper so it can be animated on its own later.

Normalizing the path. An exported SVG path speaks in absolute pixels. A CSS clip-path set to objectBoundingBox speaks in a 0–1 range so it can scale with whatever element it clips. Bridging the two is “normalizing”: multiply every coordinate by 1/width and 1/height. The SVG Path Editor does this for you — paste the path data, note the SVG’s width and height, and run a scale with 1/width and 1/height. In the video’s case that was 1/15 ≈ 0.067 and 1/11 ≈ 0.091. The result is a path that maps cleanly onto any box.

Two clean animations, one page-load trigger. Both interactions fire on page load. The text reveal targets the four small text blocks by a custom attribute (not a class) and animates opacity 0 → 100% and X -50% → 0, staggered by 0.1s, for a slide-and-fade from the left. The big-letter sequence is all one idea repeated: animate a single property, move X, on three targets — the dot, the JM inner wrapper, and the A — with slightly different offsets and timings so they slide in from the left (and the A from the right, as if it’s moving inside the JM group), slide back out, then return. Two overflow: hidden guards make the masking read correctly: one on the content wrapper (so the oversized JM can bleed off the edge), one on the letters inner wrapper (so the A stays hidden inside the JM until it slides).

The finishing fixes. Three details make it production-ready. A thin outline appears where two aligned elements move together — a sub-pixel anti-aliasing artifact, fixed with a -1px margin on the section letters. Responsiveness is a couple of vh offset tweaks at the portrait breakpoint. And the load flicker — where elements flash before GSAP sets their start state — is solved by hiding the content wrapper with head CSS and restoring its opacity as the interaction’s very first action.

How to use it

  1. Build the frame. Section at 100vh, red background. Inside, a main container (height: 100%, flex, centered). Inside that, a section content wrapper: position: relative, flex column, aligned to bottom, 2:3 aspect ratio, overflow: hidden. Set your typography here — a medium weight, line-height: 1.3, font-size: 2.5vh, letter-spacing: -0.02em, most text right-aligned.

  2. Structure the big letters. Inside the content wrapper add a letters wrapper holding two things: a section dot div (width: 13%, aspect-ratio: 1, light background) and a section letters inner wrapper (position: relative). Inside the inner wrapper, add section letters (landscape aspect ratio, red background — this is what gets clipped) and a section letter wrapper for the A (position: absolute, full). Reorder in the navigator and set section letters to position: relative so it stacks above the absolutely-positioned A wrapper.

  3. Make the A. In Figma, type “A” in the right font/weight, copy as SVG, and paste it into an embed inside the A wrapper. Set the SVG’s fill to currentColor and its width/height to 100%.

  4. Make the cut-out shape. In Figma type “JM”, convert text to vector (Cmd/Ctrl+E), delete the anchor points forming the J’s dot, place a rectangle snugly behind the letters, select both, and run Object → Boolean → Subtract. Copy the result as SVG.

  5. Normalize the path. Paste the SVG into the SVG Path Editor. Keep only the d path data (from M to Z), note the width/height, then use the scale operation with 1/width and 1/height to remap coordinates into the 0–1 range. Copy the normalized path.

  6. Define and apply the clip-path. Add an embed at the very top of the page (the definition must come before anything that references it) containing an inline SVG clipPath with id="jm" and clipPathUnits="objectBoundingBox", with your normalized path in its d. Give the embed position: absolute. Then, on the section letters element, add a custom property clip-path: url(#jm). The letters now appear cut out.

  7. Fine-tune the layout. Widen the letters inner wrapper to ~127% with a left offset of ~-8.5% so the M crops off the right edge; the content wrapper’s overflow: hidden hides the overflow. Offset the dot ~8.5% to align with the J. Give the A wrapper flex display, ~15% horizontal and ~4% vertical padding, and set the A itself to ~73% height. Add your two text wrappers (top and bottom), position: absolute, with 7vh offsets.

  8. Animate the text (page load). Add each of the four text blocks a custom attribute like data-content-role="text". Create a page-load interaction, add a custom action targeting that attribute (all matching elements), type From, animating opacity 0% and move X: -50%, duration ~4s, ease power-out, stagger by offset time 0.1s.

  9. Animate the big letters. In the same interaction, build the dot first: a From action on move X: -200%, then a To action out to a large positive move X (e.g. 500%), with matched start times and eases. Duplicate that pair for the JM inner wrapper (in from -100%, out the other way) and for the A (in from the right, out to the left). Then duplicate the three “in” actions again as a second round set to type From with move X: 0% so everything returns for a seamless loop. Add overflow: hidden on the letters inner wrapper so the A stays masked inside the JM.

  10. Polish. Add -1px margin on section letters to kill the anti-aliasing sliver. At the portrait breakpoint, adjust the text wrappers’ offsets (9vh) and add bottom padding (7vh) on the content wrapper. Finally, fix the load flicker: hide the content wrapper with an opacity: 0 rule in the page’s head, then make the very first action of the interaction set that wrapper’s opacity back to 100% (duration 0), dragged to the top of the timeline.

Resources

Frequently asked questions

How do you cut letters out of a solid shape in Webflow?
Build the negative shape in Figma — type the letters, subtract them from a rectangle with a boolean operation, and export the result as SVG. Take the path data into a CSS clip-path defined in an embed, then apply it to your solid-colored element with clip-path: url(#your-id). The color shows only where the path allows, so the letters read as cut-outs.
Why do you have to normalize an SVG path before using it as a clip-path?
A path exported from Figma uses absolute pixel coordinates tied to its original width and height. CSS clip-paths set to objectBoundingBox expect coordinates in a 0–1 range so the shape scales with the element. Normalizing means multiplying every coordinate by 1/width and 1/height — a free tool like the SVG Path Editor does it with a scale operation.
How do you target elements in a GSAP interaction without giving them a shared class?
Add the same custom attribute to each element (for example data-content-role="text"), then in the interaction set the target type to Attribute and enter that name and value, with scope set to all matching elements. Every element carrying the attribute animates together — useful when the elements do not otherwise share a class.
How do you stop GSAP animations from flickering on page load in Webflow?
JavaScript loads after CSS, so for one frame the browser paints your elements in their normal (final) state before GSAP sets the starting values. Force the animated wrapper to opacity: 0 with a small CSS snippet in the page head so it loads instantly, then make the first action of your page-load interaction set that wrapper back to opacity: 100% — the content stays hidden until the animation is ready to run.
Why size a poster layout in vh units instead of pixels or rem?
The whole composition needs to fit inside the visible viewport height like a printed poster. Tying font sizes and spacing to vh means everything scales down together as the screen gets shorter, so the content never overflows or clips vertically. Letter-spacing stays in em so it stays proportional to the font size.

← Back to the course

Also part of these courses