semantic bufo search find-bufo.com
bufo
1
fork

Configure Feed

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

add animated peeking bufo from all four edges

isolate all bufo peeking logic into separate bufo-peek.js file, keeping
the silly whimsical behavior cleanly separated from core search functionality.

behavior:
- bufo randomly appears from one of four edges (top, right, bottom, left)
- rotates 90° between edges to always peek perpendicular to edge
- animates: peeks in → holds → peeks back out (6s cycle)
- moves to new random edge after each cycle
- hides permanently after first search

technical:
- new static/bufo-peek.js handles all positioning and animation logic
- CSS variables (--peek-start, --peek-in) drive smooth animations
- event-based communication (bufo-hide) for clean separation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

zzstoatzz f85b472d 4b93f8da

+84 -15
+43
static/bufo-peek.js
··· 1 + // bufo peeking animation logic 2 + // this file handles the silly but delightful bufo that peeks from random edges 3 + 4 + (function() { 5 + const peekingBufo = document.getElementById('peekingBufo'); 6 + let hasSearched = false; 7 + 8 + // animation cycle duration (must match CSS animation duration) 9 + const PEEK_CYCLE_MS = 6000; 10 + 11 + // function to set random bufo position 12 + function setRandomBufoPosition() { 13 + const positions = ['top', 'right', 'bottom', 'left']; 14 + 15 + // remove all position classes 16 + peekingBufo.classList.remove( 17 + 'peeking-bufo-top', 18 + 'peeking-bufo-right', 19 + 'peeking-bufo-bottom', 20 + 'peeking-bufo-left' 21 + ); 22 + 23 + // set new random position 24 + const position = positions[Math.floor(Math.random() * positions.length)]; 25 + peekingBufo.classList.add(`peeking-bufo-${position}`); 26 + } 27 + 28 + // set initial position 29 + setRandomBufoPosition(); 30 + 31 + // move to new position after each peek cycle 32 + setInterval(() => { 33 + if (!hasSearched) { 34 + setRandomBufoPosition(); 35 + } 36 + }, PEEK_CYCLE_MS); 37 + 38 + // hide bufo after first search 39 + window.addEventListener('bufo-hide', () => { 40 + hasSearched = true; 41 + peekingBufo.classList.add('hidden'); 42 + }); 43 + })();
+41 -15
static/index.html
··· 202 202 padding: 40px; 203 203 } 204 204 205 + @keyframes peek-in-out { 206 + 0% { 207 + transform: var(--peek-start); 208 + } 209 + 20% { 210 + transform: var(--peek-in); 211 + } 212 + 80% { 213 + transform: var(--peek-in); 214 + } 215 + 100% { 216 + transform: var(--peek-start); 217 + } 218 + } 219 + 205 220 .peeking-bufo { 206 221 position: fixed; 207 222 pointer-events: none; 208 223 z-index: 1000; 209 - top: 50%; 210 224 width: 200px; 211 225 height: auto; 212 - transition: opacity 0.3s ease-out; 226 + animation: peek-in-out 6s ease-in-out infinite; 213 227 } 214 228 215 229 .peeking-bufo.hidden { 216 230 opacity: 0; 217 231 pointer-events: none; 232 + animation: none; 218 233 } 219 234 220 235 .peeking-bufo-right { 221 - right: 0; 222 - transform: translateY(-50%); 236 + right: -200px; 237 + top: 50%; 238 + --peek-start: translateY(-50%); 239 + --peek-in: translateX(-200px) translateY(-50%); 240 + } 241 + 242 + .peeking-bufo-bottom { 243 + bottom: -200px; 244 + left: 50%; 245 + --peek-start: translateX(-50%) rotate(90deg); 246 + --peek-in: translateX(-50%) translateY(-200px) rotate(90deg); 223 247 } 224 248 225 249 .peeking-bufo-left { 226 - left: 0; 227 - transform: translateY(-50%) scaleX(-1); 250 + left: -200px; 251 + top: 50%; 252 + --peek-start: translateY(-50%) scaleX(-1); 253 + --peek-in: translateX(200px) translateY(-50%) scaleX(-1); 254 + } 255 + 256 + .peeking-bufo-top { 257 + top: -200px; 258 + left: 50%; 259 + --peek-start: translateX(-50%) rotate(-90deg); 260 + --peek-in: translateX(-50%) translateY(200px) rotate(-90deg); 228 261 } 229 262 230 263 @media (max-width: 1024px) { ··· 266 299 267 300 <img src="https://all-the.bufo.zone/bufo-just-checking.gif" alt="bufo peeking" class="peeking-bufo" id="peekingBufo"> 268 301 302 + <script src="/static/bufo-peek.js"></script> 269 303 <script> 270 304 const searchInput = document.getElementById('searchInput'); 271 305 const searchButton = document.getElementById('searchButton'); 272 306 const resultsDiv = document.getElementById('results'); 273 307 const loadingDiv = document.getElementById('loading'); 274 308 const errorDiv = document.getElementById('error'); 275 - const peekingBufo = document.getElementById('peekingBufo'); 276 - 277 - // randomly position bufo on left or right 278 - if (Math.random() < 0.5) { 279 - peekingBufo.classList.add('peeking-bufo-left'); 280 - } else { 281 - peekingBufo.classList.add('peeking-bufo-right'); 282 - } 283 309 284 310 let hasSearched = false; 285 311 ··· 289 315 290 316 // hide bufo after first search 291 317 if (!hasSearched) { 292 - peekingBufo.classList.add('hidden'); 318 + window.dispatchEvent(new Event('bufo-hide')); 293 319 hasSearched = true; 294 320 } 295 321