MediaWiki:Common.js
From Skateboarding on the Pacific Ocean
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* Willoughby Tucker, I'll Always Love You — MediaWiki Skin JS
* Ethel Cain, 2025
*
* Load via MediaWiki:Common.js or skin template
* Requires: vanilla JS, no dependencies
*
* Effects:
* 1. Amber loading bar
* 2. Dust particle system
* 3. Magnetic cursor (dot + ring)
* 4. Scroll-reveal observer
* 5. Sidebar link ripples
* 6. Title ambient pulse
* 7. Search placeholder cycling
* 8. VHS glitch on title
* 9. Table row glow
* 10. Fog parallax on sidebar
*/
/* ═══════════════════════════════════════════════════════
JS EFFECTS
═══════════════════════════════════════════════════════ */
/* 1. Amber loading bar */
(function() {
const bar = document.getElementById('loading-bar');
if (!bar) return;
let w = 0;
const iv = setInterval(() => {
w += Math.random() * 18 + 4;
if (w >= 95) { clearInterval(iv); w = 95; }
bar.style.width = w + '%';
}, 80);
window.addEventListener('load', () => {
clearInterval(iv);
bar.style.width = '100%';
setTimeout(() => { bar.style.opacity = '0'; bar.style.transition = 'opacity 0.5s'; }, 300);
});
})();
/* 2. Dust particle system */
(function() {
const canvas = document.getElementById('dust-canvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
let W, H, particles = [];
function resize() {
W = canvas.width = window.innerWidth;
H = canvas.height = window.innerHeight;
}
function createParticle() {
return {
x: Math.random() * W,
y: H + Math.random() * 20,
vx: (Math.random() - 0.5) * 0.4,
vy: -(Math.random() * 0.6 + 0.1),
size: Math.random() * 2 + 0.3,
opacity: Math.random() * 0.5 + 0.1,
life: 0,
maxLife: Math.random() * 600 + 200,
hue: Math.random() * 20 + 25
};
}
for (let i = 0; i < 80; i++) {
const p = createParticle();
p.y = Math.random() * H;
p.life = Math.random() * p.maxLife;
particles.push(p);
}
function draw() {
ctx.clearRect(0, 0, W, H);
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx + Math.sin(p.life * 0.01) * 0.15;
p.y += p.vy;
p.life++;
const fade = p.life < 60 ? p.life / 60 : p.life > p.maxLife - 60 ? (p.maxLife - p.life) / 60 : 1;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fillStyle = `hsla(${p.hue}, 55%, 60%, ${p.opacity * fade})`;
ctx.fill();
if (p.life >= p.maxLife || p.y < -10) {
particles[i] = createParticle();
}
}
// Occasional larger dust mote
if (Math.random() < 0.01 && particles.length < 120) {
const mote = createParticle();
mote.size = Math.random() * 3 + 1.5;
mote.opacity = 0.08;
mote.vy = -(Math.random() * 0.2 + 0.05);
particles.push(mote);
}
requestAnimationFrame(draw);
}
resize();
window.addEventListener('resize', resize);
draw();
})();
/* 3. Magnetic cursor */
(function() {
const dot = document.getElementById('cursor-dot');
const ring = document.getElementById('cursor-ring');
if (!dot || !ring) return;
let mx = 0, my = 0, rx = 0, ry = 0;
let entered = false;
document.addEventListener('mouseenter', () => {
entered = true;
dot.style.opacity = '1';
ring.style.opacity = '1';
});
document.addEventListener('mouseleave', () => {
entered = false;
dot.style.opacity = '0';
ring.style.opacity = '0';
});
document.addEventListener('mousemove', e => {
mx = e.clientX; my = e.clientY;
dot.style.left = mx + 'px';
dot.style.top = my + 'px';
if (!entered) { dot.style.opacity = '1'; ring.style.opacity = '0.7'; entered = true; }
});
// Links make cursor expand
document.querySelectorAll('a, button').forEach(el => {
el.addEventListener('mouseenter', () => {
ring.style.width = '44px';
ring.style.height = '44px';
ring.style.borderColor = 'rgba(200,132,58,0.8)';
dot.style.transform = 'translate(-50%,-50%) scale(1.8)';
});
el.addEventListener('mouseleave', () => {
ring.style.width = '28px';
ring.style.height = '28px';
ring.style.borderColor = 'rgba(200,132,58,0.4)';
dot.style.transform = 'translate(-50%,-50%) scale(1)';
});
});
// Smooth ring follow
function lerp(a, b, t) { return a + (b - a) * t; }
function animate() {
rx = lerp(rx, mx, 0.12);
ry = lerp(ry, my, 0.12);
ring.style.left = rx + 'px';
ring.style.top = ry + 'px';
requestAnimationFrame(animate);
}
animate();
})();
/* 4. Scroll-reveal observer */
(function() {
const els = document.querySelectorAll('.scroll-reveal');
const obs = new IntersectionObserver(entries => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.classList.add('revealed');
obs.unobserve(e.target);
}
});
}, { threshold: 0.12 });
els.forEach(el => obs.observe(el));
})();
/* 5. Sidebar link ripple */
(function() {
document.querySelectorAll('.portal a').forEach(link => {
link.addEventListener('click', function(e) {
const ripple = document.createElement('span');
ripple.style.cssText = `
position: absolute;
border-radius: 50%;
background: rgba(200,132,58,0.25);
width: 6px; height: 6px;
top: 50%; left: ${e.offsetX}px;
transform: translate(-50%,-50%) scale(0);
animation: ripple-out 0.5s ease forwards;
pointer-events: none;
`;
link.appendChild(ripple);
setTimeout(() => ripple.remove(), 500);
});
});
const style = document.createElement('style');
style.textContent = `
@keyframes ripple-out {
to { transform: translate(-50%,-50%) scale(20); opacity: 0; }
}
`;
document.head.appendChild(style);
})();
/* 6. Ambient audio visualizer hint (subtle amber pulse on #firstHeading) */
(function() {
const title = document.getElementById('firstHeading');
if (!title) return;
let frame = 0;
function pulse() {
frame++;
const s = 1 + Math.sin(frame * 0.025) * 0.003;
const g = 0.3 + Math.sin(frame * 0.018) * 0.15;
title.style.textShadow = `0 2px ${30 + Math.sin(frame*0.02)*20}px rgba(200,132,58,${g})`;
requestAnimationFrame(pulse);
}
pulse();
})();
/* 7. Search bar typewriter placeholder cycling */
(function() {
const input = document.getElementById('searchInput');
if (!input) return;
const phrases = [
'search the dust…',
'find Willoughby…',
'janie, please stay…',
'nettles and ghosts…',
'waco, texas, 1986…',
'i\'ll always love you…'
];
let i = 0;
setInterval(() => {
if (document.activeElement !== input) {
i = (i + 1) % phrases.length;
input.placeholder = phrases[i];
}
}, 3200);
})();
/* 8. VHS glitch flicker on title — rare, occasional */
(function() {
const title = document.getElementById('firstHeading');
if (!title) return;
function maybeGlitch() {
if (Math.random() < 0.3) {
title.style.transition = 'none';
title.style.transform = `translateX(${(Math.random()-0.5)*3}px)`;
title.style.filter = 'brightness(1.3) saturate(0.5)';
setTimeout(() => {
title.style.transform = 'translateX(0)';
title.style.filter = '';
}, 60 + Math.random() * 80);
}
setTimeout(maybeGlitch, 4000 + Math.random() * 8000);
}
setTimeout(maybeGlitch, 5000);
})();
/* 9. Table row glow on hover */
(function() {
document.querySelectorAll('.wikitable tbody tr').forEach(row => {
row.addEventListener('mouseenter', () => {
row.style.boxShadow = 'inset 0 0 40px rgba(200,132,58,0.07)';
});
row.addEventListener('mouseleave', () => {
row.style.boxShadow = '';
});
});
})();
/* 10. Fog drifting parallax on sidebar */
(function() {
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
const offset = window.scrollY * 0.15;
const panel = document.getElementById('mw-panel');
if (panel) panel.style.backgroundPositionY = offset + 'px';
ticking = false;
});
ticking = true;
}
});
})();