Skip to main content

Glaze for WP (and for BricksBuilder)

Tailwind-style GSAP animations for WordPress, driven entirely by HTML data attributes. This plugin wraps the tiny GlazeJS syntax layer around GSAP, and gives you a dark, flat-file-based admin UI to configure libraries, presets, breakpoints, global defaults and automatic class mappings — with first-class support for the Bricks builder.


1. What This Plugin Does

  • Lets you animate any element by adding an attribute like data-animate="from:opacity-0|y-50|duration-1".
  • Loads GSAP + GlazeJS from a CDN, from a local /lib copy, or reuses a global window.gsap provided by another plugin.
  • Provides reusable, categorized presets (preset-fadeIn) editable from the admin.
  • Supports responsive breakpoints, global defaults, and ScrollTrigger.
  • Can auto-attach animations 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 & Compatibility

Requirement Notes
WordPress Standard modern install (uses wp_remote_get, wp_json_encode, etc.).
PHP 7.x+ (uses array_replace_recursive, arrow-free class code).
Browser ES module support required (runtime uses dynamic import()).
GSAP GSAP has its own license — verify commercial compliance. GlazeJS itself is MIT.
Bricks Optional. Detected via bricks_is_builder(); not required.

3. Installation

  1. Copy the glaze-wp-bricks folder into wp-content/plugins/.
  2. Activate Glaze for WP / Bricks from the Plugins screen.
  3. On activation the plugin (glaze_wpb_activate()):
    • Creates /config and /lib folders.
    • Drops an index.php ("Silence is golden") into each to prevent listing.
    • Writes a .htaccess (Deny from all) into /config to keep the JSON private.
    • Seeds /config/settings.json with the defaults from Glaze_WPB_Settings::defaults().
  4. Open the Glaze menu item (superhero dashicon) in wp-admin.

4. Quick Start

  1. Go to Glaze → Libraries, keep CDN mode (zero setup).

  2. Add an attribute to any element on the front end:

    <div data-animate="from:opacity-0|y-50|duration-0.8">Hello</div>
    
  3. In Bricks, add data-animate as a custom attribute on any element, or add a class like glaze-preset-fadeIn (class-based syntax).

  4. Load the front end — the element animates in.

To preview inside the Bricks builder canvas, enable General → 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
Token Purpose
from: / to: State (use both for fromTo). Every animation needs one.
| Separates properties.
- Splits property name / value (duration-1).
. Nested property (scale.x-2).
[-50] Negative / literal value.
[0_0_50px_red] Value with spaces (underscores → spaces).
@bp: Breakpoint prefix (e.g. @tablet:).
[&>sel]: Child selector (& = the element itself).
tl / tl/name Timeline container / named timeline.
preset-name Expand a preset string.
scrollTrigger.* Any GSAP ScrollTrigger option via dot notation.
[&] "This element" (e.g. 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 UI

The admin app is a single dark-themed SPA rendered by Glaze_WPB_Admin::render() (empty panels) 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:

  • Save changes — serializes the whole form (collect()) and posts it to glaze_wpb_save.
  • Reset — confirms, then posts glaze_wpb_reset to restore defaults.

Feedback is shown through a bottom-right toast (#gwpb-toast).

6.1 Libraries Tab

Controls how GSAP + Glaze are loaded.

  • Library mode
    • CDN — loads from jsDelivr / esm.sh. Fastest to set up, relies on a third-party request.
    • Local (/lib) — serves copies stored in the plugin's /lib folder from your own origin. You must Download each library first.
  • Use existing global GSAP — if another plugin/theme already loads GSAP, enable this and Glaze reuses window.gsap (no duplicate scripts).
  • Local libraries list — each registered library (GSAP core, ScrollTrigger, GlazeJS ESM) has:
    • Download — fetch a fresh local copy.
    • Refresh — re-download an existing copy (after an update).
    • Remove — delete the local copy; falls back to CDN.

6.2 General Tab

  • Data attribute — the HTML attribute Glaze scans (default data-animate).
  • Class prefix — prefix for class-based syntax (default glaze).
  • Enable ScrollTrigger — registers the ScrollTrigger plugin.
  • Force GPU (force3D) — forces hardware transforms; smoother, occasionally blurs thin text.
  • Watch DOM & re-animatesee the Firefox note; this flag is currently ignored by the runtime in favor of a safer observer.
  • Watch debounce (ms) — delay before re-scan (only meaningful with watch).
  • Run inside Bricks builder — allow Glaze to run in the builder canvas.
  • Verbose console logging — logs parsed strings and GSAP calls.

6.3 Breakpoints & Defaults Tab

  • Breakpoints — name/media-query pairs. The name is what you reference in strings (@tablet:from:opacity-0); the special default name applies when no @breakpoint: prefix is given.
  • Global defaults
    • Element defaults — fallback applied to every animated element (e.g. duration-0.6|ease-power2.out). Per-element props override these.
    • Timeline defaults — applied to every timeline (and every element is internally wrapped in one), e.g. defaults:ease-power2.out.

6.4 Presets Tab

Reusable strings referenced via preset-name. Categories are purely for organizing the admin — they do not affect the front end (all presets are flattened into a single map by flatten_presets() before enqueue). Preset names must be unique across all categories.

The defaults ship with a large library: Fades, Scale, Slide, Zoom, Bounce, Elastic, Attention Seekers, Rotate & Flip, Blur, Skew, Scroll, Stagger, and several SVG categories (Draw, Fill, Transform, Stagger, Lines).

6.5 Mappings Tab

Automatically attach an animation to every element matching a CSS class or selector — no data-animate needed. Each row:

  • CSS class or selector — e.g. .fv-a-icon or bare fv-a-icon.
  • Animation string or preset-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. Architecture & Technical Internals

7.1 File / Class Map

File Responsibility
glaze-wp-bricks.php Bootstrap: constants, activation hook, requires, instantiates Admin + Frontend on plugins_loaded.
includes/class-glaze-settings.php Defaults, load/merge/save of settings.json, preset flattening.
includes/class-glaze-libs.php Library registry, CDN/local URL resolution, download/refresh/delete, ESM import rewriting.
includes/class-glaze-admin.php Admin menu, asset enqueue, localization, three AJAX endpoints, panel markup.
includes/class-glaze-frontend.php Front-end enqueue of GSAP/ScrollTrigger/runtime + inline config payload.
uninstall.php Recursively deletes /config and /lib.
assets/admin.js Admin SPA (render + collect + AJAX).
assets/admin-strings.js All admin UI copy.
assets/admin.css Dark admin theme.
assets/runtime.js Front-end orchestrator that waits for GSAP, loads Glaze, applies mappings, prepares SVGs, and observes the DOM.
glaze-opus-docs-2.0.md Glaze syntax cheat sheet (the AI "skill" reference).

7.2 Data Storage (Flat Files)

  • No database usage. All configuration lives in /config/settings.json as pretty-printed JSON (JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE).
  • Downloaded libraries live in /lib.
  • Both folders are protected by an index.php stub; /config also gets a .htaccess Deny from all.

7.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) returns the local /lib URL only if mode === 'local' and the file physically exists; otherwise it returns the CDN URL. This means local mode gracefully degrades to CDN for any not-yet-downloaded lib.
  • download($key) uses wp_remote_get (30s timeout, 5 redirects), validates the HTTP 200 and non-empty body, then writes to /lib.

ESM import rewriting (important): esm.sh often returns a thin ES module "shim" whose specifiers are root-relative, e.g.:

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 Front-end Enqueue Pipeline

Glaze_WPB_Frontend::enqueue() (hooked at wp_enqueue_scripts, priority 20):

  1. Loads config via Glaze_WPB_Settings::get().

  2. Bails inside the Bricks builder unless loadInBuilder is on (bricks_is_builder()).

  3. If useExternalGsap is false, enqueues glaze-gsap (and glaze-scrolltrigger when enabled) from the resolved CDN/local URL. If true, it loads nothing — relying on the global gsap.

  4. Enqueues assets/runtime.js with the collected GSAP deps.

  5. Injects the runtime config as an inline before script:

    window.GlazeWPBricks = { glazeUrl, settings, breakpoints, defaults, presets, animationMappings };
    

    Note presets is the flattened map (categories dropped).

7.6 The Runtime (runtime.js)

An async IIFE that orchestrates everything on the front end:

  1. Reads window.GlazeWPBricks; sets up log/warn gated by debug.
  2. Waits for DOMContentLoaded if needed.
  3. waitForGSAP() polls (via requestAnimationFrame, 5s timeout) until window.gsap exists — this is what makes external GSAP and async CDN loads work reliably.
  4. Applies force3D config and registers ScrollTrigger if enabled and present.
  5. applyAnimationMappings(document) — injects mapping classes into matching elements.
  6. prepareSVGs(document) — sets up stroke-draw SVGs.
  7. Dynamically import()s the Glaze module from glazeUrl (CDN or local).
  8. Builds glazeConfig and calls glaze(...) guarded by a running flag to prevent re-entrancy.
  9. Installs a childList MutationObserver for dynamic content.
  10. Dispatches a glazeReady window event with { glaze, gsap, config, reinit } so other scripts can hook in / re-init.

7.7 The Firefox "Watch" Problem

Glaze's built-in watch mode uses an observer that also reacts to attribute mutations. Because GSAP rewrites inline styles on every tick, watch re-triggers Glaze on its own output → a mutate → re-init → mutate loop that Firefox renders as endlessly restarting animations.

Therefore the runtime deliberately ignores watchEnabled. If it's turned on in the admin, the runtime logs a warning and instead handles dynamic content with a childList-only MutationObserver on document.body (subtree: true). When a relevant node is added (an <svg>, an element with a glaze class, or one matching a mapping), it debounces (300ms) then re-prepares SVGs, re-runs Glaze, and refreshes ScrollTrigger.

Practical takeaway: the "Watch DOM" toggle in the admin is effectively a no-op; dynamic content is always covered by the safe childList observer.

7.8 SVG Preparation

For stroke-draw animations, an SVG's paths need stroke-dasharray / stroke-dashoffset set to their measured length. runtime.js:

  • prepareSVGs() finds candidate SVGs (by class/data-animate hints) and, if isStrokeDrawSVG() matches (/draw/i or strokeDashoffset), walks each strokeable child (path, line, polyline, polygon, circle, ellipse, rect).
  • prepareStrokeElement() calls getTotalLength(), sets dasharray/offset to that length, defaults stroke to currentColor if none, and stores dataset.pathLength so it never re-processes the same element.

7.9 Automatic Class Mappings

  • A mapping is { class, animation } (legacy { selector } also honored via getMappingSelector()).
  • addAnimationClasses() splits the animation string on spaces and adds each as a class (idempotently), returning how many were newly added.
  • applyAnimationMappings() runs once at init over the document; invalid selectors are caught and warned.
  • checkAndApplyMappings() runs per added node in the observer so dynamically injected elements (e.g. Bricks output) also get mapped.

7.10 AJAX & Security Model

Three endpoints, all wp_ajax_* (logged-in only):

Action Method
glaze_wpb_save ajax_save()
glaze_wpb_lib ajax_lib() (op: download / delete)
glaze_wpb_reset ajax_reset()

Every handler calls guard(), which requires:

  • Capability manage_options, and
  • A valid nonce (glaze_wpb_nonce) via check_ajax_referer.

Failure → 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 by WP_UNINSTALL_PLUGIN) recursively removes both /config and /lib. Since the plugin stores no options, CPTs, or transients, uninstalling leaves nothing behind.


8. Vibe Coding (Editing JSON Directly)

You can bypass the UI and edit /config/settings.json directly (or have an AI do it) to add many presets at once. The bundled glaze-opus-docs-2.0.md is the canonical syntax reference (the AI "skill"). After editing, the next front-end load picks up the changes; the admin also reflects them because get() merges the file over defaults.

Because .htaccess denies public web access to /config, editing is done via the file system / SFTP, not over HTTP.


9. Extending the Plugin

  • Add a library — add an entry to Glaze_WPB_Libs::registry() with label, cdn, download, file, type. It automatically appears in the Libraries tab and gets download/refresh/remove buttons.
  • Change defaults — edit Glaze_WPB_Settings::defaults(). Existing installs gain the new keys automatically thanks to array_replace_recursive.
  • Hook glazeReady — listen on window for glazeReady to access { glaze, gsap, config, reinit }; call detail.reinit() after your own DOM injections.
window.addEventListener('glazeReady', ({ detail }) => {
  console.log('Glaze up with', detail.gsap.version);
  // detail.reinit(); // after injecting new animated markup
});

10. Troubleshooting

Symptom Likely cause / fix
Nothing animates Check the front end actually loads GSAP. With Use external GSAP on, ensure a global gsap exists. Enable debug and watch the console for [Glaze].
GSAP not found. in console 5s timeout elapsed — GSAP never loaded. Verify CDN reachability or download local copies.
Animations loop endlessly in Firefox Expected with Glaze's native watch; the runtime already disables it. Don't attempt to re-enable watch.
Local mode still hits the CDN The library wasn't downloaded yet — url() falls back to CDN until the file exists in /lib.
Locally served Glaze module fails to import Re-download it; the plugin rewrites root-relative ESM specifiers on download (absolutize_root_relative_imports).
Nothing shows in the Bricks builder Enable General → Run inside Bricks builder.
SVG draw effect doesn't run Ensure the SVG class/data-animate contains a draw / strokeDashoffset hint so prepareSVGs targets it.
Can't save settings /config not writable — the error toast reports permission issues on settings.json.

11. Configuration Reference

Top-level keys of settings.json (see Glaze_WPB_Settings::defaults()):

Key Type Meaning
libraryMode "cdn" | "local" Source of GSAP/Glaze at runtime.
useExternalGsap bool Reuse a global gsap instead of enqueuing our own.
settings.dataAttribute string Attribute Glaze scans (default data-animate).
settings.className string Class-mode prefix (default glaze).
settings.enableScrollTrigger bool Enqueue + register ScrollTrigger.
settings.force3D bool GSAP config({force3D}).
settings.watchEnabled bool Ignored by runtime (see §7.7).
settings.watchDebounce int Debounce ms (only meaningful with watch).
settings.loadInBuilder bool Run inside the Bricks builder.
settings.debug bool Verbose [Glaze] console logging.
breakpoints object name → media query; default is the fallback.
defaults.element string Fallback props for every element.
defaults.tl string Defaults applied to every timeline.
animationMappings array { class, animation } auto-attach rules.
presets object Category → { name → string } (flattened at enqueue).

Author: art10m (Claude Opus 4.8) · License: MIT (GlazeJS). GSAP is licensed separately — verify commercial compliance.