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.
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 togsap.from,gsap.to, andgsap.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-nameand 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
- Upload the
glaze-wp-bricks-ff2folder to/wp-content/plugins/. - Activate the plugin from the WordPress Plugins screen.
On activation (glaze_wpb_activate in glaze-wp-bricks.php), the plugin:
- Creates the
/configand/libdirectories if missing. - Drops a silent
index.php(<?php // Silence is golden.) into each to prevent directory listing. - Writes a
.htaccess(Deny from all) into/configso the settings JSON is not publicly served. - Seeds
/config/settings.jsonwith the defaults fromGlaze_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, currentconfig, and library states.window.GlazeWPBStrings— all UI labels and hint text (fromadmin-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 mode —
CDN(loads from jsDelivr / esm.sh, zero setup) orLocal(serves copies from the plugin's/libfolder). - 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 specialdefaultname is used when no breakpoint prefix is given. - Global defaults —
elementdefaults (applied as a fallback to every animated element, e.g.duration-0.6|ease-power2.out) andtltimeline 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():
- Runs the
guard()check (capability + nonce). - Decodes the JSON payload.
- Merges it onto the defaults with
array_replace_recursive()so the structure always stays intact and new keys survive. - 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:
- It reads the merged config. If
loadInBuilderis off and the code is running inside the Bricks builder (bricks_is_builder()), it bails out entirely. - Unless
useExternalGsapis set, it enqueues GSAP (and ScrollTrigger, if enabled) from the URL resolved byGlaze_WPB_Libs::url()for the active mode. - It enqueues
runtime.js, depending on the GSAP scripts. - It injects a
window.GlazeWPBrickspayload (viawp_add_inline_script(..., 'before')) containing the Glaze module URL, settings, breakpoints, defaults, flattened presets, and animation mappings.
runtime.js then executes this flow:
- Waits for
DOMContentLoaded, then polls forgsap(up to a 5s timeout). - Optionally applies
force3Dconfig and registers ScrollTrigger. - Runs
applyAnimationMappings()— for each mapping, it queries matching elements and appends the animation class(es). - Runs
prepareSVGs()— for SVGs flagged as "draw" animations, it measures each strokeable child's total length viagetTotalLength()and setsstrokeDasharray/strokeDashoffsetso line-drawing effects work. - Dynamically imports the Glaze ESM module from
glazeUrland callsglaze(glazeConfig), passing GSAP core, the data attribute, class name, breakpoints, defaults, and presets. - Fires a
glazeReadywindow event exposingglaze,gsap, the config, and areinitfunction 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):
gsap→gsap.min.jsfrom jsDelivr.scrolltrigger→ScrollTrigger.min.jsfrom jsDelivr.glaze→glaze.module.js, downloaded fromesm.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.
delete($key) unlinks the local file (returning true if it was already absent).
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 themanage_optionscapability and a validglaze_wpb_nonce, returning a 403 JSON error otherwise. - The
/configfolder is protected with.htaccessand anindex.phpsilence file. - In
admin.js, all stored, user-editable values (breakpoint media queries, preset strings, selectors, names) are passed throughescHtml()/escAttr()helpers before being injected viainnerHTML, 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 thatuseExternalGsapmatches 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
/configfolder. - 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
glazeReadyevent and call itsreinitafter your own content loads.