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.
The current setup covers:
/snowball to /snowball/forum/snowball/ to /snowball/forum/snowball/forum and /snowball/forum/*is-snowball-forumThese paths are treated as Snöbollseffekten paths:
/snowball -> redirect to /snowball/forum/snowball/ -> redirect to /snowball/forum/snowball/forum/snowball/forum/*The integration is currently driven from the vBulletin hook/template system.
header_headsnowball_header_scriptuserinfo=userinfo
bbuserinfo=bbuserinfo
Purpose:
/snowball -> /snowball/forum redirectis-snowball-forum CSS class on <html>#header .site-logo a > img exists in the DOMWhen 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.
snowball_footer_scriptCurrent expectation:
header_head with early CSS and DOM observationCurrent logo URL:
https://forum.tornevall.net/filedata/fetch?photoid=1063354
The header script replaces this selector:
#header .site-logo a > img
Move the logo to a static file, for example:
/images/snowball-logo.png
That should give:
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.jsmember-count.jsThey 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.
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.jsThat would allow one combined fetch instead of two separate widget loads.
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-spinnerThis keeps the widget visually separate from the default forum chrome without requiring a separate theme.
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>
If the integration does not behave as expected, start with these checks.
Search for snowballLogoUrl in page source or DevTools.
Run in the browser console:
document.querySelector("#header .site-logo a > img")
Run:
document.documentElement.classList.contains("is-snowball-forum")
Run:
decodeURIComponent(window.location.pathname).toLowerCase()
Confirm that the early visibility:hidden CSS rule is present in header_head.
Check the network timings for:
latest-member.jsmember-count.js