Entry Point

This space introduces an initial idea without defining its outcome. Forms exist in a state of balance, shaped by intention rather than urgency.

Gesture

Form and expression intersect without explanation. The subject exists between motion and stillness, marked by attitude rather than intent.

Variation

This section explores difference through placement and proportion. Repetition is avoided in favor of subtle change.

The Stance

A clearer position begins to take shape. Elements feel grounded, deliberate, and visually assured.

The layout favors stability over motion, offering a moment of visual certainty.

Stillness

This space holds without interruption. Nothing shifts unnecessarily, allowing form and color to remain uninterrupted.

Release

The composition loosens its grip and allows space to dominate. Elements soften, contrast fades, and structure becomes secondary.

FollowArt Scroll Animation Tutorial

1. CDN Scripts

Add these scripts to your <head> or before the closing </body> tag:

<!-- GSAP -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/ScrollTrigger.min.js"></script>

<!-- Lenis Smooth Scroll -->
<script src="https://unpkg.com/lenis@1.1.18/dist/lenis.min.js"></script>

2. Required CSS

/* Section base styles */
[data-followart-section] {
  position: relative;
  width: 100%;
  height: 100svh;
  min-height: 100svh;
  overflow: hidden;
}

/* Container that rotates */
[data-followart-container] {
  position: relative;
  width: 100%;
  height: 100%;
  padding: 2rem;
  display: flex;
  transform: rotate(var(--followart-rotation, 30deg));
  transform-origin: var(--followart-origin, bottom left);
  will-change: transform;
}

/* Column layout helper */
[data-followart-col] {
  flex: 1;
  display: flex;
}

/* Hide scrollbar (optional) */
::-webkit-scrollbar {
  display: none;
}

3. JavaScript

gsap.registerPlugin(ScrollTrigger);

document.addEventListener("DOMContentLoaded", () => {
  // Initialize Lenis smooth scroll
  const lenis = new Lenis();
  lenis.on("scroll", ScrollTrigger.update);
  gsap.ticker.add((time) => lenis.raf(time * 1000));
  gsap.ticker.lagSmoothing(0);

  // Get all sections with the data attribute
  const sections = document.querySelectorAll("[data-followart-section]");

  sections.forEach((section, index) => {
    const container = section.querySelector("[data-followart-container]");
    if (!container) return;

    // Read custom rotation from data attribute or CSS variable
    const rotation = section.dataset.followartRotation ||
      getComputedStyle(container).getPropertyValue("--followart-rotation") || "30deg";

    // Set initial rotation via CSS variable
    container.style.setProperty("--followart-rotation", rotation);

    // Animate rotation to 0 on scroll
    gsap.to(container, {
      rotation: 0,
      ease: "none",
      scrollTrigger: {
        trigger: section,
        start: "top bottom",
        end: "top 20%",
        scrub: true,
      },
    });

    // Pin section unless it's marked with data-followart-no-pin
    const shouldPin = !section.hasAttribute("data-followart-no-pin");

    if (shouldPin) {
      ScrollTrigger.create({
        trigger: section,
        start: "bottom bottom",
        end: "bottom top",
        pin: true,
        pinSpacing: false,
      });
    }
  });
});

4. HTML Structure

<!-- Basic Section -->
<section data-followart-section>
  <div data-followart-container>
    <div data-followart-col>
      <h1>Your Heading</h1>
    </div>
    <div data-followart-col>
      <p>Your content here.</p>
    </div>
  </div>
</section>

<!-- Section with custom rotation -->
<section data-followart-section data-followart-rotation="45deg">
  <div data-followart-container>
    <!-- content -->
  </div>
</section>

<!-- Last section (disable pinning) -->
<section data-followart-section data-followart-no-pin>
  <div data-followart-container>
    <!-- content -->
  </div>
</section>

5. Data Attributes Reference

Attribute Element Description
data-followart-section section Marks element as an animated section (required)
data-followart-container div The container that rotates (required)
data-followart-col div Flex column helper (optional)
data-followart-rotation section Custom initial rotation (default: 30deg)
data-followart-no-pin section Disable pinning for this section

6. CSS Variables (Alternative Customization)

/* Override rotation per section */
.my-section [data-followart-container] {
  --followart-rotation: 45deg;
  --followart-origin: bottom right;
}

/* Custom colors via inline styles or classes */
.my-section [data-followart-container] {
  background-color: #8e9487;
  color: #000;
}

7. Webstudio Setup

For Webstudio: Add the CSS to your project's custom CSS and the JavaScript to your custom code section. Then use the data attributes on your elements:

1. Go to Project Settings → Custom Code
2. Add GSAP & Lenis scripts to <head>
3. Add the JavaScript before </body>
4. Add required CSS to your stylesheet
5. Apply data attributes to your elements:
   - Add "data-followart-section" to section elements
   - Add "data-followart-container" to the inner container
   - Add "data-followart-col" to column divs
   - Add "data-followart-no-pin" to the last section

8. Tips & Tricks

Tip Description
Taller sections Set height: 200svh on section for longer scroll duration
Different origins Use --followart-origin: bottom right for reverse rotation
No smooth scroll Remove Lenis initialization to use native scrolling
Mobile Add flex-direction: column to container on small screens