Build a 3D CMS Cubic Gallery in Webflow

This tutorial offers an immersive, masterclass-level guide to building a fully CMS-powered cubic gallery inside Webflow. Led by Francesco from SupaSaito, the video breaks down how to orchestrate complex, scroll-based animations using the latest JavaScript Animation (GSAP) powered Webflow interactions. Viewers will learn to bridge the gap between structural development and advanced motion design to create web experiences that feel incredibly fluid and cutting-edge.

The curriculum uniquely addresses how to scale animations by combining Webflow CMS fields, custom CSS transforms, and attribute-based interaction thresholds. Rather than treating motion as an afterthought, the tutorial teaches viewers to design a responsive animation system. This includes managing staggered typography masks, synchronized background image crossfades, and precise 3D spatial translations.

By the conclusion of this video, viewers will achieve a deep structural understanding of 3D layouts and interactive math in Webflow. They will successfully build a responsive cube that rotates seamlessly through 3D space along a hexagonal path as the user scrolls, perfectly mapped to changing background imagery and typography. This project serves as a definitive roadmap for pushing Webflow far beyond its standard native constraints.

Key Takeaways

  • Advanced CMS-Driven Transforms: Learn to utilize custom numeric and decimal CMS fields to dynamically inject individual 3D properties—such as transform origin, axis translation ($X, Y, Z$), and rotation—directly into an embedded CSS snippet per collection item.
  • Preserving 3D Context in the Browser: Master the implementation of custom CSS properties like transform-style: preserve-3d and container-type: size to force browsers to accurately render depth, perspective, and percentage-based Z-axis depth changes.
  • Attribute-Based Interaction Mapping: Restructure interactions independently of class names by utilizing custom attributes (e.g., data-animate, data-background-image) bound directly to CMS order fields, allowing precise control over separate elements within the timeline.
  • Responsive Fluid Design Mathematics: Implement responsive CSS sizing using the min() function (combining VW and VH units) to ensure the 3D gallery components scale flawlessly across both horizontal desktop and vertical mobile viewports.
  • Professional Motion Polish: Discover essential optimization techniques for high-end web builds, including clipping layout overflows, engineering staggered text maskings, and executing global CSS rule additions to remove distracting native overscroll bounce behaviors.

Timestamps

  • 05:52 - Creating the .section class and setting its height to 500 VH to control the global scroll velocity.
  • 06:48 - Configuring the .main-container position to sticky with a top: 0 offset and setting its dimensions to 100 VH height and max-width: none.
  • 08:09 - Connecting the background collection list wrapper to the Webflow CMS "pictures" collection and sorting items by the order field from largest to smallest.
  • 08:46 - Setting the .pictures_list-item position to absolute and applying the full positioning preset to stack the images.
  • 09:45 - Setting the .pictures_image sizing to 100% width/height and configuring its fit property to cover.
  • 10:42 - Creating a .pictures_overlay div block with an absolute layout, full preset, and a custom 3-stop linear gradient background (black 60% at 0%, transparent at 50%, black 60% at 100%).
  • 11:48 - Updating the .pictures_list-item display property to flex with horizontal and vertical center alignments.
  • 12:26 - Mapping the text element to the CMS text field, setting its font size to fluid 12 VW, setting the line-height to a unitless 1, and turning on capitalization.
  • 14:52 - Adding the custom HTML attribute data-animate="section" to the section trigger element.
  • 15:13 - Applying the custom attribute data-background-image to the collection item and dynamically binding its value field directly to the CMS order field.
  • 16:26 - Applying the custom attribute data-image-text to the text element and dynamically binding its value field directly to the CMS order field.
  • 16:48 - Initiating a new scroll interaction named "cube rotation and background image", targeting the interaction via the data-animate="section" custom attribute, and adjusting the scroll smoothing setting to 0.8.
  • 17:26 - Defining the interaction thresholds to start when the top of the section meets the top of the viewport, and to conclude when the bottom of the section meets the bottom of the viewport.
  • 18:21 - Creating the "text one out" linear-eased timeline action targeting data-text="1", selecting the move Y property, and setting it to a 2 value of 100%.
  • 19:40 - Setting the text stagger property to offset time inside the action settings, activating the split text feature by word, and inputting a stagger interval of 0.3 seconds.
  • 21:50 - Duplicating the action to create "text two in", updating the attribute target to 2, assigning a timeline delay start property of 0.6 seconds, and switching the animation mode from to to from at 100%.
  • 23:27 - Creating the "image one arrow 2" action targeting data-background-image="1", assigning a 0.5 second duration with a starting point of 0.4 seconds, and choosing a to opacity value of 0%.
  • 31:07 - Selecting the secondary collection list wrapper for the cube (.pictures_cube-lister), setting its layout to absolute full, its display to centered flex, and switching its pointer-events property to none.
  • 31:26 - Activating the advanced 3D transforms panel settings on the cube list wrapper and inputting a children perspective distance value of 1000px.
  • 32:06 - Navigating to the .pictures_cube-list element, forcing its aspect ratio to square, positioning it relative, and assigning the custom CSS properties transform-style: preserve-3d and container-type: size.
  • 33:60 - Writing a dynamic CSS function constraint for the .pictures_cube-list width property using min(30vw, 30vh).
  • 34:56 - Structuring the inner collection item (.pictures_cube-item) as absolute full, reverting its pointer events to auto, and generating an inner .pictures_cube-image-wrapper containing the CMS image styled as cover with an added 3% uniform layout padding.
  • 37:46 - Dropping an HTML Embed element (.pictures_cube-css) inside the image wrapper containing the automated custom item CSS loops mapped to the nth-child pseudoclass and custom CMS numeric transform/origin fields.
  • 39:55 - Applying an initial workspace correction to the .pictures_cube-lister container by giving it a static move X transform value of -35%, then flipping the cube collection list sorting order to map from smallest to largest.
  • 41:44 - Orchestrating the "cube one arrow 2" action in the timeline targeting data-animate="cube-list" with a 1.3 second duration and a to Y-axis rotation value of 90°.
  • 43:24 - Creating the accompanying translation action "cube move one arrow 2" targeting data-animate="cube-listwrapper" at a 1.3 second duration, altering its move X property to a target value of -7.5%.
  • 45:26 - Configuring subsequent multi-axis milestones sequentially across the animation timeline (e.g., updating target Y-rotations to 180°, translating X-axis points to 17.5%, or switching to an X-axis rotation variant of -90°).
  • 46:58 - Appending a structural layout fix to the .main-container by setting its CSS overflow property to hidden to clear runtime viewport bleeding.
  • 47:40 - Introducing a global embedded utility style component (.global-styles) configured with position none, positioned at the top of the navigator hierarchy, containing modern platform overrides to permanently disable native overscroll browser bounce behaviors.

Dynamic 3D Cubic Gallery Masterclass

Step 1: Base Section & Viewport Locking Configuration (05:31 - 07:29)

How to Implement:

  1. Add a Section element and assign it the class .section. Use this class to set its height to 500 VH.
  2. Inside the section, insert a Div Block (.main-container). Set its position to sticky with a top offset of 0.
  3. Adjust the .main-container sizing properties by setting its height to 100 VH and its max-width to none.
  4. Add another Div Block inside the main container (.section_content-wrapper), applying 100% width and height.

Why It's Important: The 500 VH dimension establishes the global speed of the scroll interaction; making the section taller will organically slow down the animation. Applying a sticky position to the child container turns it into a locked, fixed "window" that keeps layout graphics anchored perfectly inside the viewport while the user scrolls down the document.

Step 2: Structuring the Background Elements & Typography Masks (07:29 - 13:03)

How to Implement:

  1. Insert a Collection List inside the content wrapper and assign the class .pictures_list-wrapper, sizing it to 100% width and height. Connect it to the "pictures" CMS collection and configure the sorting to use the order field from Largest to Smallest.
  2. Target the Collection Item (.pictures_list-item), setting its layout to absolute with the full option, and change its display property to flex aligned to the center.
  3. Inside the item, place an Image element set to 100% width and height. Configure its fit property to cover and bind it to the image field in the CMS.
  4. Under the image, add a Div Block (.pictures_overlay) positioned absolute full. Apply a linear gradient with stops at 0% (black 60%), 50% (transparent), and 100% (black 60%).
  5. Under the overlay, insert a Text Block positioned absolute and bound to the CMS text field. Utilize fluid typography by assigning a 12 VW font size, a unitless line-height of 1, and a capitalized text-transform.

Why It's Important: Sorting the items from largest to smallest guarantees that the dynamically generated absolute layers stack properly, naturally revealing item 1 on top at the beginning of the animation. Relying on VW (Viewport Width) units rather than static pixels ensures the typography remains structurally responsive and scales dynamically across varying screen environments.

Step 3: Architecting Attribute-Based Interaction Triggers (14:35 - 16:48)

How to Implement:

  1. Select the main trigger .section node, go to the Element Settings Panel, and add the Custom Attribute data-animate="section".
  2. Select the absolute background Collection Item and add the custom attribute data-background-image. Bind its value directly to the CMS order field.
  3. Select the text element and add a custom attribute named data-image-text, once again binding its value directly to the CMS order field.

Why It's Important: Standard combo classes limit the ability to control specific individual iterations of a CMS list. By linking a custom HTML attribute dynamically to a CMS order integer (e.g., data-background-image="1" versus data-background-image="2"), you decouple the animation from class names and allow exact, programmatic timeline isolation of each independent item.

Step 4: Scroll Timeline Sequencing & Staggered Masking (16:48 - 29:56)

How to Implement:

  1. Open the Interactions Panel, start a Page Scroll Interaction named "cube rotation and background image", and target it at the attribute data-animate="section". Apply a scroll smoothing of 0.8.
  2. Set execution thresholds so the animation begins when the top of the element hits the top of the viewport, and concludes when the bottom meets the bottom.
  3. At the 0s marker, move data-text="1" on the Y-axis to 100% using linear easing (0.4s duration). Inside the action settings, apply Stagger via Offset Time, activate Split Text by Word, and define an interval of 0.3s.
  4. At the 0.4s timeline mark, fade the current image by targeting data-background-image="1" with an Opacity action set to 0% over 0.5s.
  5. At the 0.6s mark, introduce the second text block targeting data-text="2" and moving it on the Y-axis from 100%.
  6. Copy these nodes to orchestrate the rest of the collection, injecting a 1.5 second gap between transition intervals (e.g., sequence two starts at 1.5s plus prior values).

Why It's Important: Using the native text-splitting by word allows you to generate staggered masking sequences while keeping edits manageable inside the Webflow canvas. The distinct 1.5-second pauses between structural transitions create a deliberate anchoring effect, establishing visual pacing and avoiding motion-blur fatigue.

Step 5: Assembling the Advanced 3D Component Perspective (30:09 - 36:36)

How to Implement:

  1. Embed a second Collection List within the primary container mapped to the same "Pictures" collection. Apply the class .pictures_cube-lister, setting its layout to absolute full, display to flex center, and pointer events to none.
  2. Open the 3D transforms panel dropdown for this wrapper and configure the Children Perspective to 1000px. Add the custom attribute data-animate="cube-lister".
  3. Target the inner list (.pictures_cube-list), lock the aspect ratio to square, and set the position to relative. Give it the custom attribute data-animate="cube-list".
  4. Using an HTML Embed or elements panel, apply two custom CSS properties to the list: transform-style: preserve-3d and container-type: size. Set the width using fluid mathematical constraints: min(30vw, 30vh).
  5. Structure the internal Collection Items as absolute full, revert pointer events to auto, and drop in a responsive image set to cover.

Why It's Important: To stop web browsers from automatically collapsing transformed children elements onto the flat 2D plane of the parent, transform-style: preserve-3d ensures your structural layers interact properly in 3D space. The min(30vw, 30vh) configuration forces the cube to maintain perfect proportions dynamically, optimizing the design automatically for horizontal monitors and vertical mobile screens.

Step 6: Constructing the Cube Geometry via CSS (36:36 - 40:32)

How to Implement:

  1. Inside the collection item, add an HTML Embed and inject a CSS snippet. The CSS must map the pseudo-selector :nth-child() securely to the CMS numeric integer field.
  2. Within this snippet, utilize the eight specialized CMS decimal and integer fields to programmatically dictate individual output properties for transform-origin and spatial axes transforms (move X/Y/Z, rotate X/Y/Z).
  3. Move up to the .pictures_cube-lister and assign a static move X transform of -35%.
  4. Reverse the Collection List sort order within the elements panel to process the dataset from Smallest to Largest.

Why It's Important: By automating mathematical layout configurations with :nth-child() logic tied to backend CMS variables, you circumvent writing cumbersome manual CSS rules for every individual face. The system intrinsically maps geometric calculations right out of the box. Additionally, shifting the structure -35% on the X-axis positions the element asymmetrically so the cube and text components operate visually in separate column zones.

Step 7: Multi-Axis Synchronization & Final Polish (40:35 - 48:58)

How to Implement:

  1. Return to the core page scroll interaction. At the 0s interval, create a Rotate node targeting data-animate="cube-list". Render a 1.3s To sequence, configuring the Y property to 90°.
  2. In tandem at the 0s mark, introduce a Move transition mapped to data-animate="cube-lister". Generate a 1.3s To command establishing a target X value of -7.5%. Scale this logic linearly for items 3 through 6.
  3. Close interactions and choose .main-container. Alter its CSS overflow property parameters to hidden.
  4. Create an HTML Embed at the topmost node of your web project. Label it .global-styles configured with display: none. Write a CSS rule to explicitly disable overscroll bounce behaviors.

Why It's Important: Combining Y-axis rotations with subtle X-axis spatial translations generates a refined organic pathing illusion; the cube seemingly travels along the borders of an invisible hexagon instead of merely spinning in place. Updating the container overflow blocks graphical layout errors where rotating polygons briefly clip past viewport rendering boundaries. Employing global style embeds eliminates macOS's native physics system (elastic white-space bouncing), rendering a seamless application experience.

FAQs

How do you create a fully CMS-driven 3D rotating cube gallery in Webflow?

To build a CMS-powered 3D cube, structure a collection list wrapper to provide a 1000px children perspective, set the collection list aspect ratio to square, and apply transform-style: preserve-3d. Inside the collection item, use an HTML Embed to dynamically map custom numeric CMS transform fields (move X/Y/Z, rotate X/Y/Z) to an automated item CSS loop using the nth-child pseudoclass.

How do you fix percentage-based Z-axis depth transforms for Webflow 3D elements?

Browsers natively tend to flatten 3D transformed child elements onto the flat plane of their parent container. To force proper 3D rendering and make percentage-based values scale correctly when applying a move transform along the Z-axis (depth), you must explicitly apply the custom CSS properties transform-style: preserve-3d and container-type: size to the parent container element.

How do you trigger and target multiple distinct scroll animations independently of class names in Webflow?

Instead of class-based animations, use attribute-based interaction mapping by assigning custom HTML attributes (e.g., data-animate="section") to your trigger section. To animate separate elements within the same timeline smoothly, apply custom attributes bound dynamically to a CMS order field (e.g., data-background-image="1") to uniquely isolate and target target elements inside your interaction timeline.

How do you stop screen overflow bleeding and native browser overscroll bounce in Webflow?

To prevent horizontal layout bleeding from moving elements, set the outer container's overflow property to hidden. To permanently eliminate the white, bouncy background gap that appears when users scroll past the viewport limits on devices like macOS, add a global HTML embed element at the top of your page hierarchy containing a custom CSS utility rule targeting the overscroll bounce behavior.

This is some text inside of a div block.