A short fade-in when the page loads can make a WordPress hero heading feel less abrupt. The old version of this snippet faded in every h1 with GSAP, but it hid all of them with CSS first. When GSAP did not load (a blocked CDN, a script error, a slow connection), the headings stayed invisible.
This version only animates elements you mark with the class dpc-fade-in, one after another. It loads GSAP itself from a pinned CDN URL, keeps the content visible without JavaScript, and skips the animation for visitors who turned on reduced motion.
The snippet
<?php
/**
* Fade in elements with the class "dpc-fade-in" one after another with GSAP.
*/
// ===== Config =====
const DPC_GSAP_VERSION = '3.12.5';
// ===== End of config =====
add_action( 'wp_enqueue_scripts', 'dpc_fade_in_assets' );
function dpc_fade_in_assets() {
wp_enqueue_script(
'dpc-gsap',
'https://cdnjs.cloudflare.com/ajax/libs/gsap/' . DPC_GSAP_VERSION . '/gsap.min.js',
array(),
null, // The version is already in the URL.
array( 'in_footer' => true )
);
// Runs after GSAP. If GSAP did not load, it shows the content instead of animating it.
wp_add_inline_script( 'dpc-gsap', <<<'JS'
(function (root) {
var items = document.querySelectorAll('.dpc-fade-in');
if (!root.classList.contains('dpc-fade-pending') || !items.length || typeof window.gsap === 'undefined') {
root.classList.remove('dpc-fade-pending');
return;
}
gsap.set(items, { autoAlpha: 0 });
root.classList.remove('dpc-fade-pending');
gsap.to(items, { autoAlpha: 1, duration: 0.8, stagger: 0.5, ease: 'power1.out', clearProps: 'opacity,visibility' });
})(document.documentElement);
JS
);
// Hide the elements only while the animation is pending, never without JavaScript.
wp_register_style( 'dpc-fade-in', false, array(), '1.0' );
wp_enqueue_style( 'dpc-fade-in' );
wp_add_inline_style( 'dpc-fade-in', '.dpc-fade-pending .dpc-fade-in { visibility: hidden; }' );
}
add_action( 'wp_head', 'dpc_fade_in_head_script', 1 );
function dpc_fade_in_head_script() {
wp_print_inline_script_tag( <<<'JS'
(function (root) {
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
return;
}
root.classList.add('dpc-fade-pending');
// Safety net: show the content after 3 seconds, whatever happens to GSAP.
setTimeout(function () {
root.classList.remove('dpc-fade-pending');
}, 3000);
})(document.documentElement);
JS
);
}
Then add the class dpc-fade-in to the elements to animate: in the block editor under Advanced → Additional CSS class(es), or in your builder’s class field.
Add it with Scripts Organizer
In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Everywhere, tick PHP under Script location, paste the code into the PHP editor and publish. Leave Action/Hook empty.
Everything is in one PHP block on purpose: the CSS and both scripts are added through WordPress’s own enqueue functions, in the right order. The code only adds hooks; wp_enqueue_scripts runs on the front end only.
The same code also works unchanged in a small plugin or an mu-plugin.
How it works
- A tiny script at the top of
<head>adds the classdpc-fade-pendingto<html>. The CSS hides.dpc-fade-inelements only under that class, so without JavaScript nothing is ever hidden. - GSAP loads in the footer from cdnjs with an exact version in the URL. After it, an inline script sets the elements to
autoAlpha: 0, removes the pending class and fades them in with a 0.5 second stagger.clearPropsremoves GSAP’s inline styles at the end. - If GSAP is missing when the inline script runs, it removes the pending class at once. A three-second timer in the head script does the same as a last resort.
- With reduce motion turned on, the head script does nothing and the content simply shows.
- The older version used
gsap.from("h1")on the windowloadevent withh1 { visibility: hidden; }, and did not load GSAP at all.
Customise it
- Other GSAP version: change
DPC_GSAP_VERSION. Pin an exact version; never load alatestURL. - Speed and order: change
duration,staggerandeaseingsap.to(). Addy: 20togsap.set()andy: 0togsap.to()for a slide-up, and addtransformtoclearProps. - Only on some pages: return early in
dpc_fade_in_assets()anddpc_fade_in_head_script(), for exampleif ( ! is_front_page() ) { return; }. - Animations on scroll instead of on load: see Add and Remove a Class on Scroll in WordPress.
Gotchas
- The page loads GSAP from cdnjs. If you already load GSAP from your theme or another plugin, delete the
wp_enqueue_script()call and attach the inline script to that handle instead, so GSAP is not loaded twice. - Loading from a CDN sends the visitor’s IP address to Cloudflare. If that matters for your privacy policy, host
gsap.min.jsyourself and change the URL. - Above-the-fold elements are hidden until the footer scripts run. Keep the class on a few elements, not on the main content.
In the Scripts Organizer library Import it with one click from Scripts Organizer.