Glaze for WP (and for BricksBuilder)
Tailwind-styleHereGSAPisanimationscomprehensivefor WordPress, driven entirely by HTML data attributes. This plugin wraps the tinyGlazeJSsyntax layer aroundGSAP, and gives you a dark, flat-file-based admin UI to configure libraries, presets, breakpoints, global defaults and automatic class mappings — with first-class supportdocumentation for theBricksbuilder.
1. What This Plugin DoesLets you animate any element by adding an attribute likedata-animate="from:opacity-0|y-50|duration-1".Loads GSAP + GlazeJS from a CDN, from a local/libcopy, or reuses a globalwindow.gsapprovided by another plugin.Provides reusable, categorizedpresets(preset-fadeIn) editable from the admin.Supports responsivebreakpoints,global defaults, andScrollTrigger.Canauto-attachanimations to elements by CSS class/selector, without editing markup (handy for Bricks-generated wrappers).Stores everything in flat files (/config/settings.json) — no database options, no custom tables, and a clean uninstall.
2. Requirements & CompatibilityRequirementNotesWordPressStandard modern install (useswp_remote_get,wp_json_encode, etc.).PHP7.x+ (usesarray_replace_recursive, arrow-free class code).BrowserES module support required (runtime uses dynamicimport()).GSAPGSAP has itsown license— verify commercial compliance. GlazeJS itself is MIT.BricksOptional. Detected viabricks_is_builder(); not required.
3. InstallationCopy theglaze-wp-bricksfolder intowp-content/plugins/.ActivateGlaze for WP / Bricksfromplugin (v 1.1.0).
Glaze for WP / Bricks — Complete Documentation
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 PluginsBricks screen.page
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 ( and glaze_wpb_activate()/config/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:
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
glaze-wp-bricks-ff2 folder 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/libfolders.directories if missing. - Drops
ana silentindex.php("<?php // Silence is) into each to prevent directory listing.golden"golden. - Writes a
.htaccess(Deny from all) into/configto keepso the settings JSONprivate.is not publicly served. - Seeds
/config/settings.jsonwith the defaults fromGlaze_WPB_Settings::defaults().if it doesn't already exist.
A
4. Quick Start
Go to Glaze → Libraries, keep CDN mode (zero setup).
Add an attribute to any element on the front end:
<div data-animate="from:opacity-0|y-50|duration-0.8">Hello</div>
In Bricks, add data-animate as a custom attribute on any element, or add a class like glaze-preset-fadeIn (class-based syntax).
Load the front end — the element animates in.
To preview inside the Bricks builder canvas, enableGeneral → Run inside Bricks builder.
5. Writing Animations (Glaze Syntax)
Full details live in glaze-opus-docs-2.0.md. The essentials:
Anatomy: [breakpoint][selector]:state:properties
@lg:[&>div]:from:opacity-0|y-50|stagger-0.1|duration-0.5|ease-power2.out
from:to:fromTo|-duration-1.scale.x-2[-50][0_0_50px_red]@bp:@tablet:[&>sel]:&tltl/namepreset-namescrollTrigger.*[&]scrollTrigger.trigger-[&]Two input modes (configurable in General):
<!-- Data-attribute mode (default) -->
<div data-animate="from:opacity-0|duration-1"></div>
<!-- Class mode (prefix = "glaze" by default) -->
<div class="glaze-from:opacity-0|duration-1"></div>
<div class="glaze-preset-fadeIn"></div>
6. The Admin UIInterface
The adminsettings appscreen is a singleself-contained dark-themedsingle-page SPAapp rendered by Glaze_WPB_Admin::render(). (emptyThe panels)PHP outputs only a skeleton — a header with Reset and populated entirely by assets/admin.js. Strings are decoupled into assets/admin-strings.js under window.GlazeWPBStrings, and initial data is injected via wp_localize_script as window.GlazeWPBAdmin.
Two global actions in the header:
collect()<section>glaze_wpb_saveadmin.jswindow.GlazeWPBAdmin — AJAX URL, nonce, current config, and library states.
window.GlazeWPBStrings — glaze_wpb_resetadmin-strings.js), FeedbackThe interface is shownstyled throughby admin.css in a bottom-rightdark toasttheme scoped under .glaze-wpb, using CSS custom properties for colors, radius, and accents. It also darkens the surrounding WordPress chrome (, #gwpb-toast#wpwrap#wpbody-content).
The five tabs are:
6.11. Libraries Tab
Controls how GSAP +and GlazeGlazeJS are loaded.loaded:
- Library mode
CDN—CDN(loads from jsDelivr / esm.sh.sh,Fastestzerotosetup)setorup, relies on a third-party request.
Local (/lib window.gsap (gsap, scrolltrigger, glaze) Library operations are handled by AJAX (glaze_wpb_lib) and delegated to Glaze_WPB_Libs (see Technical Processes below).
2. General
DownloadAttributes —fetchtheadatafreshattributelocalnamecopy.(data-animate) Glaze scans, and the class prefix (glaze) for the optional class-based syntax. Change these only to avoid conflicts.RefreshBehavior toggles:Enable ScrollTrigger—re-downloadregistersanGSAP'sexistingScrollTriggercopyplugin for scroll-based animations.
Force GPU (afterforce3D) Watch DOM & re-animate — enables a Firefox-safe MutationObserver for dynamically added content (see below), with a configurable debounce (ms).
Run inside Bricks builder — Verbose toconsole CDN.logging — logs parsing and GSAP calls for debugging.
6.2 General Tab
data-animateglaze6.33. Breakpoints & Defaults Tab
- Breakpoints — name/media-query
pairs.pairs that power responsive prefixes like@tablet:. Thename is what you reference in strings (@tablet:from:opacity-0); thespecialdefaultnameappliesis used when nobreakpoint prefix is given.@breakpoint: - Global defaults
Elementelementdefaults—(applied as a fallbackappliedto every animatedelementelement,(e.g.duration-0.6|ease-power2.out).Per-elementandpropstloverride these.
defaults:ease-power2.out).
6.44. Presets Tab
Reusable animation strings referencedorganized viainto named categories (Fades, Scale, Slide, Zoom, Bounce, SVG Draw, Stagger, etc.). Categories preset-nameareexist purelyonly forto organizingorganize the admin UI — they dohave notno affect the front front-end (alleffect presetsand are flattened into a single map by flatten_presets() before enqueue).delivery. Preset names must be unique across all categories.
TheYou defaultsreference shipa preset with a large library: Fades, Scale, Slide, Zoom, Bounce, Elastic, Attention Seekers, Rotate & Flip, Blur, Skew, Scroll, Stagger, and several SVG categoriespreset-name (Draw,or Fill,glaze-preset-name Transform,in Stagger,class Lines)mode).
6.55. Mappings Tab
AutomaticallyAutomatic class mappings attach an animation to every element matching a CSS classclass/selector orwithout editing each element's markup — useful for markup you don't control (e.g. Bricks-generated icon wrappers). Each row pairs a selector — no data-animate needed. Each row:
.fv-a-icon or fv-a-iconpreset-name — e.g. glaze-preset-svgStaggerFadeIn.The runtime adds the given classes to matched elements before Glaze runs, and re-applies on dynamically added nodes.
7.Saving, ArchitectureResetting, &and TechnicalData InternalsFlow
7.1
When Fileyou /click ClassSave Mapchanges,
admin.js's collect() walks glaze-wp-bricks.phpglaze_wpb_save Glaze_WPB_Admin::ajax_save():
guard() check (capability + plugins_loadedarray_replace_recursive()includes/class-glaze-settings.phpstructure settings.jsonGlaze_WPB_Settings::save(), includes/class-glaze-libs.phpincludes/class-glaze-admin.phpincludes/class-glaze-frontend.phpuninstall.php/config/libassets/admin.jsassets/admin-strings.jsassets/admin.cssassets/runtime.jsglaze-opus-docs-2.0.md7.2 Data Storage (Flat Files)
/config/settings.json.
Reset ()JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODEglaze_wpb_reset.
/libindex.php/config.htaccessDeny from all7.3 Settings Lifecycle
defaults() ──► save(defaults) ──► settings.json (on activation)
get():
settings.json ──► json_decode ──► array_replace_recursive(defaults(), data)
└─ guarantees new keys always exist
save($data):
wp_mkdir_p(config) ──► wp_json_encode ──► file_put_contents(settings.json)
ajax_save:
wp_unslash($_POST['config']) ──► json_decode ──►
array_replace_recursive(defaults(), data) ──► save()
array_replace_recursive on both read and save is deliberate: it keeps the structure forward-compatible when new default keys are introduced in updates.
7.4 Library Management (CDN vs Local)
Glaze_WPB_Libs::registry() defines three libraries, each with a cdn URL (used at runtime), a download URL (fetched for the local copy), a local file name, and a type (script or module).
url($key, $mode)/libmode === 'local'defaults() Glaze_WPB_Settings::get() always reads the file and merges it returnsonto the CDNdefaults, URL.so Thisa meanscorrupt localor modepartial gracefullyfile degradessafely falls back to CDN for any not-yet-downloaded lib.
download($key)wp_remote_get/libESM import rewriting (important): esm.sh often returns a thin ES module "shim" whose specifiers are root-relative, e.g.:defaults.
export
* from "/glazejs@2.0.1/es2020/glazejs.bundle.mjs";
That only resolves against the CDN's own origin. Since we serve the saved file from the WordPress origin, absolutize_root_relative_imports() rewrites any from "/..." / import "/..." / import("/...") specifier into a fully qualified URL pointing back at the original CDN origin, so the locally served module keeps working.
7.5
The Front-endEnd Enqueue Pipeline
Runtime
Glaze_WPB_Frontend::enqueue() (hookedruns aton wp_enqueue_scripts, (priority 20): and orchestrates loading:
- It
Loadsreadsconfigtheviamerged config. IfGlaze_WPB_Settings::get()loadInBuilder.
Bailsand the code is running inside the Bricks builder unless loadInBuilder is on (bricks_is_builder()).
IfUnless useExternalGsap is false,set, it enqueues GSAP (and glaze-gsapScrollTrigger, glaze-scrolltriggerwhenif enabled) from the URL resolved CDN/local URL. Ifby for the active mode.trueGlaze_WPB_Libs::url()
runtime.js, gsapEnqueuesinjects a assets/runtime.jswindow.GlazeWPBrickswithpayload (via wp_add_inline_script(..., 'before')) containing the collectedGlaze GSAPmodule deps.
Injects the runtime config as an inline before script:
window.GlazeWPBricks = { glazeUrl,URL, settings, breakpoints, defaults, presets, animationMappings };
Note presets is the flattened mappresets, (categoriesand dropped).
7.6 The Runtime (
runtime.js)
Anthen asyncexecutes IIFEthis that orchestrates everything on the front end:flow:
window.GlazeWPBrickslogwarndebugDOMContentLoaded, waitForGSAP()then polls requestAnimationFramegsapwindow.gsapforce3D config and registers applyAnimationMappings(document)) — prepareSVGs(document)) — for SVGs flagged as "draw" animations, it measures each strokeable child's total length via getTotalLength() and sets strokeDasharray/strokeDashoffset import()glazeUrl glazeConfigglaze(...)glazeConfig), runningthe glazeReady window event {glaze, gsap, reinit 7.7 The Firefox "Watch"looping" Problemsafeguard
A key technical detail: Glaze's built-inown native watch mode usesis andeliberately never enabled. Its internal observer that also reacts to attribute mutations.mutations, Becauseand since GSAP rewrites inline styles on every tick, native watch would re-triggerstrigger Glaze on its own output →— a mutate → re-init → mutate loop that Firefox renders as endlessly restarting animations.
ThereforeInstead, the runtimeWatch deliberatelyDOM ignores& watchEnabled.re-animate Iftoggle it's turned on in the admin, the runtime logsdrives a warningcustom, andFirefox-safe insteadMutationObserver handlesthat dynamiclistens contentfor withchildList achanges childList-only (never MutationObserveron document.body (subtree: true)attributes). When anew relevantnodes nodeappear, isit addedchecks (anwhether <svg>,they anare elementSVGs withor match any mapping, and if so schedules a debounced glazeclass, or one matching a mapping), it debouncesre-init (300ms) then re-preparespreparing SVGs, re-runsrunning Glaze, and refreshesrefreshing ScrollTrigger.
Detail
Practical takeaway:When the"WatchtoggleDOM"istoggleoff, no observer is attached at all. Either way,scheduleReinitremains exposed through theglazeReadyevent for manual re-runs.
Technical Processes in
the admin is effectively a no-op; dynamic content isalwayscovered by the safe childList observer.
7.8Library SVGmanagement Preparation(Glaze_WPB_Libs)
ForA stroke-drawstatic animations,registry() andefines SVG'each library's pathsruntime needCDN URL, download URL, local filename, and type (script vs stroke-dasharraymodule / stroke-dashoffset set to their measured length. ):runtime.js
prepareSVGs()gsapfinds→candidate SVGs (by class/data-animategsap.min.jshints)fromand, ifisStrokeDrawSVG()matches (/draw/iorstrokeDashoffset), walks each strokeable child (path, line, polyline, polygon, circle, ellipse, rect).jsDelivr.→prepareStrokeElement(scrolltriggerScrollTrigger.min.jsfrom jsDelivr.
glaze → glaze.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() callssimply checks whether the local file exists.
download($key) fetches the source via (30s timeout, following redirects), getTotalLength(wp_remote_get()setsvalidates dasharray/offsetthe 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 length,begin defaultswith stroke/tointo 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 if currentColortruenone,it andwas storesalready absent).
Preset flattening
dataset.pathLengthGlaze_WPB_Settings::flatten_presets()so it never re-processescollapses the samecategorized element.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.
7.9 Automatic Class MappingsSecurity
A mapping is{ class, animation }(legacy{ selector }also honored viagetMappingSelector()).
addAnimationClasses()applyAnimationMappings()checkAndApplyMappings()7.10All AJAX &handlers Security Model
Three endpoints, all wp_ajax_* (logged-in only):
glaze_wpb_saveajax_save()glaze_wpb_libajax_lib()downloaddeleteglaze_wpb_resetajax_reset()Every handler callscall guard(), which requires:
manage_optionsglaze_wpb_nonce, returning a 403 JSON error otherwise.
/config .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 glaze_wpb_nonceescHtml() / escAttr() helpers before being injected via check_ajax_refererinnerHTMLFailure → wp_send_json_error(..., 403). Library op/key are sanitized with sanitize_key(). The save payload is wp_unslash-ed, JSON-decoded, validated as an array, and merged onto defaults before writing.
7.11 Uninstall Behavior
uninstall.php (guarded byruns WP_UNINSTALL_PLUGINglaze_wpb_rrmdir()) to recursively removesdelete boththe /config and /lib. Sincedirectories and everything inside them. Because the plugin storesnever notouches options,the CPTs,options table, transients, or transients,custom uninstallingpost leavestypes, nothing behind.else needs cleanup — the removal is complete.
8.Writing VibeAnimations: CodingQuick (Editing JSON Directly)Reference
YouOnce canconfigured, bypassadd theanimations UIdirectly 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 editup -->
<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 /config/settings.json[-50]directlyfor negative/literal values, underscores for values with spaces (or[top_center]), havedots for nested props (scale.x-2), and tl to make an AIelement doa it)timeline tocontainer. addConsult many presets at once. Thethe bundled glaze-opus-docs-2.0.md isfor the canonicalexhaustive syntax referencereference.
Bricks-Specific Notes
get()
Because.htaccessdenies public web access to/config, editing is doneglobally via thefileMappingssystemtab/withoutSFTP,editingnotindividualoverelements.
9. Extending the PluginTroubleshooting
AddAnimationsadon'tlibraryrun:—confirmaddGSAPanisentry toGlaze_WPB_Libs::registry()withlabel,cdn,download,file,type. It automatically appears in the Libraries tab and gets download/refresh/remove buttons.
Glaze_WPB_Settings::defaults()array_replace_recursiveglazeReadywindowglazeReady{ glaze, gsap, config, reinit }detail.reinit()window.addEventListener('glazeReady',loading ({ detail }) => {
console.log('Glaze up with', detail.gsap.version);
// detail.reinit(); // after injecting new animated markup
});
10. Troubleshooting
gsap[Glaze]GSAP not found.useExternalGsap url() falls back to CDN until /libabsolutize_root_relative_importsdata-animatedrawstrokeDashoffsetprepareSVGs/configsettings.json/config folder.
Scroll animations inert: verify Enable ScrollTrigger is on and your preset includes scrollTrigger.trigger-[&].
Dynamic/AJAX content not 11. Configuration Reference
Top-level keys of settings.json (see Glaze_WPB_Settings::defaults()):
libraryMode"cdn" | "local"useExternalGsapgsapsettings.dataAttributedata-animatesettings.classNameglazesettings.enableScrollTriggersettings.force3Dconfig({force3D})settings.watchEnabledsettings.watchDebouncere-animate, settings.loadInBuilderglazeReady settings.debug[Glaze]reinit breakpointsloads.
name → media querydefaultdefaults.elementdefaults.tlanimationMappings{ class, animation }presetsCategory → { name → string }