Contents

jQuery → Vanilla ES6 Migration

⚡ In short

This site was migrated from jQuery to native JavaScript (Vanilla ES6). An 87 KB external dependency removed, replaced by modern code that does exactly the same thing — no extra download, no third-party library.

For visitors, nothing changes. For security and performance, it’s a measurable improvement.

🧠 Why

jQuery was essential in 2010 when browsers all behaved differently. In 2026, modern browsers natively support everything jQuery did:

  • $(selector)document.querySelector()
  • $.ajax()fetch()
  • $(el).on('click', fn)el.addEventListener('click', fn)
  • $(document).ready()document.addEventListener('DOMContentLoaded', fn)

Keeping jQuery today means loading 87 KB of code you no longer need.

🔧 What was done

The default Grav theme (Quark) used jQuery for four functions:

  • The scrolled header — add/remove a CSS class when scrolling the page
  • The parallax effect — offset the background based on scroll position
  • The mobile menu — open/close the navigation overlay on click
  • The tree menu — manage submenu toggling in mobile navigation

Each was rewritten in native ES6 JavaScript, with no external dependencies. An independent arleo theme was created from Quark, then Quark was removed from the server.

🔍 Before / After — JS loading

Before:

<!-- Loading jQuery (87 KB) -->
<script src="/system/assets/jquery/jquery-3.x.min.js"></script>
<!-- jQuery-dependent treemenu plugin -->
<script src="/user/themes/quark/js/jquery.treemenu.js"></script>
<!-- Application code using $ -->
<script src="/user/themes/quark/js/site.js"></script>

After:

<!-- Single file, loaded deferred -->
<script src="/user/themes/arleo/js/site.js" defer></script>

⚙️ Migration example — mobile menu

Before (jQuery):

jQuery(document).ready(function($) {
    $('#toggle').click(function() {
        $(this).toggleClass('active');
        $('#overlay').toggleClass('open');
        $('body').toggleClass('mobile-nav-open');
    });
});

After (Vanilla ES6):

document.addEventListener('DOMContentLoaded', () => {
    const toggle = document.getElementById('toggle');
    const overlay = document.getElementById('overlay');
    if (toggle && overlay) {
        toggle.addEventListener('click', () => {
            toggle.classList.toggle('active');
            overlay.classList.toggle('open');
            document.body.classList.toggle('mobile-nav-open');
        });
    }
});

⚙️ Tree menu without a plugin

The jquery.treemenu.js plugin was replaced by a native implementation:

document.querySelectorAll('.tree li').forEach(item => {
    const children = item.querySelector('ul');
    if (children) {
        item.classList.add('tree-closed');
        const toggler = document.createElement('span');
        toggler.className = 'toggler';
        item.insertBefore(toggler, item.firstChild);
        toggler.addEventListener('click', () => {
            setTimeout(() => {
                item.classList.toggle('tree-closed');
                item.classList.toggle('tree-opened');
            }, 300);
        });
    }
});

⚙️ Creating an independent theme

# Copy Quark to arleo
cp -r /var/www/grav/user/themes/quark /var/www/grav/user/themes/arleo

# Remove jQuery from the base template
# partials/base.html.twig: javascripts block rewritten

# Activate the theme
sed -i 's/theme: quark/theme: arleo/' /var/www/grav/user/config/system.yaml

# Remove Quark
rm -rf /var/www/grav/user/themes/quark

🏁 Conclusion

CriterionBeforeAfter
Scripts loaded3 (jQuery + treemenu + site.js)1 (site.js)
External JS weight~87 KB0 KB
defer attributeNoYes
Third-party dependencies20
Trusted Types compatible (CSP)NoYes
Theme independent of QuarkNoYes

This migration is part of a broader effort to harden the security of the arleo.eu server, including PGP-signed security.txt configuration, CSP optimisation, and removal of unnecessary dependencies.

💡 Theme source code available on request via security.txt.