← Back to docs

vBulletin - Snöbollseffekten channel-driven header, logo, and member widget

Language: EN | EN | SV

vBulletin - Snöbollseffekten channel-driven header, logo, and member widget

This note describes the current Snöbollseffekten forum-side integration for vBulletin.

The goal is to keep the forum on the normal vBulletin layout while still applying a Snöbollseffekten-specific logo, route handling, and member-widget presentation when the current path belongs to the Snöbollseffekten channel.

What this solution handles

The current setup covers:

  • redirect from /snowball to /snowball/forum
  • redirect from /snowball/ to /snowball/forum
  • path detection for /snowball/forum and /snowball/forum/*
  • adding the HTML class is-snowball-forum
  • hiding the original vBulletin logo early enough to reduce flicker
  • preloading the Snöbollseffekten logo
  • preconnect and preload hints for Tools-hosted scripts
  • fast logo replacement without forcing a dedicated vBulletin theme
  • styling for the member-widget block
  • removal of the older footer-based logo swap

Current route rules

These paths are treated as Snöbollseffekten paths:

  • /snowball -> redirect to /snowball/forum
  • /snowball/ -> redirect to /snowball/forum
  • /snowball/forum
  • /snowball/forum/*

Hook and template setup

The integration is currently driven from the vBulletin hook/template system.

Header hook

  • Hook location: header_head
  • Template name: snowball_header_script
  • Hook arguments:
userinfo=userinfo
bbuserinfo=bbuserinfo

Purpose:

  • handles the /snowball -> /snowball/forum redirect
  • sets the is-snowball-forum CSS class on <html>
  • hides the original logo before the DOM swap happens
  • preloads the Snöbollseffekten logo
  • preconnects/preloads the Tools scripts
  • loads the Tools vBulletin bundle
  • swaps the logo as soon as #header .site-logo a > img exists in the DOM

When the same hook should also talk directly to Tools for invite verification after forum registration, the user fields should be read from userinfo / bbuserinfo, not from an invented user.* object. That is also where a profile field such as field66 normally needs to be read.

There is now also a separate practical guide for that part, including adapted bootstrap code, the public token-free API call, invite-code URL stripping, and a styled success-popup example:

That guide now also contains the full finished Snowball autoverify example that is used for automatic admission and popup feedback.

Footer hook

  • Template name: snowball_footer_script

Current expectation:

  • it should be empty or inactive
  • the old footer-based logo swap is no longer used
  • the active logo replacement is now handled from header_head with early CSS and DOM observation

Current Snöbollseffekten logo

Current logo URL:

https://forum.tornevall.net/filedata/fetch?photoid=1063354

The header script replaces this selector:

#header .site-logo a > img

Recommended future improvement

Move the logo to a static file, for example:

/images/snowball-logo.png

That should give:

  • better browser caching
  • faster repeat loads
  • less dependency on the current attachment/filedata path

Tools bundle and widget scripts

The Tools-managed vBulletin bundle is loaded from:

https://tools.tornevall.net/api/managed-scripts/vbulletin/bundle.js

The member widgets currently use separate scripts:

  • latest-member.js
  • member-count.js

They are preloaded in header_head to reduce later fetch delay.

The HTML module should still contain the actual widget containers. The preload only helps the browser fetch earlier; it does not replace the need for the widget HTML placeholders.

Next performance step if widgets still feel slow

If the widget area is still slower than expected, the next improvement should be to combine the latest-member and member-count fetch into one script/endpoint, for example:

  • member-summary.js

That would allow one combined fetch instead of two separate widget loads.

Current member-widget styling

The integration includes a dedicated style block for the widget wrapper and loading indicator:

  • #tools-vb-member-widgets
  • .tools-vb-widget-line
  • #tools-vb-widget-loading
  • .tools-vb-spinner

This keeps the widget visually separate from the default forum chrome without requiring a separate theme.

Current snowball_header_script

<script>
(function () {
    var path = decodeURIComponent(window.location.pathname).toLowerCase();

    if (path === "/snowball" || path === "/snowball/") {
        window.location.replace("/snowball/forum");
        return;
    }

    if (path === "/snowball/forum" || path.startsWith("/snowball/forum/")) {
        document.documentElement.classList.add("is-snowball-forum");
    }
})();
</script>

<style>
html.is-snowball-forum #header .site-logo a > img {
    visibility: hidden !important;
}

#tools-vb-member-widgets {
    max-width: 1080px;
    margin: 0 auto 24px auto;
    padding: 18px 22px;
    border-left: 5px solid #6faf8b;
    border-radius: 10px;
    background: #f7fff9;
    box-shadow: 0 8px 22px rgba(0, 0, 0, 0.08);
    text-align: center;
    font-weight: 600;
}

#tools-vb-member-widgets .tools-vb-widget-line {
    display: block;
    margin: 4px 0;
}

#tools-vb-widget-loading {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    opacity: 0.75;
}

.tools-vb-spinner {
    width: 14px;
    height: 14px;
    border: 2px solid rgba(0, 0, 0, 0.18);
    border-top-color: rgba(0, 0, 0, 0.65);
    border-radius: 50%;
    animation: toolsVbSpin 0.8s linear infinite;
}

@keyframes toolsVbSpin {
    to {
        transform: rotate(360deg);
    }
}
</style>

<link rel="preload" as="image" href="https://forum.tornevall.net/filedata/fetch?photoid=1063354" fetchpriority="high">
<link rel="preconnect" href="https://tools.tornevall.net" crossorigin>
<link rel="preload" as="script" href="https://tools.tornevall.net/api/managed-scripts/vbulletin/bundle.js">
<link rel="preload" as="script" href="https://tools.tornevall.net/vbulletin/widgets/latest-member.js?group_ids=155&variant=plain&container_id=tools-vb-latest-member-widget&template=Vi+v%C3%A4lkomnar+v%C3%A5r+senaste+medlem+%7Bname%7D">
<link rel="preload" as="script" href="https://tools.tornevall.net/vbulletin/widgets/member-count.js?group_ids=155&variant=plain&container_id=tools-vb-member-count-widget&template=Sn%C3%B6bollseffekten+v%C3%A4xer+-+vi+%C3%A4r+nu+%7Bcount%7D+medlemmar">

<script defer src="https://tools.tornevall.net/api/managed-scripts/vbulletin/bundle.js"></script>

<script>
(function () {
    var snowballLogoUrl = "https://forum.tornevall.net/filedata/fetch?photoid=1063354";
    var path = decodeURIComponent(window.location.pathname).toLowerCase();

    if (!(path === "/snowball/forum" || path.startsWith("/snowball/forum/"))) {
        return;
    }

    var preloadedLogo = new Image();
    preloadedLogo.fetchPriority = "high";
    preloadedLogo.src = snowballLogoUrl;

    function swapSnowballLogo() {
        var logo = document.querySelector("#header .site-logo a > img");

        if (!logo) {
            return false;
        }

        logo.src = snowballLogoUrl;
        logo.setAttribute("data-orig-src", snowballLogoUrl);
        logo.alt = "Snöbollseffekten";
        logo.title = "Snöbollseffekten";
        logo.style.setProperty("visibility", "visible", "important");

        return true;
    }

    if (!swapSnowballLogo()) {
        var observer = new MutationObserver(function () {
            if (swapSnowballLogo()) {
                observer.disconnect();
            }
        });

        observer.observe(document.documentElement, {
            childList: true,
            subtree: true
        });

        window.setTimeout(function () {
            observer.disconnect();
        }, 5000);
    }
})();
</script>

Troubleshooting

If the integration does not behave as expected, start with these checks.

Check that the header hook loaded

Search for snowballLogoUrl in page source or DevTools.

Check that the logo selector still matches

Run in the browser console:

document.querySelector("#header .site-logo a > img")

Check that the Snöboll class is being set

Run:

document.documentElement.classList.contains("is-snowball-forum")

Check the current path matching

Run:

decodeURIComponent(window.location.pathname).toLowerCase()

If the original logo still flickers

Confirm that the early visibility:hidden CSS rule is present in header_head.

If the member widget still feels slow

Check the network timings for:

  • the Tools bundle
  • latest-member.js
  • member-count.js
  • the backend calls triggered by those widgets