Skip to main content

Glaze for WP (and for BricksBuilder)

Here is comprehensive documentation for the Glaze for WP / Bricks plugin (v 1.1.0).

Overview

Glaze for WP / Bricks is a WordPress plugin that brings Tailwind-style, declarative GSAP animations to any WordPress site, with first-class support for the Bricks page builder. Instead of writing JavaScript animation code, you describe animations directly in HTML using data-animate attributes (or CSS classes), and the plugin's runtime translates those strings into GSAP calls via the lightweight ~3kb GlazeJS library.

Everything the plugin needs — settings, presets, breakpoints, and downloaded libraries — is stored in flat files inside the plugin directory (/config and /lib). No custom database tables, options, transients, or custom post types are created, and every trace is removed cleanly on uninstall.

The plugin is authored by art10m (with Claude Opus 4.8) and licensed MIT. Note that GSAP itself carries its own license, which you should review for commercial use.


Core Concept

A Glaze animation is expressed as a pipe-separated string in an HTML attribute:

<div data-animate="from:opacity-0|y-50|duration-1"></div>

This is parsed into the equivalent GSAP call:

gsap.from(element, { opacity: 0, y: 50, duration: 1 });

The animation string follows the anatomy [breakpoint][selector]:state:properties, where:

  • State is from:, to:, or both (mapping to gsap.from, gsap.to, and gsap.fromTo).
  • Properties are separated by pipes |, split on the dash - into name/value pairs.
  • Breakpoints are prefixed with @name: (e.g. @tablet:from:opacity-0).
  • Child selectors use bracket notation [&>div]: where & is the element itself.
  • Presets are referenced with preset-name and expand into their full string.

The full syntax is documented in the bundled glaze-opus-docs-2.0.md "skill" file, which also serves as an AI-readable reference for generating presets via "Vibe Coding."


Installation & Activation

  1. Upload the glaze-wp-bricks folder to /wp-content/plugins/. (Or install the ZIP)
  2. Activate the plugin from the WordPress Plugins screen.

On activation (glaze_wpb_activate in glaze-wp-bricks.php), the plugin:

  • Creates the /config and /lib directories if missing.
  • Drops a silent index.php (<?php // Silence is golden.) into each to prevent directory listing.
  • Writes a .htaccess (Deny from all) into /config so the settings JSON is not publicly served.
  • Seeds /config/settings.json with the defaults from Glaze_WPB_Settings::defaults() if it doesn't already exist.

A new top-level Glaze menu item (superhero dashicon) appears in the WordPress admin.


The Admin Interface

The settings screen is a self-contained single-page app rendered by Glaze_WPB_Admin::render(). The PHP outputs only a skeleton — a header with Reset and Save changes buttons, a tab bar, five empty <section> panels, and a toast container. All panel content is generated client-side by admin.js, which reads two localized globals:

  • window.GlazeWPBAdmin — AJAX URL, nonce, current config, and library states.
  • window.GlazeWPBStrings — all UI labels and hint text (from admin-strings.js), keeping copy separate from logic.

The interface is styled by admin.css in a dark theme scoped under .glaze-wpb, using CSS custom properties for colors, radius, and accents. It also darkens the surrounding WordPress chrome (#wpwrap, #wpbody-content).

The five tabs are:

1. Libraries

Controls how GSAP and GlazeJS are loaded:

  • Library modeCDN (loads from jsDelivr / esm.sh, zero setup) or Local (serves copies from the plugin's /lib folder).
  • Use existing global GSAP — when enabled, the plugin does not load its own GSAP and instead reuses an existing window.gsap (e.g. from a dedicated GSAP WordPress plugin), avoiding duplicate scripts and version conflicts.
  • A Local libraries card lists each registered library (gsap, scrolltrigger, glaze) with a badge showing whether a local copy exists, plus Download/Refresh and Remove buttons.

Library operations are handled by AJAX (glaze_wpb_lib) and delegated to Glaze_WPB_Libs (see Technical Processes below).

2. General

  • Attributes — the data attribute name (data-animate) Glaze scans, and the class prefix (glaze) for the optional class-based syntax. Change these only to avoid conflicts.
  • Behavior toggles:
    • Enable ScrollTrigger — registers GSAP's ScrollTrigger plugin for scroll-based animations.
    • Force GPU (force3D) — forces hardware-accelerated transforms.
    • Watch DOM & re-animate — enables a Firefox-safe MutationObserver for dynamically added content (see below), with a configurable debounce (ms).
    • Run inside Bricks builder — allows animations to run live inside the Bricks editing canvas.
    • Verbose console logging — logs parsing and GSAP calls for debugging.

3. Breakpoints & Defaults

  • Breakpoints — name/media-query pairs that power responsive prefixes like @tablet:. The special default name is used when no breakpoint prefix is given.
  • Global defaultselement defaults (applied as a fallback to every animated element, e.g. duration-0.6|ease-power2.out) and tl timeline defaults (applied to every internal timeline, e.g. defaults:ease-power2.out).

4. Presets

Reusable animation strings organized into named categories (Fades, Scale, Slide, Zoom, Bounce, SVG Draw, Stagger, etc.). Categories exist only to organize the admin UI — they have no front-end effect and are flattened into a single map before delivery. Preset names must be unique across all categories. You reference a preset with preset-name (or glaze-preset-name in class mode).

5. Mappings

Automatic class mappings attach an animation to every element matching a CSS class/selector without editing each element's markup — useful for markup you don't control (e.g. Bricks-generated icon wrappers). Each row pairs a selector (e.g. .fv-a-icon or fv-a-icon) with an animation string or preset-name.


Saving, Resetting, and Data Flow

When you click Save changes, admin.js's collect() walks the rendered DOM, rebuilds a config object (validating and defaulting empty fields), and POSTs it as JSON to the glaze_wpb_save AJAX action. On the server, Glaze_WPB_Admin::ajax_save():

  1. Runs the guard() check (capability + nonce).
  2. Decodes the JSON payload.
  3. Merges it onto the defaults with array_replace_recursive() so the structure always stays intact and new keys survive.
  4. Persists it via Glaze_WPB_Settings::save(), which writes pretty-printed JSON to /config/settings.json.

Reset (glaze_wpb_reset) rewrites the file with defaults() and re-renders the UI.

Glaze_WPB_Settings::get() always reads the file and merges it onto the defaults, so a corrupt or partial file safely falls back to defaults.


The Front-End Runtime

Glaze_WPB_Frontend::enqueue() runs on wp_enqueue_scripts (priority 20) and orchestrates loading:

  1. It reads the merged config. If loadInBuilder is off and the code is running inside the Bricks builder (bricks_is_builder()), it bails out entirely.
  2. Unless useExternalGsap is set, it enqueues GSAP (and ScrollTrigger, if enabled) from the URL resolved by Glaze_WPB_Libs::url() for the active mode.
  3. It enqueues runtime.js, depending on the GSAP scripts.
  4. It injects a window.GlazeWPBricks payload (via wp_add_inline_script(..., 'before')) containing the Glaze module URL, settings, breakpoints, defaults, flattened presets, and animation mappings.

runtime.js then executes this flow:

  1. Waits for DOMContentLoaded, then polls for gsap (up to a 5s timeout).
  2. Optionally applies force3D config and registers ScrollTrigger.
  3. Runs applyAnimationMappings() — for each mapping, it queries matching elements and appends the animation class(es).
  4. Runs prepareSVGs() — for SVGs flagged as "draw" animations, it measures each strokeable child's total length via getTotalLength() and sets strokeDasharray/strokeDashoffset so line-drawing effects work.
  5. Dynamically imports the Glaze ESM module from glazeUrl and calls glaze(glazeConfig), passing GSAP core, the data attribute, class name, breakpoints, defaults, and presets.
  6. Fires a glazeReady window event exposing glaze, gsap, the config, and a reinit function so other integrations can re-run Glaze after their own AJAX loads.

The Firefox "looping" safeguard

A key technical detail: Glaze's own native watch mode is deliberately never enabled. Its internal observer reacts to attribute mutations, and since GSAP rewrites inline styles on every tick, native watch would re-trigger Glaze on its own output — a mutate → re-init → mutate loop that Firefox renders as endlessly restarting animations.

Instead, the Watch DOM & re-animate toggle drives a custom, Firefox-safe MutationObserver that listens for childList changes only (never attributes). When new nodes appear, it checks whether they are SVGs or match any mapping, and if so schedules a debounced re-init (re-preparing SVGs, re-running Glaze, and refreshing ScrollTrigger). When the toggle is off, no observer is attached at all. Either way, scheduleReinit remains exposed through the glazeReady event for manual re-runs.


Technical Processes in Detail

Library management (Glaze_WPB_Libs)

A static registry() defines each library's runtime CDN URL, download URL, local filename, and type (script vs module):

  • gsapgsap.min.js from jsDelivr.
  • scrolltriggerScrollTrigger.min.js from jsDelivr.
  • glazeglaze.module.js, downloaded from esm.sh/glazejs?bundle&target=es2020.

url($key, $mode) returns the local /lib URL when mode is local and a local copy exists, otherwise the CDN URL. is_local() simply checks whether the local file exists.

download($key) fetches the source via wp_remote_get() (30s timeout, following redirects), validates the HTTP status and non-empty body, and writes it into /lib.

ESM import rewriting — a notable subtlety: esm.sh often returns a thin ESM shim whose specifiers are root-relative (e.g. export * from "/glazejs@2.0.1/es2020/glazejs.bundle.mjs";). Those only resolve against the CDN's origin. Since the plugin serves the file from your own WordPress domain, absolutize_root_relative_imports() uses a regex to rewrite from/import specifiers that begin with / into fully-qualified URLs pointing back at the CDN origin, so the locally-served module still resolves its dependencies correctly.

Preset flattening

Glaze_WPB_Settings::flatten_presets() collapses the categorized preset structure (category → { name → string }) into a single flat name → string map, which is what Glaze actually consumes at runtime. This is why preset names must be globally unique.

Security

  • All AJAX handlers call guard(), which requires the manage_options capability and a valid glaze_wpb_nonce, returning a 403 JSON error otherwise.
  • The /config folder is protected with .htaccess and an index.php silence file.
  • In admin.js, all stored, user-editable values (breakpoint media queries, preset strings, selectors, names) are passed through escHtml() / escAttr() helpers before being injected via innerHTML, preventing broken markup and stored XSS. Toggle labels, hints, and attribute values are all escaped at their injection points.

Uninstall

uninstall.php runs glaze_wpb_rrmdir() to recursively delete the /config and /lib directories and everything inside them. Because the plugin never touches the options table, transients, or custom post types, nothing else needs cleanup — the removal is complete.


Writing Animations: Quick Reference

Once configured, add animations directly in your markup or Bricks elements:

<!-- Simple fade up -->
<div data-animate="from:opacity-0|y-30|duration-0.6"></div>

<!-- Using a preset -->
<div data-animate="preset-fadeInUp"></div>

<!-- Responsive: only on tablet and up -->
<div data-animate="@tablet:from:opacity-0|scale-0.9"></div>

<!-- Staggered children -->
<div data-animate="[&>*]:from:opacity-0|y-20|stagger-0.1|duration-0.5">
  <div>One</div><div>Two</div><div>Three</div>
</div>

<!-- Scroll-triggered -->
<div data-animate="from:opacity-0|y-40|scrollTrigger.trigger-[&]|scrollTrigger.start-[top_85%]"></div>

<!-- SVG line-drawing -->
<svg class="glaze-preset-svgDrawIn"> ... </svg>

Syntax notes: use [-50] for negative/literal values, underscores for values with spaces ([top_center]), dots for nested props (scale.x-2), and tl to make an element a timeline container. Consult the bundled glaze-opus-docs-2.0.md for the exhaustive syntax reference.


Bricks-Specific Notes

  • Bricks-generated markup (like icon wrappers) can be animated globally via the Mappings tab without editing individual elements.
  • Enable Run inside Bricks builder only if you want to preview animations live in the canvas; leaving it off avoids distracting replays on every edit.
  • The runtime's childList observer specifically watches for components Bricks injects dynamically, applying mappings and re-initializing Glaze as they appear (when Watch DOM is enabled).

Troubleshooting

  • Animations don't run: confirm GSAP is loading (check the console for [Glaze] GSAP detected), or that useExternalGsap matches your actual setup. Enable Verbose console logging to see parsed strings and GSAP calls.
  • Local mode not working: ensure you clicked Download for each library first; url() falls back to CDN until a local copy exists.
  • Settings won't save: the error toast "Could not write /config/settings.json" indicates a file-permission problem on the /config folder.
  • Scroll animations inert: verify Enable ScrollTrigger is on and your preset includes scrollTrigger.trigger-[&].
  • Dynamic/AJAX content not animating: enable Watch DOM & re-animate, or listen for the glazeReady event and call its reinit after your own content loads.