Add infinite scrolling cause zeppo is silly
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 1m57s

This commit is contained in:
Stephen Tafoya
2026-06-10 20:26:08 -06:00
parent 57a3847d18
commit 47a1ae92be
3 changed files with 161 additions and 11 deletions

View File

@@ -62,17 +62,21 @@
<section class="testimonials">
<h2>What Others Are Saying</h2>
<div class="testimonial">
<p>"I have never met a yinglet with such presence. Zeppo is a force of nature wrapped in fur."</p>
<cite>— A Very Reliable Source</cite>
</div>
<div class="testimonial">
<p>"He once stared down a baxxid and the baxxid blinked first (even though it doesn't have eyelids). Legend."</p>
<cite>— Witness</cite>
</div>
<div class="testimonial">
<p>"I asked him to tone it down and he toned it UP. Respect."</p>
<cite>— Anonymous</cite>
<div class="testimonial-viewport">
<div id="testimonial-track">
<div class="testimonial">
<p>"I have never met a yinglet with such presence. Zeppo is a force of nature wrapped in fur."</p>
<cite>— A Very Reliable Source</cite>
</div>
<div class="testimonial">
<p>"He once stared down a baxxid and the baxxid blinked first (even though it doesn't have eyelids). Legend."</p>
<cite>— Witness</cite>
</div>
<div class="testimonial">
<p>"I asked him to tone it down and he toned it UP. Respect."</p>
<cite>— Anonymous</cite>
</div>
</div>
</div>
</section>
@@ -108,5 +112,6 @@
<footer>
<p>Built with love and a healthy fear of Zeppo's judgment.</p>
</footer>
<script src="script.js"></script>
</body>
</html>

121
script.js Normal file
View File

@@ -0,0 +1,121 @@
(function () {
'use strict';
var fragments = [
'I have never met a yinglet with such presence.',
'Zeppo is a force of nature wrapped in fur.',
'He once stared down a baxxid and the baxxid blinked first (even though it doesn\'t have eyelids).',
'Legend.',
'I asked him to tone it down and he toned it UP.',
'Respect.',
'Zeppo doesn\'t walk into a room \u2014 he arrives.',
'Whiskers perfectly groomed, tail held high and shelltooth sharp.',
'He knows he\'s him.',
'Wits sharper than a creystone blade.',
'Zeppo could scheme his way out of any jam and still have time to steal your lunch.',
'Despite being a tiny rodent-person, his heart is the size of a melon.',
'Loyal, fierce, and ride-or-die for his crew.',
'Name a better dental situation. You can\'t.',
'That shelltooth has 10,000 PSI of pure charisma.',
'Where Zeppo goes, adventure follows.',
'Not always good adventure, but never boring.',
'The universe literally cannot ignore him.',
'No throne needed. Zeppo rules through sheer force of personality.',
'Say his name three times and he appears to judge you.',
];
var sources = [
'A Very Reliable Source',
'Witness',
'Anonymous',
'Local Prophet',
'The Council of Elders',
'A Slightly Biased Fan',
'His Mother',
'A Former Rival',
'The Oracle of Weh',
'Some Guy',
'An Expert',
'History',
'A Completely Unbiased Observer',
'The Voices',
'A Very Smart Person',
'The Echo',
'A Distant Admiral',
'A Humble Historian',
'A Random Vagrant',
'The Governor',
'A Self-Proclaimed Expert',
'The Town Crier',
'A Very Loud Individual',
];
function randomItem(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function generateQuote() {
var count = Math.random() > 0.5 ? 2 : 1;
var selected = [];
var available = fragments.slice();
for (var i = 0; i < count && available.length > 0; i++) {
var idx = Math.floor(Math.random() * available.length);
selected.push(available.splice(idx, 1)[0]);
}
var text = selected.join(' ');
if (Math.random() > 0.3) {
var extra = randomItem(fragments);
if (selected.indexOf(extra) === -1) {
text += ' ' + extra;
}
}
var source = randomItem(sources);
return { text: text, source: source };
}
function createElement(data) {
var div = document.createElement('div');
div.className = 'testimonial';
var p = document.createElement('p');
p.textContent = '\u201C' + data.text + '\u201D';
div.appendChild(p);
var cite = document.createElement('cite');
cite.textContent = '\u2014 ' + data.source;
div.appendChild(cite);
return div;
}
var track = document.getElementById('testimonial-track');
if (!track) return;
var viewport = track.parentElement;
var paused = false;
var scrollPos = 0;
var speed = 0.35;
function step() {
try {
if (!paused) {
scrollPos += speed;
track.style.transform = 'translateY(' + (-scrollPos) + 'px)';
var first = track.firstElementChild;
var second = first && first.nextElementSibling;
if (first && second) {
var distance = second.offsetTop - first.offsetTop;
if (distance > 0 && scrollPos >= distance) {
scrollPos -= distance;
track.removeChild(first);
track.appendChild(createElement(generateQuote()));
}
}
}
} catch (_) {}
requestAnimationFrame(step);
}
viewport.addEventListener('mouseenter', function () { paused = true; });
viewport.addEventListener('mouseleave', function () { paused = false; });
step();
})();

View File

@@ -218,6 +218,30 @@ footer {
margin-top: 2rem;
}
.testimonial-viewport {
overflow: hidden;
max-height: 320px;
position: relative;
}
.testimonial-viewport::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 48px;
background: linear-gradient(transparent, #151520);
pointer-events: none;
z-index: 1;
}
#testimonial-track {
display: flex;
flex-direction: column;
gap: 1rem;
}
@media (max-width: 600px) {
h1 { font-size: 3rem; }
.hero h2 { font-size: 2rem; }