# GSAP + Glaze + BricksBuilder

<span>You’ll first build a solid mental model of how animations work, then learn </span>**GSAP properly**<span> , and finally learn how to use </span>**Glaze efficiently**<span> for everyday work—without losing the ability to create advanced custom animations later.</span>

# Glaze for WP (and for BricksBuilder)

> Update 1.2.0: Preview of the Presets directly in the WP backend now possible.
> Update 1.3.0: Preview improved (more reliable + scroll preview)

## What It Is

Glaze for WP / Bricks brings Tailwind-style, attribute-driven animations to any WordPress site (with special affinity for Bricks Builder) using GSAP under the hood. Instead of writing JavaScript for every animated element, you simply add a `data-animate` attribute (or a CSS class, if you prefer class-based syntax) containing a compact animation string, and the plugin's runtime script parses it and drives GSAP automatically.

## The Technology Behind It

**GSAP (GreenSock Animation Platform)** is the actual animation engine — it handles all the tweening, easing, timelines, and (optionally) scroll-triggered behavior via its ScrollTrigger plugin. GSAP itself knows nothing about your markup; it's just invoked with the right parameters.

**GlazeJS** is a small ES module that sits between your HTML and GSAP. It scans the DOM for elements carrying the configured attribute (default `data-animate`) or class prefix (default `glaze-`), parses the pipe-separated animation string on each one (e.g. `from:opacity-0|y-30|duration-0.6`), and translates that into the equivalent GSAP `.from()`/`.to()`/timeline calls — including support for staggered children (`[&>*]:...`), nested selectors (`[&_path]:...`), transform origins, easing functions, and scroll-trigger properties.

This plugin is the WordPress "glue" layer: it loads GSAP and Glaze (from CDN or a locally hosted copy), lets you configure breakpoints, global defaults, reusable presets, and automatic class-to-animation mappings through an admin UI, and injects the resulting configuration into the front end via a small runtime script (`runtime.js`). Everything is stored in flat JSON/files (`/config/settings.json`, `/lib/*.js`) rather than the options table, and is fully removed on uninstall.

## Getting Started

1. After activation, go to the **Glaze** menu item in the WordPress admin sidebar (superhero icon).
2. The admin screen has five tabs: **Libraries**, **General**, **Breakpoints & Defaults**, **Presets**, and **Mappings**.
3. Make your changes and click **Save changes** in the header. **Reset** restores all settings to plugin defaults (with a confirmation prompt).

## Tab-by-Tab Guide

### Libraries

Controls how GSAP, ScrollTrigger, and GlazeJS are loaded on the front end.

- **Library mode**: `CDN` (jsDelivr/esm.sh — fastest to set up, but relies on external requests) or `Local` (serves self-hosted copies from the plugin's `/lib` folder — no third-party requests, but you must download each library first).
- **Use existing global GSAP**: Enable if another plugin or your theme already loads GSAP globally, so Glaze reuses `window.gsap` instead of loading a duplicate copy.
- **Local libraries** section: For each library (GSAP core, ScrollTrigger, GlazeJS), you can **Download** (fetch a fresh local copy), **Refresh** (re-download an existing one, e.g. after an update), or **Remove** (delete the local copy, falling back to CDN until re-downloaded).

### General

- **Data attribute**: The HTML attribute Glaze scans for animation strings (default `data-animate`). Only change this if it conflicts with another plugin/theme.
- **Class prefix**: Prefix for the optional class-based syntax (default `glaze`), e.g. `class="glaze-from:opacity-0"`.
- **Enable ScrollTrigger**: Registers GSAP's ScrollTrigger plugin so animations can react to scroll position (`scrollTrigger.*` properties).
- **Force GPU (force3D)**: Forces animated elements onto GPU-accelerated layers for smoother motion (may occasionally blur very small text).
- **Watch DOM & re-animate**: Watches for elements added after page load (AJAX content, Bricks-injected components) and automatically animates them.
- **Watch debounce (ms)**: Delay before re-scanning after a DOM change is detected (only relevant when Watch DOM is on).
- **Run inside Bricks builder**: Plays animations live while editing in the Bricks canvas (off by default, since constant replay can be distracting while building).
- **Verbose console logging**: Logs parsed animation strings and GSAP calls to the console — useful for debugging, should stay off in production.

### Breakpoints & Defaults

- **Breakpoints**: Named media queries used for responsive prefixes like `@tablet:from:opacity-0`. The name is what you reference in animation strings; the value is the actual CSS media query. `default` applies when no `@breakpoint:` prefix is used.
- **Global defaults**: Baseline settings applied automatically everywhere.
  - **Element defaults**: Fallback properties for every animated element (e.g. `duration-0.6|ease-power2.out`) — overridden by anything set directly in an element's own attribute.
  - **Timeline defaults**: Applied to every internal timeline Glaze creates (every element is wrapped in one), useful for a global easing or default `scrollTrigger.trigger`.

### Presets

Reusable animation strings referenced by name inside `data-animate` (e.g. `fadeIn`) or as a class (`glaze-preset-fadeIn`). Categories only organize the admin UI — they have no front-end effect, and **preset names must be unique across all categories** since they're flattened into one list at runtime.

- **+ Add category** / **+ Preset**: create new groups/rows.
- **Preview** button on each preset row: opens a modal that plays the animation live on a sample layout (single box, staggered row, grid, or SVG shape) using the same GSAP/Glaze pipeline as the front end — handy for tuning a string without leaving wp-admin.
- Advanced users can bypass the UI and hand-edit `/config/settings.json` directly to add many more presets at once; a `glaze-opus-docs-2.0.md` file in the plugin directory documents the full animation-string syntax for AI-assisted ("Vibe Coding") editing.

### Mappings

Automatically attaches an animation to every element matching a CSS class or selector, without needing to add `data-animate` manually — useful for markup you don't control directly (e.g. icon wrappers Bricks generates).

- Left field: CSS class or selector to match (e.g. `.fv-a-icon` or `fv-a-icon`).
- Right field: raw animation string or `preset-name` to apply.

## Writing Animation Strings

Animation strings are pipe-separated (`|`) key-value pairs, e.g.:

```
from:opacity-0|y-30|duration-0.6|ease-power2.out
```

Common patterns:

- `from:` / `to:` define the animated state (properties before the tween vs. the target values).
- Numeric values follow the property name with a hyphen: `y-30`, `scale-0.8`, `rotate-[-45]` (brackets allow negative/complex values).
- `duration-`, `delay-`, `ease-`, `stagger-`, `repeat-`, `yoyo-` control timing/behavior.
- `[&>*]:` prefix applies the animation with staggering to direct children (useful for lists/grids).
- `[&_path]:` / `[&_line]:` prefix targets nested SVG elements.
- `scrollTrigger.trigger-[&]`, `scrollTrigger.start-[top_85%]`, `scrollTrigger.scrub-1`, etc. wire the animation to scroll position (requires **Enable ScrollTrigger**).
- `@breakpoint:` prefix (e.g. `@tablet:from:opacity-0`) restricts a rule to a given breakpoint's media query.

## Practical Notes

- Changing the **library mode** to Local without first downloading a library will simply cause the plugin to fall back to CDN for that library until you download it.
- **Watch DOM** uses a Firefox-safe, childList-only `MutationObserver` — it intentionally never listens for attribute changes, avoiding an infinite re-trigger loop that can occur when GSAP rewrites inline styles.
- If **Run inside Bricks builder** is left off, animations simply won't fire while editing in the canvas; they still work normally on the live front end.
- All plugin data lives in flat files (`/config/settings.json`, `/lib/*.js`) — nothing is stored in the WordPress options table, and everything is deleted automatically on uninstall.

# Glaze Cheat Sheet

*Tailwind-style animations for HTML. Describe GSAP animations with data attributes—no JS animation code.*

---

## 1. Core Concept

Glaze is a **~3kb syntax layer on top of GSAP**. You write animation strings in HTML attributes; Glaze parses them into GSAP calls.

```html
<div data-animate="from:opacity-0|y-50|duration-1"></div>
<!-- becomes -->
gsap.from(element, { opacity: 0, y: 50, duration: 1 })
```

**Requirements:** GSAP must be installed and passed to Glaze. (GSAP has its own license—check compliance for commercial use. Glaze itself is MIT.)

**Features:** responsive breakpoints (via GSAP `matchMedia`), timelines, dot notation for nested props, full GSAP access (ScrollTrigger, easing, stagger), library-agnostic design.

---

## 2. Install & Setup

```bash
npm i glazejs   # or yarn add / pnpm add / bun add glazejs
```

```js
import gsap from "gsap";
import glaze from "glazejs";

glaze({
  lib: { gsap: { core: gsap } },   // ONLY required option
});
```

**CDN:**

```js
import glaze from "https://esm.sh/glazejs";
import gsap from "https://esm.sh/gsap";
glaze({ lib: { gsap: { core: gsap } } });
```

Glaze auto-finds all `data-animate` elements and animates them.

---

## 3. Full Configuration

```js
glaze({
  lib: { gsap: { core: gsap } },          // required

  element: document.querySelector("#app"), // search scope (default: document)
  dataAttribute: "data-move",              // custom attr (default: "data-animate")
  className: "animate",                    // enable class-based syntax instead

  breakpoints: {                           // responsive (GSAP matchMedia)
    default: "(min-width: 1px)",           // runs when no bp specified
    sm: "(min-width: 640px)",
    md: "(min-width: 768px)",
    lg: "(min-width: 1024px)",
  },

  defaults: {                              // global default settings
    tl: "ease-power2.inOut",               // applies to every timeline
    element: "duration-1",                 // applies to every element
  },

  presets: {                               // reusable animation shortcuts
    fadeIn: "from:opacity-0|duration-1",
    slideUp: "from:y-50|opacity-0",
  },

  watch: { debounceTime: 500 },            // watch DOM & re-animate (default: false)
});
```

---

## 4. Anatomy of an Animation String

```
[breakpoint][selector]:state:properties
```

Example: `@lg:[&>div]:from:opacity-0|y-50|stagger-0.1|duration-0.5|ease-power2.out`

---

## 5. States *(every animation needs one)*

| Syntax            | GSAP equivalent | Meaning                                          |
| ----------------- | --------------- | ------------------------------------------------ |
| `from:...`        | `gsap.from`     | Start at these values → animate to natural state |
| `to:...`          | `gsap.to`       | Animate from current state → these values        |
| `from:... to:...` | `gsap.fromTo`   | Animate between two explicit states              |

```html
<div data-animate="from:opacity-0"></div>
<div data-animate="to:xPercent-50"></div>
<div data-animate="from:opacity-0.5 to:opacity-1"></div>
```

---

## 6. Properties

- List after state, separated by **pipes `|`**.
- Parsed by splitting at the **dash `-`**: first part = property name, second = value.
- Values auto-convert to number, boolean, or string.

```html
<div data-animate="to:opacity-1|yPercent-10|duration-0.5"></div>
```

| Need                                 | Syntax                             | Result                      |
| ------------------------------------ | ---------------------------------- | --------------------------- |
| **Nested props** (dots)              | `to:scale.x-2\|scale.y-2`           | `{ scale: { x: 2, y: 2 } }` |
| **Negative values** (brackets)       | `to:xPercent-[-50]`                | `{ xPercent: -50 }`         |
| **Values with spaces** (underscores) | `to:boxShadow-[0_0_50px_20px_red]` | `0 0 50px 20px red`         |

---

## 7. Breakpoints

Prefix with `@breakpoint:` — animation only runs at that screen size.

```html
<div data-animate="@sm:from:opacity-0"></div>          <!-- sm only -->
<div data-animate="@lg:from:opacity-0|rotate-180"></div>
```

**Stack multiple** with spaces; larger breakpoints add to/override smaller:

```html
<div data-animate="@sm:from:opacity-0|y-50 @lg:from:scale-0.5"></div>
```

`default` breakpoint runs when none specified (defaults to `(min-width: 1px)`).

---

## 8. Selectors *(target children)*

Bracket notation `[selector]:`. The `&` = the parent element.

```html
<div data-animate="[&>h1]:to:opacity-1|stagger-0.25">
  <h1>One</h1><h1>Two</h1><h1>Three</h1>
</div>
```

Combine with breakpoints: `@sm:[&>h1]:to:opacity-1|stagger-0.25`

---

## 9. Alternative Input Modes

**Custom attribute:** set `dataAttribute: "data-move"` → use `<div data-move="from:opacity-0">`.

**Class-based:** set `className: "animate"` → write:

```html
<div class="animate-from:opacity-0|duration-1"></div>
```

---

## 10. Timelines (`tl`)

Add `tl` keyword to make an element a **timeline container**. All child elements are automatically included in its scope.

```html
<div data-animate="tl defaults:ease-elastic|duration-1">
  <div data-animate="to:rotate-360"></div>
  <div data-animate="to:rotate-360"></div>
</div>
```

**Timeline defaults:** `tl defaults:ease-elastic|duration-4 yoyo-true`

**Timing control** with `tl:[...]` on children:

| Value      | Meaning                                     |
| ---------- | ------------------------------------------- |
| `tl:[+=1]` | Start 1s **after** previous ends            |
| `tl:[-=1]` | Start 1s **before** previous ends (overlap) |
| `tl:[<]`   | Start **at same time** as previous          |

**Responsive timelines:** mix breakpoints inside children:

```html
<div data-animate="tl defaults:power2.inOut|duration-2">
  <div data-animate="to:rotate-360 @lg:to:xPercent-[50]"></div>
  <div data-animate="tl:[-=1] to:rotate-360 @lg:to:xPercent-[-50]"></div>
</div>
```

**Named timelines** — reference from anywhere in the DOM:

```html
<div data-animate="tl/main defaults:power2.inOut|duration-2">
  <div data-animate="to:rotate-360"></div>
</div>
<div data-animate="tl:main-[-=1] to:scale-1.5"></div>
```

`tl/name` = create named timeline · `tl:name` = join it.

> **Internally, Glaze creates a timeline for every element** even without `tl`, so `tl` defaults apply globally.

---

## 11. ScrollTrigger

No special syntax—it's just the GSAP `scrollTrigger` property via dot notation.

**Setup:**

```js
import ScrollTrigger from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
```

```html
<div data-animate="from:opacity-0|y-50|scrollTrigger.trigger-[&]"></div>
```

| Option                             | What it does                                           |
| ---------------------------------- | ------------------------------------------------------ |
| `scrollTrigger.trigger-[&]`        | Element that triggers animation (`[&]` = this element) |
| `scrollTrigger.start-[top_center]` | When to start (element pos + viewport pos)             |
| `scrollTrigger.end-[bottom_top]`   | When to end                                            |
| `scrollTrigger.scrub=true`         | Link progress to scroll position                       |
| `scrollTrigger.markers=true`       | Show debug markers                                     |

> Use underscores for spaces: `[top_center]` not `[top center]`.

---

## 12. Defaults (behavior)

```js
defaults: {
  tl: "defaults:ease-power2.inOut scrollTrigger.trigger-[&]", // every timeline
  element: "duration-1",                                       // every element
}
```

Because every element gets an internal timeline, `tl` defaults effectively apply everywhere (great for global ScrollTrigger/easing).

**Override per element:**

```html
<div data-animate="from:opacity-0"></div>            <!-- default 1s -->
<div data-animate="from:opacity-0|duration-0.5"></div> <!-- overrides -->
```

---

## 13. Presets

Define reusable strings in config; reference with `preset-`.

```js
presets: {
  fadeIn: "from:opacity-0|duration-1",
  slideUp: "from:y-50|opacity-0|duration-0.5",
  helicopter: "from:rotate-2160|duration-5",
}
```

```html
<div data-animate="preset-fadeIn"></div>
<div class="animate-preset-helicopter"></div>        <!-- class mode -->
<div data-animate="preset-fadeIn scrollTrigger.trigger-[&]"></div> <!-- combine -->
```

Presets simply expand into the full string before processing.

---

## 14. Quick Syntax Reference

| Token                  | Purpose                         |
| ---------------------- | ------------------------------- |
| `from:` `to:`          | states (fromTo = use both)      |
| `\|`                    | separates properties            |
| `-`                    | splits property name / value    |
| `.`                    | nested property (`scale.x-2`)   |
| `[-50]`                | negative / literal value        |
| `[0_0_50px_red]`       | value with spaces (underscores) |
| `@bp:`                 | breakpoint prefix               |
| `[&>sel]:`             | child selector (`&` = parent)   |
| `tl`                   | timeline container              |
| `tl/name`              | named timeline                  |
| `tl:name` / `tl:[+=1]` | join timeline / timing offset   |
| `preset-name`          | apply preset                    |
| `defaults:`            | timeline-scoped defaults        |
| `scrollTrigger.*`      | ScrollTrigger options           |
| `[&]`                  | "this element" (trigger/self)   |

---

**Full complex example:**

```html
<div data-animate="@lg:[&>div]:from:opacity-0|y-50|stagger-0.1|duration-0.5|ease-power2.out">
  <div>Card 1</div><div>Card 2</div><div>Card 3</div>
</div>
```

*On large screens, each child div fades in from 50px below, staggered by 0.1s.*