Monorepo for Tangled
0

Configure Feed

Select the types of activity you want to include in your feed.

chore: add willow.sh's profile effect thingy

authored by

Willow (GHOST) and committed by
Tangled
(Jul 14, 2026, 3:50 PM +0300) b9fbe81c 506dc233

+60
+60
appview/pages/profile-fx/willow.sh.js
··· 1 + const styles = ` 2 + [data-punchcard] { 3 + .bg-green-100, .dark\\:bg-green-100 { --w-size: 20%; } 4 + .bg-green-200, .dark\\:bg-green-200 { --w-size: 30%; } 5 + .bg-green-300, .dark\\:bg-green-300 { --w-size: 40%; } 6 + .bg-green-400, .dark\\:bg-green-400 { --w-size: 50%; } 7 + .bg-green-500, .dark\\:bg-green-500 { --w-size: 60%; } 8 + .bg-green-600, .dark\\:bg-green-600 { --w-size: 70%; } 9 + .bg-green-700, .dark\\:bg-green-700 { --w-size: 80%; } 10 + .bg-green-800, .dark\\:bg-green-800 { --w-size: 90%; } 11 + .bg-green-900, .dark\\:bg-green-900 { --w-size: 100%; } 12 + 13 + > div > div { 14 + background-color: color-mix(in srgb, #2160ec var(--w-size), light-dark(white, black)); 15 + transition: transform 0.2s ease-out; 16 + will-change: transform; 17 + } 18 + } 19 + `; 20 + 21 + const sheet = new CSSStyleSheet(); 22 + sheet.replaceSync(styles); 23 + document.adoptedStyleSheets = [sheet]; 24 + 25 + const RADIUS = 60; 26 + const MAX_SCALE = 2.5; 27 + 28 + const grid = document.querySelector('[data-punchcard]'); 29 + const dots = Array.from(grid.querySelectorAll(':scope > div > div')); 30 + 31 + function mousemove(e) { 32 + for (const dot of dots) { 33 + const rect = dot.getBoundingClientRect(); 34 + const cx = rect.left + rect.width / 2; 35 + const cy = rect.top + rect.height / 2; 36 + const dist = Math.hypot(e.clientX - cx, e.clientY - cy); 37 + const scale = 1 + (MAX_SCALE - 1) * Math.max(0, 1 - dist / RADIUS); 38 + dot.style.transform = `scale(${scale})`; 39 + } 40 + } 41 + 42 + function mouseleave() { 43 + for (const dot of dots) { 44 + dot.style.transform = 'scale(1)'; 45 + } 46 + } 47 + 48 + function toggleAnimation(enabled) { 49 + if (enabled) { 50 + grid.addEventListener('mousemove', mousemove); 51 + grid.addEventListener('mouseleave', mouseleave); 52 + } else { 53 + grid.removeEventListener('mousemove', mousemove); 54 + grid.removeEventListener('mouseleave', mouseleave); 55 + } 56 + } 57 + 58 + const motion = window.matchMedia('(prefers-reduced-motion: reduce)'); 59 + toggleAnimation(!motion.matches); 60 + motion.addEventListener('change', (event) => toggleAnimation(!event.matches));